Python日志记录模块:重复的控制台输出[IPython Notebook / Qtconsole]

时间:2015-07-14 10:10:00

标签: python logging

我正在尝试使用python logging模块,但在这里有点困惑。以下是首先创建logger,然后创建file handlerconsole handler logger并添加import logging logger = logging.getLogger('logging_test') logger.setLevel(logging.DEBUG) print(len(logger.handlers)) # output: 0 # create file handler which logs even debug messages fh = logging.FileHandler('/home/Jian/Downloads/spam.log', mode='w') fh.setLevel(logging.DEBUG) # create console handler with a higher log level ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # create formatter and add it to the handlers formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) fh.setFormatter(formatter) # add the handlers to logger logger.addHandler(ch) logger.addHandler(fh) print(len(logger.handlers)) # output: 2 # write some log messages logger.debug('debug message') logger.info('info message') logger.warn('warn message') logger.error('error message') logger.critical('critical message') 的标准脚本。

file handler

我在新启动的内核上运行它。 2015-07-14 10:59:26,942 - logging_test - DEBUG - debug message DEBUG:logging_test:debug message 2015-07-14 10:59:26,944 - logging_test - INFO - info message INFO:logging_test:info message 2015-07-14 10:59:26,944 - logging_test - WARNING - warn message WARNING:logging_test:warn message 2015-07-14 10:59:26,945 - logging_test - ERROR - error message ERROR:logging_test:error message 2015-07-14 10:59:26,946 - logging_test - CRITICAL - critical message CRITICAL:logging_test:critical message 按预期工作。但是在控制台输出中,我得到了某种重复的消息:

console handler

我认为那些带有时间戳的日志消息来自用户定义的MailItem.Printout,但重复的消息来自哪里?我可以摆脱它们,比如说,只保留其他所有线路吗?任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:6)

问题已经提出here

观察结果如下:在普通的python或IPython控制台中,在根记录器本身用于发出日志消息之前,没有安装根记录器的处理程序:

In [1]: import logging

In [2]: logging.getLogger().handlers
Out[2]: []

In [3]: logging.warn('Something happened!')
WARNING:root:Something happened!

In [4]: logging.getLogger().handlers
Out[4]: [<logging.StreamHandler at 0x42acef0>]

但是,在IPython笔记本中,立即安装了默认的stderr根记录器:

In [1]: import logging

In [2]: logging.getLogger().handlers
Out[2]: [<logging.StreamHandler at 0x35eedd8>]

也许我错过了什么,但我认为在笔记本中,不应该自动安装处理程序,原因有很多:

  1. 这将使标准python,IPython控制台和IPython笔记本之间的默认日志记录配置保持一致。
  2. 一旦用户使用根记录器写入日志消息,处理程序就会自动安装,因此不容易错过日志消息。
  3. 使用当前行为,配置子记录器的库和该子记录器的处理程序可能很容易使用应该只在日志文件(或其他地方)中的调试消息来对笔记本发送垃圾邮件。例如,astropy似乎有这样的问题,我和我自己的库遇到了同样的问题。问题是对于这样的库,没有“干净”的方法。该库可以在导入时删除根记录器的处理程序,这是hack-y。它可以将自己的记录器的propagate属性设置为False,这样日志消息就不会传播到根记录器,但这不仅会禁止调试输出进入笔记本,还会禁止更严重的消息。此外,它还可以防止用户实际捕获所有日志输出。
  4. 另一种方法可能是添加一个配置选项,为自动添加的流处理程序指定日志级别,以便可以忽略不太严重的消息自动显示在笔记本中。但这仍然会使IPython控制台和IPython笔记本之间的行为不同。

    我确定没有默认处理程序集的唯一缺点是,正在使用的某些库/笔记本可能依赖于此行为并主动解决它,例如,如果它们检测到它们正在运行,则禁用它们自己的处理程序在ipython笔记本中。这种情况可能会因这种变化而破裂。

    因此,将logger.propagate设置为False或使用reload(logging)可以防止重复输出,但视情况会产生副作用。

    请注意,reload在较新版本的python中不可用(3.4,可能更早)。从3.1开始,请参阅importlib.reload

答案 1 :(得分:2)

执行时

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

您正在创建另一个StreamHandler。 要解决您的问题,您应该从iPython中捕获StreamHandler:

import logging

handlers = logging.getLogger().handlers
for h in handlers:
    if isinstance(h, logging.StreamHandler):
        handler_console = h
        break

如果它不存在,您可以创建自己的:

if handler_console is None:
    handler_console = logging.StreamHandler()

最后根据需要格式化(设置其他属性):

if handler_console is not None:
    # first we need to remove to avoid duplication
    logging.getLogger().removeHandler(handler_console)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    handler_console.setFormatter(formatter)
    # then add it back
    logger.addHandler(handler_console)

答案 2 :(得分:1)

我的解决方案是:

import logging

logger = logging.getLogger()
logger.propagate = False