我对Python的日志记录模块的输出感到惊讶。我写了Python.org的How-To for Logging。当我运行示例代码时,会有很多(令人困惑的)重复。
StreamHandler复制日志消息,每次我使用魔法%run
In [4]: %run main.py
2018-05-11 2127:33 - WARNING - 3. This is a warning, yous!
2018-05-11 2127:33 - ERROR - 4. Here is an error
2018-05-11 2127:33 - CRITICAL - 5. This is f-ing critical!
[...]
In [7]: %run main.py
2018-05-11 2127:38 - WARNING - 3. This is a warning, yous!
2018-05-11 2127:38 - WARNING - 3. This is a warning, yous!
2018-05-11 2127:38 - WARNING - 3. This is a warning, yous!
2018-05-11 2127:38 - ERROR - 4. Here is an error
2018-05-11 2127:38 - ERROR - 4. Here is an error
2018-05-11 2127:38 - ERROR - 4. Here is an error
2018-05-11 2127:38 - CRITICAL - 5. This is f-ing critical!
2018-05-11 2127:38 - CRITICAL - 5. This is f-ing critical!
2018-05-11 2127:38 - CRITICAL - 5. This is f-ing critical!
我添加了一个FileHandler:
fh = logging.FileHandler("app.log")
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(fh)
这也重复:
xtian@spaceghost> cat app.log
2018-05-11 2159:24 - WARNING - 3. This is a warning!
2018-05-11 2159:24 - ERROR - 4. This is an error
2018-05-11 2159:24 - CRITICAL - 5. This is fucking critical!
[...]
2018-05-11 2201:00 - WARNING - 3. This is a warning, yous!
2018-05-11 2201:00 - WARNING - 3. This is a warning, yous!
2018-05-11 2201:00 - WARNING - 3. This is a warning, yous!
2018-05-11 2201:00 - ERROR - 4. Here is an error.
2018-05-11 2201:00 - ERROR - 4. Here is an error.
2018-05-11 2201:00 - ERROR - 4. Here is an error.
2018-05-11 2201:00 - CRITICAL - 5. This is f-ing critical!
2018-05-11 2201:00 - CRITICAL - 5. This is f-ing critical!
2018-05-11 2201:00 - CRITICAL - 5. This is f-ing critical!
我也遵循other posts的建议,并在消息调用之前添加了这些行:
# propagation fix
logger.propagate = False
结果是一样的。
设置发布,我看到一个类似的问题:
What could cause the logging module to log a record multiple times?
但所有这些调试都是针对OP的原始自定义代码。我的问题是示例代码,我希望它可以警告或更好地解释发生了什么。
文档说,
请注意。如果您将处理程序附加到记录器及其一个或多个祖先, 它可能会多次发出相同的记录。一般来说,你不应该 需要将处理程序附加到多个记录器 - 如果您只是附加 它到记录器层次结构中最高的适当记录器, 然后它会看到提供的所有后代记录器记录的所有事件 他们的传播设置被设置为True。常见的情况是 将处理程序仅附加到根记录器,并允许传播 照顾好其余的事。
您可以看到完整的测试文件main.py
here,看看我是否有'多个记录器'。
import logging
# Root Logger
logger = logging.getLogger(__name__)
# Console handler
ch = logging.StreamHandler()
ch.setLevel(logging.WARNING)
# Formatter
formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s' , datefmt='%Y-%m-%d %H%M:%S', style='%')
# Add formatter to Console handler ch
ch.setFormatter(formatter)
# Add ch to logger
logger.addHandler(ch)
# Text File handler
fh = logging.FileHandler("app.log")
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(fh)
# propagation fix
logger.propagate = False
# Example Application code
logger.debug("1. This is a debug message.")
logger.info("2. This is an info message.")
logger.warning('3. This is a warning!')
logger.error('4. This is an error')
logger.critical("5. This is fucking critical!")
答案 0 :(得分:1)
logger
返回的logging.getLogger(__name__)
每次执行%run main.py
时都是相同的记录器。但是
ch = logging.StreamHandler()
每次实例化一个新的StreamHandler,然后将其添加到logger
:
logger.addHandler(ch)
因此,在%run main.py
的后续运行中,logger
有多个处理程序
附加到它并且每个处理程序发出一条记录。
In [5]: %run main.py
2018-05-11 2251:17 - WARNING - 3. This is a warning!
2018-05-11 2251:17 - ERROR - 4. This is an error
2018-05-11 2251:17 - CRITICAL - 5. This is fucking critical!
In [6]: logger
Out[6]: <logging.Logger at 0x7f5d0152fe10>
第一次运行%run main.py
时,两个处理程序附加到logger
:
In [7]: logger.handlers
Out[12]:
[<logging.StreamHandler at 0x7f5d0152fdd8>,
<logging.FileHandler at 0x7f5d014c40f0>]
In [13]: %run main.py
2018-05-11 2251:44 - WARNING - 3. This is a warning!
2018-05-11 2251:44 - WARNING - 3. This is a warning!
2018-05-11 2251:44 - ERROR - 4. This is an error
2018-05-11 2251:44 - ERROR - 4. This is an error
2018-05-11 2251:44 - CRITICAL - 5. This is fucking critical!
2018-05-11 2251:44 - CRITICAL - 5. This is fucking critical!
第二次,现在有四个处理程序:
In [14]: logger.handlers
Out[14]:
[<logging.StreamHandler at 0x7f5d0152fdd8>,
<logging.FileHandler at 0x7f5d014c40f0>,
<logging.StreamHandler at 0x7f5d014c4668>,
<logging.FileHandler at 0x7f5d014c4550>]
In [15]: logger
Out[15]: <logging.Logger at 0x7f5d0152fe10>
为防止重复,您可以在logger.removeHandler
来电之间致电%run
:
In [29]: for handler in logger.handlers: logger.removeHandler(handler)
In [30]: %run main.py
2018-05-11 2257:30 - WARNING - 3. This is a warning!
2018-05-11 2257:30 - ERROR - 4. This is an error
2018-05-11 2257:30 - CRITICAL - 5. This is fucking critical!
或修改main.py
,以便每次调用%run
时都不会附加新的处理程序。
例如,您可以使用logging.config.dictConfig
设置logger
:
import logging
import logging.config
# Modified using https://stackoverflow.com/a/7507842/190597 as a template
logging_config = {
'version': 1,
'formatters': {
'standard': {
'format': '%(asctime)s - %(levelname)s - %(message)s'
},
},
'handlers': {
'stream': {
'level': 'INFO',
'formatter': 'standard',
'class': 'logging.StreamHandler',
},
'file': {
'level': 'DEBUG',
'formatter': 'standard',
'class': 'logging.FileHandler',
'filename': 'app.log'
},
},
'loggers': {
__name__: {
'handlers': ['stream', 'file'],
'level': 'WARN',
'propagate': False
},
}
}
logging.config.dictConfig(logging_config)
logger = logging.getLogger(__name__)
# Example Application code
logger.debug("1. This is a debug message.")
logger.info("2. This is an info message.")
logger.warning('3. This is a warning!')
logger.error('4. This is an error')
logger.critical("5. This is fucking critical!")
使用此代码,%run main.py
每次都会发出相同的输出。