Skip to content

Commit 062c4bc

Browse files
Add logger
1 parent 18cbae0 commit 062c4bc

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""Structured logger utility for creating JSON logs in Delphi pipelines."""
2+
import logging
3+
import sys
4+
import threading
5+
import structlog
6+
7+
8+
def handle_exceptions(logger):
9+
"""Handle exceptions using the provided logger."""
10+
def exception_handler(etype, value, traceback):
11+
logger.exception("Top-level exception occurred",
12+
exc_info=(etype, value, traceback))
13+
14+
def multithread_exception_handler(args):
15+
exception_handler(args.exc_type, args.exc_value, args.exc_traceback)
16+
17+
sys.excepthook = exception_handler
18+
threading.excepthook = multithread_exception_handler
19+
20+
21+
def get_structured_logger(name=__name__,
22+
filename=None,
23+
log_exceptions=True):
24+
"""Create a new structlog logger.
25+
26+
Use the logger returned from this in indicator code using the standard
27+
wrapper calls, e.g.:
28+
29+
logger = get_structured_logger(__name__)
30+
logger.warning("Error", type="Signal too low").
31+
32+
The output will be rendered as JSON which can easily be consumed by logs
33+
processors.
34+
35+
See the structlog documentation for details.
36+
37+
Parameters
38+
---------
39+
name: Name to use for logger (included in log lines), __name__ from caller
40+
is a good choice.
41+
filename: An (optional) file to write log output.
42+
"""
43+
# Configure the underlying logging configuration
44+
handlers = [logging.StreamHandler()]
45+
if filename:
46+
handlers.append(logging.FileHandler(filename))
47+
48+
logging.basicConfig(
49+
format="%(message)s",
50+
level=logging.INFO,
51+
handlers=handlers
52+
)
53+
54+
# Configure structlog. This uses many of the standard suggestions from
55+
# the structlog documentation.
56+
structlog.configure(
57+
processors=[
58+
# Filter out log levels we are not tracking.
59+
structlog.stdlib.filter_by_level,
60+
# Include logger name in output.
61+
structlog.stdlib.add_logger_name,
62+
# Include log level in output.
63+
structlog.stdlib.add_log_level,
64+
# Allow formatting into arguments e.g., logger.info("Hello, %s",
65+
# name)
66+
structlog.stdlib.PositionalArgumentsFormatter(),
67+
# Add timestamps.
68+
structlog.processors.TimeStamper(fmt="iso"),
69+
# Match support for exception logging in the standard logger.
70+
structlog.processors.StackInfoRenderer(),
71+
structlog.processors.format_exc_info,
72+
# Decode unicode characters
73+
structlog.processors.UnicodeDecoder(),
74+
# Render as JSON
75+
structlog.processors.JSONRenderer()
76+
],
77+
# Use a dict class for keeping track of data.
78+
context_class=dict,
79+
# Use a standard logger for the actual log call.
80+
logger_factory=structlog.stdlib.LoggerFactory(),
81+
# Use a standard wrapper class for utilities like log.warning()
82+
wrapper_class=structlog.stdlib.BoundLogger,
83+
# Cache the logger
84+
cache_logger_on_first_use=True,
85+
)
86+
87+
logger = structlog.get_logger(name)
88+
89+
if log_exceptions:
90+
handle_exceptions(logger)
91+
92+
return logger

0 commit comments

Comments
 (0)