Логирование в Python

Введение в Python Logging

Этот модуль определяет функции и классы, которые реализуют гибкую систему регистрации событий для приложений и библиотек.

Основное преимущество API ведения журнала, предоставляемого стандартным библиотечным модулем, заключается в том, что все модули Python могут участвовать в ведении журнала, поэтому журнал вашего приложения может содержать ваши собственные сообщения, интегрированные с сообщениями от сторонних модулей.

Итак, начнем:

Пример конфигурации прямо в коде

 import logging

logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter(
        '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

logger.debug('this is a %s test', 'debug')

 

Пример вывода:

 2016-07-26 18:53:55,332 root         DEBUG    this is a debug test

 

Пример конфигурации через INI-файл

Предполагая, что файл называется logging_config.ini. Подробнее для формата файла находятся в конфигурации протоколирования раздела лесозаготовительного учебника .

 [loggers]
keys=root

[handlers]
keys=stream_handler

[formatters]
keys=formatter

[logger_root]
level=DEBUG
handlers=stream_handler

[handler_stream_handler]
class=StreamHandler
level=DEBUG
formatter=formatter
args=(sys.stderr,)

[formatter_formatter]
format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s


 

Затем используйте logging.config.fileConfig() в коде:

 import logging
from logging.config import fileConfig

fileConfig('logging_config.ini')
logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')

 

Пример конфигурации через словарь

Начиная с Python 2.7, вы можете использовать словарь с деталями конфигурации. PEP 391 содержит перечень обязательных и факультативных элементов в словаре конфигурации.

 import logging
from logging.config import dictConfig

logging_config = dict(
    version = 1,
    formatters = {
        'f': {'format':
              '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
        },
    handlers = {
        'h': {'class': 'logging.StreamHandler',
              'formatter': 'f',
              'level': logging.DEBUG}
        },
    root = {
        'handlers': ['h'],
        'level': logging.DEBUG,
        },
)

dictConfig(logging_config)

logger = logging.getLogger()
logger.debug('often makes a very good meal of %s', 'visiting tourists')


 

Регистрация исключений

Если вы хотите , чтобы войти исключения вы можете и должны использовать logging.exception(msg) метод:

 >>> import logging
>>> logging.basicConfig()
>>> try:
...     raise Exception('foo')
... except:
...     logging.exception('bar')
...
ERROR:root:bar
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
Exception: foo

 

Не передавайте исключение в качестве аргумента:

Как logging.exception(msg) ожидает msg ARG, это распространенная ошибка , чтобы передать исключение в лесозаготовительном вызов , как это:

 >>> try:
...     raise Exception('foo')
... except Exception as e:
...     logging.exception(e)
...
ERROR:root:foo
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
Exception: foo

 

Хотя на первый взгляд может показаться, что это правильно, на самом деле это проблематично из-за причины, по которой исключения и различные кодировки работают вместе в модуле регистрации:

 >>> try:
...     raise Exception(u'föö')
... except Exception as e:
...     logging.exception(e)
...
Traceback (most recent call last):
  File "/.../python2.7/logging/__init__.py", line 861, in emit
    msg = self.format(record)
  File "/.../python2.7/logging/__init__.py", line 734, in format
    return fmt.format(record)
  File "/.../python2.7/logging/__init__.py", line 469, in format
    s = self._fmt % record.__dict__
UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-2: ordinal not in range(128)
Logged from file <stdin>, line 4

 

Попытка войти исключение , которое содержит юникод символы, таким образом , будет с треском провалились.Он скроет StackTrace первоначального исключения путем переопределения его новое, которое возникает в процессе форматирования вашего logging.exception(e) вызова.

Очевидно, что в вашем собственном коде вы можете знать о кодировке в исключениях. Однако сторонние библиотеки могут справиться с этим по-другому.

Правильное использование:

Если вместо исключения вы просто передадите сообщение и дадите Python свою магию, он будет работать:

 >>> try:
...     raise Exception(u'föö')
... except Exception as e:
...     logging.exception('bar')
...
ERROR:root:bar
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
Exception: f\xf6\xf6

 

Как вы можете видеть , что мы на самом деле не использовать e в том случае, вызов logging.exception(...) волшебно форматирует самый последнее исключение.

Регистрация исключений с уровнями журнала, не являющимися ошибками

Если вы хотите , чтобы войти исключение с другим уровнем журнала , чем ошибка, вы можете использовать exc_info аргумент лесорубов по умолчанию:

 logging.debug('exception occurred', exc_info=1)
logging.info('exception occurred', exc_info=1)
logging.warning('exception occurred', exc_info=1)


 

Доступ к сообщению об исключении

Имейте в виду, что библиотеки могут выдавать исключения с сообщениями как любые из байтовых строк в кодировке Unicode или (utf-8, если вам повезет). Если вам действительно нужно , чтобы получить доступ текста в виде исключения, единственный надежный способ, который будет работать всегда, заключается в использовании repr(e) или %r форматирования строк:

 >>> try:
...     raise Exception(u'föö')
... except Exception as e:
...     logging.exception('received this exception: %r' % e)
...
ERROR:root:received this exception: Exception(u'f\xf6\xf6',)
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
Exception: f\xf6\xf6