fix: queue=True no longer strips exc_info before JsonLinesFormatter sees it
QueueHandler.prepare() formatted the record with its own default formatter and nulled exc_info/exc_text/stack_info before the listener's real formatter ran, so queue=True + output="json" produced a JSON line with no exc_info key and the traceback folded into message instead. _PreservingQueueHandler skips that premature format-and-strip (safe here - the queue is in-process only, nothing needs to stay picklable) so the listener's handler formats the untouched record exactly once, matching the non-queued path. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
+17
-1
@@ -29,6 +29,22 @@ _listener = None
|
||||
_atexit_registered = False
|
||||
|
||||
|
||||
class _PreservingQueueHandler(logging.handlers.QueueHandler):
|
||||
"""QueueHandler that hands the record to the listener untouched
|
||||
|
||||
stdlib's QueueHandler.prepare() calls self.format(record) with the queue
|
||||
handler's OWN (default) formatter, then nulls exc_info/exc_text/stack_info -
|
||||
losing structured fields (e.g. JsonLinesFormatter's exc_info key) before the
|
||||
listener's real formatter ever sees the record. that behavior exists to keep a
|
||||
record picklable across a multiprocessing.Queue; this lib only ever uses an
|
||||
in-process queue.Queue, so there is nothing to pickle and nothing to strip -
|
||||
the listener's handler formats the untouched record exactly once.
|
||||
"""
|
||||
|
||||
def prepare(self, record: logging.LogRecord) -> logging.LogRecord:
|
||||
return record
|
||||
|
||||
|
||||
def _exc_text() -> str:
|
||||
"""render sys.exc_info() as text, for capturing a traceback into a buffered warning"""
|
||||
return "".join(traceback.format_exception(*sys.exc_info())).strip()
|
||||
@@ -284,7 +300,7 @@ def setup_logging(
|
||||
|
||||
if queue:
|
||||
record_queue: "_queue.Queue" = _make_queue()
|
||||
qh = _tag(logging.handlers.QueueHandler(record_queue))
|
||||
qh = _tag(_PreservingQueueHandler(record_queue))
|
||||
root.addHandler(qh)
|
||||
_listener = logging.handlers.QueueListener(record_queue, *handlers, respect_handler_level=True)
|
||||
_listener.start()
|
||||
|
||||
Reference in New Issue
Block a user