Вход в Python в интерактивных сессиях - PullRequest
3 голосов
/ 19 февраля 2011

Я пытаюсь реализовать ведение журнала в моих приложениях Python 2.7 и нашел его очень полезным. Однако я заметил, что при интерактивном запуске Python каждое сообщение журнала печатается несколько раз. Количество раз, когда сообщение печатается, совпадает с количеством раз, которое я ранее запускал сценарий, поэтому кажется, что регистратор не очищается должным образом в конце сценария (я бы предположил). Рассмотрим следующий пример:

import sys
import logging

def main(argv=None):

    log = logging.getLogger('test')
    log.setLevel(logging.DEBUG)

    console_handler = logging.StreamHandler()
    console_handler.setFormatter(logging.Formatter("%(message)s"))
    log.addHandler(console_handler)

    log.info('Starting something...')
    log.info('Doing something...')
    log.info('Finished something.')

    logging.shutdown()

if __name__=='__main__':
    sys.exit(main(sys.argv[1:]))

Typing

>>> import file.py
>>> file.main()

дает следующее:

Starting something...
Doing something...
Finished something.

Затем, набрав file.main() во второй раз, вы получите:

Starting something...
Starting something...
Doing something...
Doing something...
Finished something.
Finished something.

Повторение в третий раз даст три или каждое сообщение и так далее. Кто-нибудь знает, почему это происходит - это ожидаемое поведение модуля ведения журнала и, если да, как я могу это изменить? Приведенный выше сценарий печатает только одно сообщение, если выполняется как сценарий (python file.py), как и ожидалось.

Ответы [ 4 ]

5 голосов
/ 19 февраля 2011

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

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

Возможно, что-то вродеэто:

import atexit
import sys
import logging

log = logging.getLogger('test')
log.setLevel(logging.DEBUG)

console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter("%(message)s"))
log.addHandler(console_handler)

def shutdown_logging():
    logging.shutdown()    

atexit.register(shutdown_logging)

def main(argv=None):
    log.info('Starting something...')
    log.info('Doing something...')
    log.info('Finished something.')


if __name__=='__main__':
    sys.exit(main(sys.argv[1:]))
2 голосов
/ 19 февраля 2011

Попробуйте это в качестве обходного пути:

if len(logging.root.handlers) == 0:
     log.add_handler(console_handler)

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

1 голос
/ 06 февраля 2019

Вы можете удалять все обработчики каждый раз, когда перезагружаете свою конфигурацию, в вашем случае перед вызовом file.main():

file.logging.getLogger('test').handlers = []

Примечание (субъективно):

As @stderrнаписал, что рекомендуется определять ваши логгеры на уровне модулей.Тем не менее, я думаю, что это также хорошая практика - устанавливать их в точке входа приложения, поэтому здесь я бы добавил обработчики только после if __name__=='__main__', или, в вашем случае, в вашей (I) консоли Python.Таким образом, импорт модуля не создает все виды обработчиков, но они создаются только человеком, который решает выполнить некоторые функции вашего модуля.

0 голосов
/ 27 января 2014

Вот альтернатива решению @ beer_monk, которое будет работать, даже если вы коснулись корневого обработчика где-то еще. Я использовал __name__ вместо 'test' здесь, чтобы было проще повторно использовать код в других модулях, возможно, превратив его в функцию, которая принимает __name__ в качестве аргумента.

if not __name__ in logging.Logger.manager.loggerDict:
    new_logger = logging.getLogger(__name__)
    new_logger.setLevel(logging.DEBUG)

    console_handler = logging.StreamHandler()
    console_handler.setFormatter(logging.Formatter("%(message)s"))
    new_logger.addHandler(console_handler)
log = logging.getLogger(__name__)
...