Странно: logger использует только форматер первого обработчика для исключений
Я наблюдаю, как модуль ведения журнала ведет себя смешно. Я что-то упускаю ?
Я делаю обычную вещь с двумя обработчиками: StreamHandler для регистрации только информации и выше на консоль и FileHandler, который также будет обрабатывать всю отладочную информацию.
он работал нормально, пока я не решил иметь другой формат для exeptions. Мне нужен полный stacktrace в файле, но только тип исключения и значение на консоли. С обработчики имеют функцию setFormatter, и так как кажется легко написать подкласс ведения журнала.Форматер, я думал, это сработает.
обработчик консоли и обработчик файлов имеют свой собственный форматер. Инструкции печати в коде доказывают это. Тем не менее, вызов логгера.исключение будет использовать только formatException первого добавленного обработчика => исключение регистрируется в файле с форматом, который он должен иметь в консоли. Измените порядок работы регистратора.линии addHandler и тогда это formatException обработчика файлов, который используется везде.
import logging
import sys
class ConsoleFormatter(logging.Formatter):
def formatException(self, exc_info):
# Ugly but obvious way to see who's talking.
return "CONSOLE EXCEPTION %s: %s" % exc_info[:2]
def setup(path):
logger = logging.getLogger()
#
file_handler = logging.FileHandler(path, mode='w')
if __debug__:
file_handler.setLevel(logging.DEBUG)
else:
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s %(levelname)-8s "
"%(name)-16s %(message)s "
"[%(filename)s@%(lineno)d in %(funcName)s]")
file_handler.setFormatter(formatter)
#
console_handler = logging.StreamHandler(sys.stderr)
console_handler.setLevel(logging.INFO)
console_formatter = ConsoleFormatter("%(levelname)-8s - %(message)s")
console_handler.setFormatter(console_formatter)
# >>> FUN HAPPENS HERE <<<
# Only the formatter of the first handler is used ! Both on the console
# and in the file. Change the order of these two lines to see.
logger.addHandler(console_handler)
logger.addHandler(file_handler)
#
# Proof that the two handlers have different formatters:
print logger.handlers
print file_handler.formatter.formatException
print console_formatter.formatException
#
logger.setLevel(logging.DEBUG)
logger.info("Logger ready.")
setup('test.log')
logger = logging.getLogger()
logger.debug("Only visible in the file.")
try:
1/0
except ZeroDivisionError:
logger.exception("boom")
что происходит ?
EDIT: кстати, я использую python 2.6. EDIT: исправлена опечатка в коде о имени переменной "console_formatter".
3 ответов
я нашел вашу проблему! Если вы посмотрите в logger/__init__.py
в исходном коде Formatter.format
в строке 440 (для py2.6) вы увидите следующее:
if record.exc_info:
# Cache the traceback text to avoid converting it multiple times
# (it's constant anyway)
if not record.exc_text:
record.exc_text = self.formatException(record.exc_info)
Это не так в вашем случае, так как вы переопределяете formatException
. Если вы прокомментируете if not record.exc_text
(и исправить отступ соответственно), похоже, работает так, как ожидалось.
ошибка, кажется, уже сообщается здесь: http://bugs.python.org/issue6435
Это код, который я придумал. Он выполняет свою работу :).
class CachelessFormatter(logging.Formatter):
# I came up with that after reading the answers to
# http://stackoverflow.com/questions/5875225/
# which pointed me to
# http://bugs.python.org/issue6435
# I still think Vinay Sajip has a bit of an attitude :p.
def format(self, record):
# Disable the caching of the exception text.
backup = record.exc_text
record.exc_text = None
s = logging.Formatter.format(self, record)
record.exc_test = backup
return s
class ConsoleFormatter(CachelessFormatter):
def formatException(self, exc_info):
return " %s: %s" % exc_info[:2]
def setup(path):
file_handler = logging.FileHandler(path, mode='w')
file_handler.setLevel(logging.DEBUG)
formatter = CachelessFormatter("%(asctime)s %(levelname)-8s "
"%(name)-16s %(message)s "
"[%(filename)s@%(lineno)d in %(funcName)s]")
file_handler.setFormatter(formatter)
console_handler = logging.StreamHandler(sys.stderr)
console_handler.setLevel(logging.INFO)
formatter = ConsoleFormatter("%(levelname)-8s - %(message)s")
console_handler.setFormatter(formatter)
logger = logging.getLogger()
logger.addHandler(file_handler)
logger.addHandler(console_handler)
if __debug__:
logger.setLevel(logging.DEBUG)
else:
logger.setLevel(logging.INFO)
logger.info("Logger ready.")
if __name__ == '__main__':
setup('test.log')
logger = logging.getLogger()
logger.debug("Only shows in the file")
try:
1 / 0
except ZeroDivisionError:
pass
logger.exception("boom")
в первый раз, когда я запустил ваш код, я получил traceback:
Traceback (most recent call last):
File "logger.py", line 42, in <module>
setup('test.log')
File "logger.py", line 37, in setup
print console_formatter.formatException
NameError: global name 'console_formatter' is not defined
Это, вероятно, источник проблемы. Форматы были правильными, когда я изменил console_handler
код:
console_handler = logging.StreamHandler(sys.stderr)
console_handler.setLevel(logging.INFO)
console_formatter = ConsoleFormatter("%(levelname)-8s - %(message)s")
console_handler.setFormatter(console_formatter)