15

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?

0

4 Answers 4

16

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 
Sign up to request clarification or add additional context in comments.

5 Comments

I have used two handlers, for console and file. I dont want stack trace on console , I used above approach but stack trace is getting printed on both console and file. Below is the code """""""fileHandler = logging.FileHandler("{0}/{1}".format(logPath, fileName)) fileHandler.setFormatter(logFormatter) Logger.addHandler(fileHandler) consoleHandler = logging.StreamHandler() consoleHandler.setFormatter(logFormatter) consoleHandler.addFilter(SetTracebackCacheFilter('')) Logger.addHandler(consoleHandler)""""""""
@ragireddysricharanreddy: ick, I see what is going wrong; an empty string still is seen as an empty cache. I'll rework this answer.
I resolved it....we need to add record.exc_info = False in filter function
@ragireddysricharanreddy: I set it to None. Take into account that this will affect other handlers if they are run later on.
Nice solution but a word of caution (which is not the fault of @Martijn Pieters). If your handler is behind a QueueHandler you must override the QueueHandler.prepare() function so that the only thing it does is return record. Otherwise it is modifying the record so that this solution doesn't work.
3

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)

3 Comments

This is nice and straightforward, but it means the exception's type and arguments don't get output either, so I've built on it. stackoverflow.com/a/77581890/1450294
I cannot get this to work for two different formatters and handlers (the original, and the "no traceback" version).
This solution and @MichaelScheper's followup are intriguing, but they don't seem to work if you have more than one formatter. The reason is that the exception text is cached in format(), and the comment there shows the problem: "Cache the traceback text to avoid converting it multiple times (it's constant anyway)". Of course, it's not constant if you are logging to two locations with different exception formatters; the first one executed will win. It might be possible to override format() to copy the record, or to set record.exc_text to None before and after formatting it.
1

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.

2 Comments

How would you use this function in a real world scenario? I'm new to Python and researching exception handling, and this looks like it would work great. As an example, if I wanted to raise an error when a passed file doesn't exist - how would I use your function? I'd like to hide the stack trace and just output my error.
@fmotion1, if you want to omit the trace only for very specific exception you created yourself you need to check that record.exc_info is an instance of your specific exception, something like that: if not isintance(MySpecialException, record.exc_info) and not self.skip_traceback:
0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.