繁体   English   中英

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

[英]Python logging module: duplicated console output [IPython Notebook/Qtconsole]

我正在尝试使用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 ,但重复的消息来自哪里? 我可以摆脱它们,比如说,只保留其他所有线路吗? 任何帮助表示赞赏。

这里提出这个问题。

观察结果如下:在普通的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 ,这样日志消息就不会传播到根记录器,但这不仅会禁止调试输出进入笔记本,还会禁用更严重的消息。 此外,它还可以防止用户在需要时实际捕获所有日志输出。

另一种方法是添加一个配置选项,为自动添加的流处理程序指定日志级别,以便可以忽略不太严重的消息自动显示在笔记本中。 但这仍然会使IPython控制台和IPython笔记本之间的行为不同。

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

因此将logger.propagate设置为False或使用reload(logging)将阻止重复输出,但取决于是否会产生副作用。

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

当你表演

# 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)

我的解决方案是:

import logging

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM