如何禁用标准错误流上的日志记录?
- 2025-02-28 08:22:00
- admin 原创
- 61
问题描述:
如何在 Python 中禁用标准错误流上的日志记录?这不起作用:
import logging
logger = logging.getLogger()
logger.removeHandler(sys.stderr)
logger.warning('foobar') # emits 'foobar' on sys.stderr
解决方案 1:
我找到了一个解决方案:
logger = logging.getLogger('my-logger')
logger.propagate = False
# now if you use logger it will not log to console.
这将阻止将日志记录发送到包含控制台日志的上层记录器。
解决方案 2:
我使用:
logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False
解决方案 3:
您可以使用:
logging.basicConfig(level=your_level)
your_level是下列之一:
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL
因此,如果您将your_level设置为logs.CRITICAL,那么您将只会收到以下发送的关键消息:
logging.critical('This is a critical error message')
将your_level设置为logs.DEBUG将显示所有级别的日志记录。
欲了解更多详细信息,请查看日志示例。
以相同的方式使用Handler.setLevel()函数来改变每个 Handler 的级别。
import logging
import logging.handlers
LOG_FILENAME = '/tmp/logging_rotatingfile_example.out'
# Set up a specific logger with our desired output level
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)
# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(
LOG_FILENAME, maxBytes=20, backupCount=5)
handler.setLevel(logging.CRITICAL)
my_logger.addHandler(handler)
解决方案 4:
使用上下文管理器-[最简单]
import logging
class DisableLogger():
def __enter__(self):
logging.disable(logging.CRITICAL)
def __exit__(self, exit_type, exit_value, exit_traceback):
logging.disable(logging.NOTSET)
使用示例:
with DisableLogger():
do_something()
如果你需要一个[更复杂]的细粒度解决方案,你可以看看AdvancedLogger
解决方案 5:
(这个问题早已过时,但可供未来的搜索者参考)
更接近原始海报的代码/意图,这对我在 python 2.6 下有效
#!/usr/bin/python
import logging
logger = logging.getLogger() # this gets the root logger
lhStdout = logger.handlers[0] # stdout is the only handler initially
# ... here I add my own handlers
f = open("/tmp/debug","w") # example handler
lh = logging.StreamHandler(f)
logger.addHandler(lh)
logger.removeHandler(lhStdout)
logger.debug("bla bla")
我必须解决的问题是,在添加新的 stdout 处理程序后删除该处理程序;如果没有处理程序,记录器代码似乎会自动重新添加 stdout。
IndexOutOfBound 修复:如果在实例化 lhStdout 时收到 IndexOutOfBound 错误,请将实例化移至添加文件处理程序之后,即
...
logger.addHandler(lh)
lhStdout = logger.handlers[0]
logger.removeHandler(lhStdout)
解决方案 6:
要完全禁用日志记录:
logging.disable(sys.maxint) # Python 2
logging.disable(sys.maxsize) # Python 3
要启用日志记录:
logging.disable(logging.NOTSET)
其他答案提供了不能完全解决问题的解决方法,例如
logging.getLogger().disabled = True
对于一些n
超过 50 的人来说,
logging.disable(n)
第一个解决方案的问题在于它只适用于根记录器。logging.getLogger(__name__)
使用此方法无法禁用使用创建的其他记录器。
第二种解决方案确实会影响所有日志。但它将输出限制在给定级别之上,因此可以通过使用大于 50 的级别进行日志记录来覆盖它。
可以通过以下方式预防
logging.disable(sys.maxint)
据我所知(在查看了源代码之后)这是完全禁用日志记录的唯一方法。
解决方案 7:
这里有一些非常好的答案,但显然最简单的答案没有得到太多考虑(仅从无限角度考虑)。
root_logger = logging.getLogger()
root_logger.disabled = True
这将禁用根记录器,从而禁用所有其他记录器。我还没有真正测试过,但它应该是最快的。
从 python 2.7 中的日志代码中我看到了这一点
def handle(self, record):
"""
Call the handlers for the specified record.
This method is used for unpickled records received from a socket, as
well as those created locally. Logger-level filtering is applied.
"""
if (not self.disabled) and self.filter(record):
self.callHandlers(record)
这意味着当它被禁用时,不会调用任何处理程序,并且它应该比过滤到非常高的值或设置无操作处理程序更有效。
解决方案 8:
日志结构如下:
记录器按照带有点分隔符的命名空间层次结构排列;
每个logger都有一个级别(
logging.WARNING
根logger默认为1,logging.NOTSET
非根logger默认为1)和一个有效级别(与该logger及其祖先logger的第一个级别不同logging.NOTSET
,logging.NOTSET
否则为1);每个记录器都有一个过滤器列表;
每个记录器都有一个处理程序列表;
每个处理程序都有一个级别(
logging.NOTSET
默认);每个处理程序都有一个过滤器列表。
日志记录具有以下过程(以流程图表示):
因此,要禁用特定的记录器,您可以采用以下策略之一:
将记录器的级别设置为
logging.CRITICAL + 1
。
* 使用主要 API:
import logging
logger = logging.getLogger("foo")
logger.setLevel(logging.CRITICAL + 1)
* 使用配置 API:
import logging.config
logging.config.dictConfig({
"version": 1,
"loggers": {
"foo": {
"level": logging.CRITICAL + 1
}
}
})
lambda record: False
向记录器添加过滤器。
* 使用主要 API:
import logging
logger = logging.getLogger("foo")
logger.addFilter(lambda record: False)
* 使用配置 API:
import logging.config
logging.config.dictConfig({
"version": 1,
"filters": {
"all": {
"()": lambda: (lambda record: False)
}
},
"loggers": {
"foo": {
"filters": ["all"]
}
}
})
删除logger现有的handlers,为logger添加一个
logging.NullHandler()
handler(当在logger及其祖先中都找不到handler时,为防止将记录传递给该handler,该handler是logging.lastResort
具有向logging.StreamHandler
流logging.WARNING
发射级别的handlersys.stderr
),并将logger的属性设置propagate
`False`为(为防止将记录传递给logger祖先的handlers)。
* 使用主要 API:
import logging
logger = logging.getLogger("foo")
for handler in logger.handlers.copy():
try:
logger.removeHandler(handler)
except ValueError: # in case another thread has already removed it
pass
logger.addHandler(logging.NullHandler())
logger.propagate = False
* 使用配置 API:
import logging.config
logging.config.dictConfig({
"version": 1,
"handlers": {
"null": {
"class": "logging.NullHandler"
}
},
"loggers": {
"foo": {
"handlers": ["null"],
"propagate": False
}
}
})
警告。——与策略 1 和 2 相反,它们只阻止由记录器(例如logging.getLogger("foo")
) 记录的记录由记录器及其祖先的处理程序发出,策略 3 还阻止由记录器 (例如 ) 的后代logging.getLogger("foo.bar")
记录的记录由记录器及其祖先的处理程序发出。
注意。 — 将disabled
记录器的属性设置为True
并不是另一种策略,因为它不是公共 API 的一部分(参见https://bugs.python.org/issue36318):
import logging
logger = logging.getLogger("foo")
logger.disabled = True # DO NOT DO THIS
解决方案 9:
无需转移 stdout。以下是更好的方法:
import logging
class MyLogHandler(logging.Handler):
def emit(self, record):
pass
logging.getLogger().addHandler(MyLogHandler())
还有更简单的方式是:
logging.getLogger().setLevel(100)
解决方案 10:
这里的答案很混乱。OP 几乎不能更清楚了:他想要停止将给定记录器输出到控制台。在他的示例中,这实际上是根记录器,但在大多数情况下情况并非如此。这与禁用处理程序或其他任何内容无关。也与从 更改为stderr
无关stdout
。
令人困惑的事实是,如果根记录器也没有处理程序,并且非根记录器没有处理程序,则仍会输出到控制台(stderr
而不是stdout
)。试试这个:
import logging
root_logger = logging.getLogger()
root_logger.warning('root warning')
my_logger = logging.getLogger('whatever')
my_logger.warning('whatever warning')
# handlers situation?
my_logger.warning(f'len(root_logger.handlers) {len(root_logger.handlers)}, len(my_logger.handlers) {len(my_logger.handlers)}')
# ... BOTH ZERO
# is the parent of my_logger root_logger?
my_logger.warning(f'my_logger.parent == root_logger? {my_logger.parent == root_logger}')
# yes indeed
# what happens if we add a (non-console) handler to my_logger?
my_logger.addHandler(logging.FileHandler('output.txt'))
# ... for example. Another example would be my_logger.addHandler(logging.NullHandler())
# solution 2, see below:
# root_logger.addHandler(logging.FileHandler('output.txt'))
# solution 3, see below:
# logging.lastResort = logging.NullHandler()
# failure, see below:
# my_logger.propagate = False
root_logger.warning('root warning 2')
# success: this is output to the file but NOT to console:
my_logger.warning('whatever warning 2')
我查看了源代码*。当框架发现 aLogger
没有处理程序时,它的行为会很奇怪。在这种情况下,logging
框架将简单地使用lastResort
处理程序与任何被发现没有处理程序的记录器(包括来自其父级或更高级别的记录器的任何处理程序)。lastResort.stream()
输出到sys.stderr
。
因此,第一个解决方案是向非根记录器提供非控制台处理程序,例如FileHandler
(注意:并非所有StreamHandler
记录器都必然输出到控制台!)...
第二种解决方案(针对上面的简单示例)是lastResort
通过为根记录器提供一个不输出到控制台的处理程序来停止此输出。在这种情况下,没有必要停止传播。my_logger.propagate = False
当然,如果记录器的层次结构很复杂,您可能必须遵循向上传播的路径来识别任何具有输出到控制台的处理程序的记录器。
第三种解决方案是替换logging.lastResort
:
logging.lastResort = logging.NullHandler()
同样,在复杂的层次结构中,可能存在更高级别的记录器处理程序来输出到控制台。
注意:仅设置my_logger.propagate = False
是不够的:在这种情况下,框架将看到my_logger
没有处理程序,并调用lastResort
:在上面的代码片段中尝试它。
NB2 如果您确实想抑制根记录器的控制台输出,解决方案 2) 或 3) 会起作用。但它们不一定抑制其他记录器的控制台输出。(NB 通常它们会这样做,因为parent
... parent
...parent
几乎总是指向根记录器。但可以想象您可能想将记录器的设置parent
为None
)。
有必要了解机制,然后进行合理化。一旦您了解了机制,这真的很容易。
源代码(NB Python 3.10)实际上并不难理解。如果您查看方法
Logger._log
(所有消息都发送到该方法),该方法以 结尾self.handle(record)
,它会self.callHandlers(record)
调用 来计算找到的处理程序的数量,包括通过使用 向上攀升Logger.parent
,检查祖先记录器的处理程序...然后:
if (found == 0):
if lastResort:
if record.levelno >= lastResort.level:
lastResort.handle(record)
elif raiseExceptions and not self.manager.emittedNoHandlerWarning:
sys.stderr.write("No handlers could be found for logger"
" \"%s\"
" % self.name)
self.manager.emittedNoHandlerWarning = True
这lastResort
本身就是一个StreamHandler
,它输出到sys.stderr
方法中stream
。
解决方案 11:
这将阻止所有来自第三个库的日志记录,如下所述:
https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library
logging.getLogger('somelogger').addHandler(logging.NullHandler())
解决方案 12:
考虑到您已经创建了自己的处理程序,那么在将它们添加到记录器之前,您可以执行以下操作:
logger.removeHandler(logger.handlers[0])
这将删除默认的 StreamHandler。在我遇到不必要的日志发送到 stderr(而这些日志本应只记录到文件中)的情况后,这个方法在 Python 3.8 上对我有用。
解决方案 13:
这不是 100% 的解决方案,但这里的所有答案都无法解决我的问题。我有自定义日志模块,可根据严重性输出彩色文本。我需要禁用 stdout 输出,因为它会重复我的日志。我同意将关键日志输出到控制台,因为我几乎不使用它。我没有针对 stderr 进行测试,因为我在日志记录中不使用它,但应该与 stdout 的工作方式相同。它将 CRITICAL 设置为仅针对 stdout(如果需要,则为 stderr)的最低严重性。
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# disable terminal output - it is handled by this module
stdout_handler = logging.StreamHandler(sys.stdout)
# set terminal output to critical only - won't output lower levels
stdout_handler.setLevel(logging.CRITICAL)
# add adjusted stream handler
logger.addHandler(stdout_handler)
解决方案 14:
import logging
log_file = 'test.log'
info_format = '%(asctime)s - %(levelname)s - %(message)s'
logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'info_format': {
'format': info_format
},
},
'handlers': {
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'info_format'
},
'info_log_file': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'INFO',
'filename': log_file,
'formatter': 'info_format'
}
},
'loggers': {
'': {
'handlers': [
'console',
'info_log_file'
],
'level': 'INFO'
}
}
})
class A:
def __init__(self):
logging.info('object created of class A')
self.logger = logging.getLogger()
self.console_handler = None
def say(self, word):
logging.info('A object says: {}'.format(word))
def disable_console_log(self):
if self.console_handler is not None:
# Console log has already been disabled
return
for handler in self.logger.handlers:
if type(handler) is logging.StreamHandler:
self.console_handler = handler
self.logger.removeHandler(handler)
def enable_console_log(self):
if self.console_handler is None:
# Console log has already been enabled
return
self.logger.addHandler(self.console_handler)
self.console_handler = None
if __name__ == '__main__':
a = A()
a.say('111')
a.disable_console_log()
a.say('222')
a.enable_console_log()
a.say('333')
控制台输出:
2018-09-15 15:22:23,354 - INFO - object created of class A
2018-09-15 15:22:23,356 - INFO - A object says: 111
2018-09-15 15:22:23,358 - INFO - A object says: 333
test.log文件内容:
2018-09-15 15:22:23,354 - INFO - object created of class A
2018-09-15 15:22:23,356 - INFO - A object says: 111
2018-09-15 15:22:23,357 - INFO - A object says: 222
2018-09-15 15:22:23,358 - INFO - A object says: 333
解决方案 15:
原因是你创建的所有记录器
my_logger = logging.getLogger('some-logger')
将父字段/属性设置为根记录器(即您通过以下方式获取的记录器:
root_logger = logging.getLogger()
当你打电话时
my_logger.debug('something')
它将调用记录器的所有处理程序,以及记录器的父记录器的处理程序(以递归方式)。因此,简而言之,它将调用根记录器,该根记录器将在 std.err 中打印。您如何解决它?两个解决方案,全局:
root_logger = logging.getLogger()
# Remove the handler of root_logger making the root_logger useless
# Any logger you create now will have parent logger as root_logger but
# root_logger has been muted now as it does not have any handler to be called
root_logger.removeHandler(root_logger.handlers[0])
我的首选解决方案是仅为我的记录器静音根记录器:
my_logger = logging.getLogger('some-logger')
my_logger.parent = None
这是调用.info,.debug等时调用的代码:
https://github.com/python/cpython/blob/44bd3fe570da9115bec67694404b8da26716a1d7/Lib/logging/ init .py#L1758
注意它如何遍历记录器的所有处理程序以及父级记录器。第 1766 行它使用父级。
解决方案 16:
还有另一种方法(至少在 Python 3 中,我没有检查 Python 2),就是首先创建 FileHandler,然后调用 basicConfig 方法,如下所示:
import logging
template_name = "testing"
fh = logging.FileHandler(filename="testing.log")
logger = logging.getLogger(template_name)
logging.basicConfig(
format="%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s",
level=logging.INFO,
handlers=[fh],
)
logger.info("Test")
解决方案 17:
我不太了解日志模块,但我使用它的方式通常是仅禁用调试(或信息)消息。您可以使用Handler.setLevel()
它将日志级别设置为 CRITICAL 或更高。
另外,你可以用一个打开的可写文件替换 sys.stderr 和 sys.stdout。请参阅http://docs.python.org/library/sys.html#sys. stdout。但我不建议这样做。
解决方案 18:
您还可以:
handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers
解决方案 19:
通过改变“logging.config.dictConfig”中的一个级别,您将能够将整个日志记录级别提升到一个新的级别。
logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'console': {
'format': '%(name)-12s %(levelname)-8s %(message)s'
},
'file': {
'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'console'
},
#CHANGE below level from DEBUG to THE_LEVEL_YOU_WANT_TO_SWITCH_FOR
#if we jump from DEBUG to INFO
# we won't be able to see the DEBUG logs in our logging.log file
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'formatter': 'file',
'filename': 'logging.log'
},
},
'loggers': {
'': {
'level': 'DEBUG',
'handlers': ['console', 'file'],
'propagate': False,
},
}
})
解决方案 20:
找到了一个使用装饰器的优雅解决方案,它解决了以下问题:如果您正在编写一个包含多个函数的模块,每个函数都包含多条调试消息,并且您想禁用除当前关注的函数之外的所有函数的日志记录,该怎么办?
您可以使用装饰器来实现:
import logging, sys
logger = logging.getLogger()
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
def disable_debug_messages(func):
def wrapper(*args, **kwargs):
prev_state = logger.disabled
logger.disabled = True
result = func(*args, **kwargs)
logger.disabled = prev_state
return result
return wrapper
然后,你可以执行以下操作:
@disable_debug_messages
def function_already_debugged():
...
logger.debug("This message won't be showed because of the decorator")
...
def function_being_focused():
...
logger.debug("This message will be showed")
...
即使您function_already_debugged
从 内部调用function_being_focused
,也不会显示来自 的调试消息function_already_debugged
。这确保您只会看到来自您关注的函数的调试消息。
希望有帮助!
解决方案 21:
您可以更改特定处理程序的调试模式级别,而不是完全禁用它。
因此,如果您只想停止控制台的调试模式,但仍需要保留其他级别(例如错误)。您可以像下面这样执行此操作
# create logger
logger = logging.getLogger(__name__)
def enableConsoleDebug (debug = False):
#Set level to logging.DEBUG to see CRITICAL, ERROR, WARNING, INFO and DEBUG statements
#Set level to logging.ERROR to see the CRITICAL & ERROR statements only
logger.setLevel(logging.DEBUG)
debugLevel = logging.ERROR
if debug:
debugLevel = logging.DEBUG
for handler in logger.handlers:
if type(handler) is logging.StreamHandler:
handler.setLevel (debugLevel)
解决方案 22:
将您想要暂时禁用的处理程序子类化:
class ToggledHandler(logging.StreamHandler):
"""A handler one can turn on and off"""
def __init__(self, args, kwargs):
super(ToggledHandler, self).__init__(*args, **kwargs)
self.enabled = True # enabled by default
def enable(self):
"""enables"""
self.enabled = True
def disable(self):
"""disables"""
self.enabled = False
def emit(self, record):
"""emits, if enabled"""
if self.enabled:
# this is taken from the super's emit, implement your own
try:
msg = self.format(record)
stream = self.stream
stream.write(msg)
stream.write(self.terminator)
self.flush()
except Exception:
self.handleError(record)
通过名称找到处理程序非常容易:
_handler = [x for x in logging.getLogger('').handlers if x.name == your_handler_name]
if len(_handler) == 1:
_handler = _handler[0]
else:
raise Exception('Expected one handler but found {}'.format(len(_handler))
一旦发现:
_handler.disable()
doStuff()
_handler.enable()
扫码咨询,免费领取项目管理大礼包!