#python #logging
#python #ведение журнала #python-ведение журнала
Вопрос:
У меня есть простой скрипт на Python, который использует встроенный logging
.
Я настраиваю ведение журнала внутри функции. Базовая структура была бы примерно такой:
#!/usr/bin/env python
import logging
import ...
def configure_logging():
logger = logging.getLogger("my logger")
logger.setLevel(logging.DEBUG)
# Format for our loglines
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# Setup console logging
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
logger.addHandler(ch)
# Setup file logging as well
fh = logging.FileHandler(LOG_FILENAME)
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(fh)
return logger
def count_parrots():
...
logger.debug??
if __name__ == '__main__':
logger = configure_logging()
logger.debug("I'm a log file")
parrots = count_parrots()
Я могу нормально вызвать logger изнутри __main__
. Однако, как мне вызвать logger изнутри функции count_parrots()? Какой самый питонический способ обработки настройки такого регистратора?
Ответ №1:
Вы можете либо использовать корневой регистратор (по умолчанию) и, следовательно, функции уровня модуля logging.debug
, … либо использовать свой регистратор в функции, используя его. Действительно, getLogger
функция является заводской функцией с реестром (типа singleton), то есть она всегда возвращает один и тот же экземпляр для данного имени регистратора. Таким образом, вы можете получить свой регистратор в count_parrots, просто используя
logger = logging.getLogger("my logger")
в начале. Однако соглашение заключается в использовании иерархического имени с точками для вашего регистратора. Смотрите http://docs.python.org/library/logging.html#logging.getLogger
Редактировать:
Вы можете использовать декоратор, чтобы добавить поведение ведения журнала к вашим отдельным функциям, например:
def debug(loggername):
logger = logging.getLogger(loggername)
def log_(enter_message, exit_message=None):
def wrapper(f):
def wrapped(*args, **kargs):
logger.debug(enter_message)
r = f(*args, **kargs)
if exit_message:
logger.debug(exit_message)
return r
return wrapped
return wrapper
return log_
my_debug = debug('my.logger')
@my_debug('enter foo', 'exit foo')
def foo(a, b):
return a b
вы можете «жестко закодировать» имя регистратора и удалить закрытие верхнего уровня и my_debug.
Комментарии:
1. Хорошо, итак, я могу просто вызвать logging.getLogger в начале каждой функции, которая должна регистрироваться. Кажется немного расточительным и повторяющимся, не так ли? Достаточно справедливо. Или мне было бы лучше перейти на объектно-ориентированный подход и попытаться встроить все это в класс? (Это очень общий вопрос, который я знаю, я просто ищу, что сделано в мире Python).
2. Вы можете поместить свою функцию в класс с регистратором в качестве переменной экземпляра или (что я предпочитаю) создать декоратор, чтобы добавить функциональность ведения журнала к вашим отдельным функциям
3. Этот ответ показывает практически все, что не так с модулем Python
logging
…4. Мне любопытно, какие альтернативные варианты, по мнению людей, желательны для такого рода сквозного модуля? Я не могу понять, в чем проблема с logging.getLogger с точки зрения общего дизайна. Кажется довольно шаблонным материалом.
Ответ №2:
Вы можете просто сделать :
logger = logging.getLogger("my logger")
в вашем count_parrots()
методе. Когда вы передаете имя, которое использовалось ранее (т. Е. «мой регистратор»), модуль ведения журнала вернет тот же экземпляр, который был создан, соответствующий этому имени.
Обновление: Из руководства по ведению журнала (подчеркнуто мной)
getLogger() возвращает ссылку на экземпляр регистратора с указанным именем, если оно предоставлено, или root, если нет. Имена представляют собой иерархические структуры, разделенные точками. Несколько вызовов getLogger() с одинаковым именем вернут ссылку на один и тот же объект logger.
Ответ №3:
Типичный способ обработки ведения журнала — хранить регистратор для каждого модуля в глобальной переменной. Любые функции и методы в этом модуле затем просто ссылаются на тот же экземпляр регистратора.
Это кратко обсуждается во введении к руководству по предварительному ведению журнала в документации: http://docs.python.org/howto/logging.html#advanced-logging-tutorial
Вы можете передавать экземпляры регистратора в качестве параметров, но обычно это делается редко.
Комментарии:
1. Я думал, что стандартной практикой является использование logger=logging.getLogger(«logger.name «)
2. На уровне модуля, конечно. Однако использование отдельных регистраторов для разных функций и методов в одном модуле, как правило, излишне. Одним из исключений является то, что использование отдельных регистраторов может быть очень простым способом записать, какие потоки регистрируют определенные события.
3. Ах. Я думал, вы имели в виду фактическое использование
global
ключевого слова.4. @MatthewCornell Ты имеешь в виду
logger = logging.getLogger(__name__)
.
Ответ №4:
Меня смутило, как глобальные переменные работают в Python. Внутри функции вам нужно только объявить, global logger
если вы делали что-то подобное logger = logging.getLogger("my logger")
и надеялись изменить глобальную logger
.
Итак, чтобы изменить ваш пример, вы можете создать глобальный объект logger в начале файла. Если ваш модуль может быть импортирован другим, вам следует добавить NullHandler
, чтобы, если импортер библиотеки не хочет, чтобы ведение журнала было включено, у него не возникло никаких проблем с вашей библиотекой (ref).
#!/usr/bin/env python
import logging
import ...
logger = logging.getLogger("my logger").addHandler(logging.NullHandler())
def configure_logging():
logger.setLevel(logging.DEBUG)
# Format for our loglines
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# Setup console logging
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
logger.addHandler(ch)
# Setup file logging as well
fh = logging.FileHandler(LOG_FILENAME)
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(fh)
def count_parrots():
...
logger.debug('counting parrots')
...
return parrots
if __name__ == '__main__':
configure_logging()
logger.debug("I'm a log file")
parrots = count_parrots()
Ответ №5:
Если вам не нужны сообщения журнала на вашей консоли, вы можете использовать минималистичный способ.
В качестве альтернативы вы можете использовать tail -f myapp.log
для просмотра сообщений на консоли.
import logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='myapp.log',
level=logging.INFO)
def do_something():
logging.info('Doing something')
def main():
logging.info('Started')
do_something()
logging.info('Finished')
if __name__ == '__main__':
main()
Ответ №6:
Вы можете указать logger
в качестве аргумента count_parrots()
Или, что бы я сделал, создать класс parrots и использовать logger в качестве одного из его методов.