简体   繁体   中英

Should logger.exception only be called inside an except block? Why?

The docs for logger.exception mention:

This method should only be called from an exception handler.

So usage should look something like this:

try:
    errorerrorerror
except NameError as e:
    logger.exception('debug message %s', e)

But when I tried doing it the "wrong" way, behaviour seemed to be just the same:

try:
    errorerrorerror
except NameError as e:
    pass

logger.exception('debug message %s', e)

What is the reason for that caveat mentioned in the docs? Is it actually true that we can only use it in an except block, for some subtle reason not evident here?

The method is designed for use in an exception handler. As such the documentation tells you this, by using the word should , not must .

In the text of standards, should and must are rigidly defined; one means we advice you to do it this way, it'd be much better if you did , the other means it's an outright error if you don't do this . See RFC 2119 for the IETF taskforce wording.

All logging.exception() does is set the exc_info keyword argument before calling logging.error() . The exc_info argument is then later fleshed out to include the most recently handled exception , taken from sys.exc_info() . It is then up to the formatter to include the exception message (via the Formatter.format_exception() method ) to format the exception.

Because sys.exc_info() works both in the except suite and out, both variants work. From a code documentation point of view , it is just clearer if you use it in the except handler.

You don't need to include the error message, really, because your log formatter should already do that for you:

logger.exception('debug message 2')  # exception should be included automatically

You can explicitly attach an exception to any log message with:

logger.error('debug message 2', exc_info=sys.exc_info())

or any other 3-tuple value with the exception type, the exception value and a traceback. Alternatively, set exc_info=1 to have the logger retrieve the information from sys.exc_info() itself.

See the documentation for Logger.debug() :

exc_info which, if it does not evaluate as false, causes exception information to be added to the logging message. If an exception tuple (in the format returned by sys.exc_info() ) is provided, it is used; otherwise, sys.exc_info() is called to get the exception information.

The behaviour is not really the same. The function logs the last raised exception, but only within an except block you can be sure this exception is actually the one happened in the try block belonging to the except block. Your example has an exception that always happens. In that, quite useless, corner case it really doesn't matter if the log.exception() call is done in the except block or after it.


Two examples to illustrate the difference:

import logging
import random

try:
    if random.randint(0, 1) == 1:
        1 / 0
except:
    logging.exception()

versus:

import logging
import random

try:
    if random.randint(0, 1) == 1:
        1 / 0
except:
    pass

logging.exception()

In the fist code there is a 50% chance a ZeroDivisionError happens, which is the logged. In the second code there is the same chance of the exception happening and it is logged if it happens. Now think what happens in the other 50% of the runs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM