Reputation: 181
Is there a simple way to disable the logging of an exception stack trace in Python 3, either in a Handler
or Formatter
?
I need the stack trace in another Handler
, so setting exc_info=False
, in the call to the Logger
is not an option. Is there a simpler way than just defining my own Formatter
?
Upvotes: 15
Views: 8250
Reputation: 7048
Johothon's answer is nice and straightforward, but it excludes all exception information, not just the stack. An exception's type and arguments can be useful for devops and end users, so I've modified their code to include it:
class NoTracebackFormatter(logging.Formatter):
def formatException(self, exc_info):
return '%s: %s' % (exc_info[0].__name__, exc_info[1])
def formatStack(self, stack_info):
return ""
Then it's just handler.setFormatter(NoTracebackFormatter())
.
This works nicely for me in Python 3.11.
Upvotes: 0
Reputation: 137398
It can be done even easier than Digoya's answer:
class NoTracebackFormatter(logging.Formatter):
def formatException(self, ei):
return ""
def formatStack(self, stack_info):
return ""
(Tested on Python 3.9)
Upvotes: 3
Reputation: 131
Late response but it might me useful for somebody else. You don't need to bother with Handler
nor Filter
. You just need to inherit your own Formatter
and skip the part where you format exception. Here is a snippet I use:
class EnhancedFormatter(logging.Formatter):
def __init__(self, fmt=None, datefmt=None, style='%', validate=True, skip_traceback=False):
self.skip_traceback = skip_traceback
super(EnhancedFormatter, self).__init__(fmt, datefmt, style, validate)
def format(self, record) -> str:
record.message = record.getMessage()
if self.usesTime():
record.asctime = self.formatTime(record, self.datefmt)
s = self.formatMessage(record)
if not self.skip_traceback: # check here do you need to format traceback
if record.exc_info:
if not record.exc_text:
record.exc_text = self.formatException(record.exc_info)
if record.exc_text:
if s[-1:] != "\n":
s = s + "\n"
s = s + record.exc_text
if record.stack_info:
if s[-1:] != "\n":
s = s + "\n"
s = s + self.formatStack(record.stack_info)
return s
Just set skip_traceback
argument while instantiating the formatter class and later use it to determine do you need to format traceback or not.
Upvotes: 1
Reputation: 1121834
The easiest option to disable per handler traceback output is to add a custom logging.Filter
subclass that alters the record object (rather than filter out records).
The filter simply has to set exc_info
on records to None
:
class TracebackInfoFilter(logging.Filter):
"""Clear or restore the exception on log records"""
def __init__(self, clear=True):
self.clear = clear
def filter(self, record):
if self.clear:
record._exc_info_hidden, record.exc_info = record.exc_info, None
# clear the exception traceback text cache, if created.
record.exc_text = None
elif hasattr(record, "_exc_info_hidden"):
record.exc_info = record._exc_info_hidden
del record._exc_info_hidden
return True
and add that filter on your handler:
# do not display tracebacks in messages handled with this handler,
# by setting the traceback cache to a non-empty string:
handler_with_no_tracebacks.addFilter(TracebackInfoFilter())
However, handlers do not copy log records, and any other handler that is passed the same log record later on will also ignore formatting the traceback. So you also need to configure any other handlers to restore the information again:
for handler in logger.handlers:
if not any(isinstance(f, TracebackInfoFilter) for f in handler.filters):
handler.addFilter(TracebackInfoFilter(clear=False))
If anyone wanted to disable all traceback outputs, everywhere, then perhaps adding a custom filter to all handlers or loggers becomes tedious. In that case another option is to register a custom record factory with the logging.setLogRecordFactory()
function; just set the exc_info
attribute on records to None
, unconditionally:
record_factory = logging.getLogRecordFactory()
def clear_exc_text(*args, **kwargs):
record = record_factory(*args, **kwargs)
record.exc_info = None
return record
logging.setLogRecordFactory(clear_exc_text)
Note that the default factory is just the logging.LogRecord
class, but the above function does its best to work with any already-set custom factory.
Of course, you can also create your own Handler
subclass where the Handler.handle()
sets and clears the exc_info
attribute:
class NoTracebackHandler(logging.Handler):
def handle(self, record):
info, cache = record.exc_info, record.exc_text
record.exc_info, record.exc_text = None, None
try:
super().handle(record)
finally:
record.exc_info = info
record.exc_text = cache
Upvotes: 16