Избегать передачи ссылки логгера между функциями? - PullRequest
47 голосов
/ 12 мая 2011

У меня есть простой скрипт 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()

Я могу нормально вызывать логгер изнутри __main__. Тем не менее, как я могу вызвать регистратор из функции count_parrots ()? Какой самый питоновский способ обработки настройки регистратора, подобного этому?

Ответы [ 6 ]

40 голосов
/ 12 мая 2011

Вы можете либо использовать корневой (по умолчанию) регистратор, и, таким образом, функции уровня модуля logging.debug, ..., либо использовать свой регистратор в функции, используя его. Действительно, функция getLogger является фабричной функцией с реестром (синглтоноподобным), то есть она всегда возвращает один и тот же экземпляр для данного имени регистратора. Таким образом, вы можете получить свой регистратор в count_parrots, просто используя

logger = logging.getLogger("my logger") 

в начале. Тем не менее, соглашение заключается в использовании точечного иерархического имени для вашего регистратора. Смотри http://docs.python.org/library/logging.html#logging.getLogger

EDIT:

Вы можете использовать декоратор для добавления поведения ведения журнала к вашим отдельным функциям, например:

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.

15 голосов
/ 12 мая 2011

Вы можете просто сделать:

logger = logging.getLogger("my logger") 

в вашем count_parrots() методе. Когда вы передаете имя, которое использовалось ранее (то есть «мой регистратор»), модуль журналирования возвращает тот же созданный экземпляр, соответствующий этому имени.

Обновление: из учебника по (выделение мое)

getLogger () возвращает ссылку на Экземпляр регистратора с указанным имя, если оно предоставлено, или root, если не. Имена разделены точкой иерархические структуры. Multiple вызывает getLogger () с тем же имя будет возвращать ссылку на тот же объект регистратора.

9 голосов
/ 12 мая 2011

Типичный способ обработки журналирования состоит в том, чтобы сохранять регистратор для каждого модуля в глобальной переменной.Любые функции и методы в этом модуле затем просто ссылаются на тот же экземпляр регистратора.

Это кратко обсуждается во вступлении к руководству по предварительной записи в документации: http://docs.python.org/howto/logging.html#advanced-logging-tutorial

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

0 голосов
/ 19 марта 2018

Если вам не нужны сообщения журнала на консоли, вы можете использовать их в минималистском стиле.

В качестве альтернативы вы можете использовать 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()
0 голосов
/ 30 августа 2017

Меня смутило, как глобальные переменные работают в Python.Внутри функции вам нужно только объявить global logger, если вы делаете что-то вроде logger = logging.getLogger("my 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()
0 голосов
/ 12 мая 2011

Вы можете указать logger в качестве аргумента для count_parrots() Или, что я бы сделал, создать попугаев класса и использовать logger в качестве одного из методов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...