@@ -23,8 +23,7 @@ def handle_exceptions(logger):
23
23
"""Handle exceptions using the provided logger."""
24
24
25
25
def exception_handler (scope , etype , value , traceback ):
26
- logger .exception ("Top-level exception occurred" ,
27
- scope = scope , exc_info = (etype , value , traceback ))
26
+ logger .exception ("Top-level exception occurred" , scope = scope , exc_info = (etype , value , traceback ))
28
27
29
28
def sys_exception_handler (etype , value , traceback ):
30
29
exception_handler ("sys" , etype , value , traceback )
@@ -33,21 +32,19 @@ def threading_exception_handler(args):
33
32
if args .exc_type == SystemExit and args .exc_value .code == 0 :
34
33
# `sys.exit(0)` is considered "successful termination":
35
34
# https://docs.python.org/3/library/sys.html#sys.exit
36
- logger .debug ("normal thread exit" , thread = args .thread ,
37
- stack = "" .join (
38
- format_exception (
39
- args .exc_type , args .exc_value , args .exc_traceback )))
35
+ logger .debug (
36
+ "normal thread exit" ,
37
+ thread = args .thread ,
38
+ stack = "" .join (format_exception (args .exc_type , args .exc_value , args .exc_traceback )),
39
+ )
40
40
else :
41
- exception_handler (f"thread: { args .thread } " ,
42
- args .exc_type , args .exc_value , args .exc_traceback )
41
+ exception_handler (f"thread: { args .thread } " , args .exc_type , args .exc_value , args .exc_traceback )
43
42
44
43
sys .excepthook = sys_exception_handler
45
44
threading .excepthook = threading_exception_handler
46
45
47
46
48
- def get_structured_logger (name = __name__ ,
49
- filename = None ,
50
- log_exceptions = True ):
47
+ def get_structured_logger (name = __name__ , filename = None , log_exceptions = True , is_real = True ):
51
48
"""Create a new structlog logger.
52
49
53
50
Use the logger returned from this in indicator code using the standard
@@ -66,17 +63,17 @@ def get_structured_logger(name=__name__,
66
63
name: Name to use for logger (included in log lines), __name__ from caller
67
64
is a good choice.
68
65
filename: An (optional) file to write log output.
66
+ log_exceptions: should we log exceptions?
67
+ is_real: is this logger *not* in a testing environment? Used to avoid
68
+ setting features that interfere with testing the logging output
69
69
"""
70
70
# Set the underlying logging configuration
71
71
if "LOG_DEBUG" in os .environ :
72
72
log_level = logging .DEBUG
73
73
else :
74
74
log_level = logging .INFO
75
75
76
- logging .basicConfig (
77
- format = "%(message)s" ,
78
- level = log_level ,
79
- handlers = [logging .StreamHandler ()])
76
+ logging .basicConfig (format = "%(message)s" , level = log_level , handlers = [logging .StreamHandler ()])
80
77
81
78
def add_pid (_logger , _method_name , event_dict ):
82
79
"""Add current PID to the event dict."""
@@ -115,7 +112,7 @@ def add_pid(_logger, _method_name, event_dict):
115
112
# Use a standard wrapper class for utilities like log.warning()
116
113
wrapper_class = structlog .stdlib .BoundLogger ,
117
114
# Cache the logger
118
- cache_logger_on_first_use = True ,
115
+ cache_logger_on_first_use = is_real ,
119
116
)
120
117
121
118
# Create the underlying python logger and wrap it with structlog
@@ -131,7 +128,7 @@ def add_pid(_logger, _method_name, event_dict):
131
128
return logger
132
129
133
130
134
- class LoggerThread () :
131
+ class LoggerThread :
135
132
"""
136
133
A construct to use a logger from multiprocessing workers/jobs.
137
134
@@ -149,15 +146,15 @@ class LoggerThread():
149
146
docs.python.org/3/howto/logging-cookbook.html#logging-to-a-single-file-from-multiple-processes
150
147
"""
151
148
152
- class SubLogger () :
149
+ class SubLogger :
153
150
"""MP-safe logger-like interface to convey log messages to a listening LoggerThread."""
154
151
155
152
def __init__ (self , queue ):
156
153
"""Create SubLogger with a bound queue."""
157
154
self .queue = queue
158
155
159
156
def _log (self , level , * args , ** kwargs ):
160
- kwargs_plus = {' sub_pid' : multiprocessing .current_process ().pid }
157
+ kwargs_plus = {" sub_pid" : multiprocessing .current_process ().pid }
161
158
kwargs_plus .update (kwargs )
162
159
self .queue .put ([level , args , kwargs_plus ])
163
160
@@ -181,7 +178,6 @@ def critical(self, *args, **kwargs):
181
178
"""Log a CRITICAL level message."""
182
179
self ._log (logging .CRITICAL , * args , ** kwargs )
183
180
184
-
185
181
def get_sublogger (self ):
186
182
"""Retrieve SubLogger for this LoggerThread."""
187
183
return self .sublogger
@@ -195,25 +191,22 @@ def __init__(self, logger, q=None):
195
191
self .msg_queue = multiprocessing .Queue ()
196
192
197
193
def logger_thread_worker ():
198
- logger .info (' thread started' )
194
+ logger .info (" thread started" )
199
195
while True :
200
196
msg = self .msg_queue .get ()
201
- if msg == ' STOP' :
202
- logger .debug (' received stop signal' )
197
+ if msg == " STOP" :
198
+ logger .debug (" received stop signal" )
203
199
break
204
200
level , args , kwargs = msg
205
- if level in [logging .DEBUG , logging .INFO , logging .WARNING ,
206
- logging .ERROR , logging .CRITICAL ]:
201
+ if level in [logging .DEBUG , logging .INFO , logging .WARNING , logging .ERROR , logging .CRITICAL ]:
207
202
logger .log (level , * args , ** kwargs )
208
203
else :
209
- logger .error ('received unknown logging level! exiting...' ,
210
- level = level , args_kwargs = (args , kwargs ))
204
+ logger .error ("received unknown logging level! exiting..." , level = level , args_kwargs = (args , kwargs ))
211
205
break
212
- logger .debug (' stopping thread' )
206
+ logger .debug (" stopping thread" )
213
207
214
- self .thread = threading .Thread (target = logger_thread_worker ,
215
- name = "LoggerThread__" + logger .name )
216
- logger .debug ('starting thread' )
208
+ self .thread = threading .Thread (target = logger_thread_worker , name = "LoggerThread__" + logger .name )
209
+ logger .debug ("starting thread" )
217
210
self .thread .start ()
218
211
219
212
self .sublogger = LoggerThread .SubLogger (self .msg_queue )
@@ -222,13 +215,13 @@ def logger_thread_worker():
222
215
def stop (self ):
223
216
"""Terminate this LoggerThread."""
224
217
if not self .running :
225
- self .logger .warning (' thread already stopped' )
218
+ self .logger .warning (" thread already stopped" )
226
219
return
227
- self .logger .debug (' sending stop signal' )
228
- self .msg_queue .put (' STOP' )
220
+ self .logger .debug (" sending stop signal" )
221
+ self .msg_queue .put (" STOP" )
229
222
self .thread .join ()
230
223
self .running = False
231
- self .logger .info (' thread stopped' )
224
+ self .logger .info (" thread stopped" )
232
225
233
226
234
227
@contextlib .contextmanager
0 commit comments