14

I would like to log all warnings. I thought that setting captureWarnings to True should do the trick, but it doesn't. Code:

import logging
import warnings

from logging.handlers import RotatingFileHandler

logger_file_handler = RotatingFileHandler(u'./test.log')
logger_file_handler.setLevel(logging.DEBUG)

logging.captureWarnings(True)

logger = logging.getLogger(__name__)

logger.addHandler(logger_file_handler)
logger.setLevel(logging.DEBUG)

logger.info(u'Test')
warnings.warn(u'Warning test')

My expectation is that 'Warning test' should appear in test.log, but it doesn't; only 'Test' is put in the log file.

How to capture all warnings and redirect them to the log file?

vvvvv
  • 25,404
  • 19
  • 49
  • 81
LAdas
  • 439
  • 1
  • 5
  • 13

4 Answers4

16

From the logging.captureWarnings documentation:

Warnings issued by the warnings module will be redirected to the logging system. Specifically, a warning will be formatted using warnings.formatwarning() and the resulting string logged to a logger named 'py.warnings' with a severity of WARNING.

You probably want something like this:

import logging
import warnings

from logging.handlers import RotatingFileHandler

logger_file_handler = RotatingFileHandler(u'test.log')
logger_file_handler.setLevel(logging.DEBUG)

logging.captureWarnings(True)

logger = logging.getLogger(__name__)
warnings_logger = logging.getLogger("py.warnings")

logger.addHandler(logger_file_handler)
logger.setLevel(logging.DEBUG)
warnings_logger.addHandler(logger_file_handler)

logger.info(u'Test')
warnings.warn(u'Warning test')

Hope it helps!

cdonts
  • 9,304
  • 4
  • 46
  • 72
5

In an application codebase, instead of explicitly configuring the py.warnings logger as other answers here suggest, you instead can - and probably want to - configure the root logger. This will:

  • Log stuff from py.warnings (the logger that the captureWarnings integration logs with)
  • Log stuff from libraries that create their own logger and log to it
  • Allow you to directly log using the logging module functions like logging.info instead of making a logger object

The following simple demo logs your warning as you want it to:

import logging
import warnings

from logging.handlers import RotatingFileHandler

logger_file_handler = RotatingFileHandler(u'./test.log')
logger_file_handler.setLevel(logging.DEBUG)

logging.captureWarnings(True)

root_logger = logging.getLogger()

root_logger.addHandler(logger_file_handler)
root_logger.setLevel(logging.DEBUG)

logging.info(u'Test')
warnings.warn(u'Warning test')
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
4

logging.captureWarnings is not using your logger. It uses a logger named 'py.warnings'. You will need to configure that logger to do what you want.

user2357112
  • 260,549
  • 28
  • 431
  • 505
0

Not directly relevant for the OP, but may be useful for others winding up here based on the title:

Another reason for warnings not to show up in your logs would be if you use warnings.catch_warnings with record=True.

Here's a minimal example:

import logging
import warnings

logging.basicConfig()  # configures the root logger

logging.captureWarnings(True)

warnings.warn('logged')

with warnings.catch_warnings():
    warnings.warn('also logged')


with warnings.catch_warnings(record=True) as warnings_caught:
    warnings.warn('not logged')

# show warnings recorded
print([w.message for w in warnings_caught])
djvg
  • 11,722
  • 5
  • 72
  • 103