如何禁用标准错误流的日志记录?

如何在Python中的标准错误流上禁用日志记录 ?这行不通:

import logging


logger = logging.getLogger()
logger.removeHandler(sys.stderr)
logger.warning('foobar')  # emits 'foobar' on sys.stderr
254743 次浏览

我不是很了解日志模块,但是我使用它的方式是我通常只希望禁用调试(或信息)消息。可以使用Handler.setLevel()将日志级别设置为CRITICAL或更高。

此外,您还可以替换sys。Stderr和sys。打开用于写入的文件的标准输出。看到http://docs.python.org/library/sys.html sys。<强> stdout < / >强。但我不建议这样做。

不需要转移标准输出。这里有一个更好的方法:

import logging
class MyLogHandler(logging.Handler):
def emit(self, record):
pass


logging.getLogger().addHandler(MyLogHandler())

一个更简单的方法是:

logging.getLogger().setLevel(100)

你可以使用:

logging.basicConfig(level=your_level)

其中your_level是其中之一:

'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL

因此,如果你将your_level设置为日志记录。至关重要的,你将只得到由以下发送的关键消息:

logging.critical('This is a critical error message')

your_level设置为日志记录。调试将显示所有级别的日志记录。

要了解更多细节,请查看日志记录的例子。

以同样的方式更改每个Handler的级别使用Handler.setLevel ()函数。

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)

我找到了一个解决方案:

logger = logging.getLogger('my-logger')
logger.propagate = False
# now if you use logger it will not log to console.

这将防止日志被发送到包含控制台日志的上层记录器。

我使用:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False

(早就死了的问题,但对未来的搜索者来说)

更接近最初海报的代码/意图,这适用于我在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")

我必须解决的问题是删除标准输出处理程序,添加一个新的;如果没有处理程序,记录器代码将自动重新添加标准输出。

IndexOutOfBound解决办法:如果你在实例化lhStdout时得到IndexOutOfBound错误,将实例化移动到添加你的文件处理程序后,即。

...
logger.addHandler(lh)


lhStdout = logger.handlers[0]
logger.removeHandler(lhStdout)

这里有一些非常好的答案,但显然没有考虑太多最简单的答案(只从无穷开始)。

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)

这意味着当它被禁用时,不会调用任何处理程序,并且它应该比过滤到一个非常高的值或设置一个no-op处理程序更有效。

使用上下文管理器- [最简单的]

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

AdvancedLogger can be used for fine grained logging temporary modifications


How it works:
Modifications will be enabled when context_manager/decorator starts working and be reverted after


Usage:
AdvancedLogger can be used
- as decorator `@AdvancedLogger()`
- as context manager `with  AdvancedLogger():`


It has three main functions/features:
- disable loggers and it's handlers by using disable_logger= argument
- enable/change loggers and it's handlers by using enable_logger= argument
- disable specific handlers for all loggers, by using  disable_handler= argument


All features they can be used together

AdvancedLogger的用例

# Disable specific logger handler, for example for stripe logger disable console
AdvancedLogger(disable_logger={"stripe": "console"})
AdvancedLogger(disable_logger={"stripe": ["console", "console2"]})


# Enable/Set loggers
# Set level for "stripe" logger to 50
AdvancedLogger(enable_logger={"stripe": 50})
AdvancedLogger(enable_logger={"stripe": {"level": 50, "propagate": True}})


# Adjust already registered handlers
AdvancedLogger(enable_logger={"stripe": {"handlers": "console"}

你还可以:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers

要完全禁用日志记录:

logging.disable(sys.maxint) # Python 2


logging.disable(sys.maxsize) # Python 3

要启用日志记录:

logging.disable(logging.NOTSET)

其他答案提供的工作并不能完全解决问题,例如

logging.getLogger().disabled = True

并且,对于一些大于50的n

logging.disable(n)

第一个解决方案的问题是它只适用于根日志记录器。使用logging.getLogger(__name__)创建的其他记录器不会被此方法禁用。

第二个解决方案确实会影响所有日志。但是它将输出限制在给定级别之上,因此可以通过记录级别大于50的日志来覆盖它。

这可以通过

logging.disable(sys.maxint)

据我所知(在审查之后)是完全禁用日志记录的唯一方法。

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

子类化你想要暂时禁用的处理程序:

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

通过更改“logging.config. log”中的一层。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,
},
}

})

使用修饰符找到了一个优雅的解决方案,它解决了以下问题:如果您正在编写一个具有多个函数的模块,每个函数都有几个调试消息,并且您想禁用除当前关注的函数之外的所有函数的登录,该怎么办?

你可以使用装饰器:

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_being_focused中调用function_already_debugged,来自function_already_debugged的调试消息也不会显示。 这将确保您将只看到来自您所关注的函数的调试消息

希望能有所帮助!

日志有以下结构:

  • 伐木工人是根据点分隔符的命名空间层次结构来排列的;
  • 每个记录器都有水平(根记录器默认为logging.WARNING,非根记录器默认为logging.NOTSET)和有效的水平(非根记录器的父记录器的有效级别,级别为logging.NOTSET,否则为记录器的级别);
  • 每个记录器都有过滤器的列表;
  • 每个记录器都有处理程序的列表;
  • 每个处理程序都有水平(默认为logging.NOTSET);
  • 每个处理器都有一个过滤器的列表。

日志有以下过程(由流程图表示):

Logging flow. < / >

因此,要禁用特定的记录器,您可以采用以下策略之一:

  1. 设置记录器的级别为logging.CRITICAL + 1

    • 使用主API:

      import logging
      
      
      logger = logging.getLogger("foo")
      logger.setLevel(logging.CRITICAL + 1)
      
    • 使用config API:

      import logging.config
      
      
      logging.config.dictConfig({
      "version": 1,
      "loggers": {
      "foo": {
      "level": logging.CRITICAL + 1
      }
      }
      })
      
  2. 向记录器添加过滤器lambda record: False

    • 使用主API:

      import logging
      
      
      logger = logging.getLogger("foo")
      logger.addFilter(lambda record: False)
      
    • 使用config API:

      import logging.config
      
      
      logging.config.dictConfig({
      "version": 1,
      "filters": {
      "all": {
      "()": lambda: (lambda record: False)
      }
      },
      "loggers": {
      "foo": {
      "filters": ["all"]
      }
      }
      })
      
  3. 删除记录器的现有处理程序,向记录器添加处理程序logging.NullHandler()(防止事件由处理程序logging.lastResort处理,它是一个使用当前流sys.stderr和级别logging.WARNINGlogging.StreamHandler处理)和将记录器的属性__ABC5设置为False(防止事件由记录器的祖先记录器的处理程序处理)。

    • 使用主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
      
    • 使用config API:

      import logging.config
      
      
      logging.config.dictConfig({
      "version": 1,
      "handlers": {
      "null": {
      "class": "logging.NullHandler"
      }
      },
      "loggers": {
      "foo": {
      "handlers": ["null"],
      "propagate": False
      }
      }
      })
      

警告。 -与策略1和2相反,策略1和2只阻止记录记录器的事件被记录器的处理程序及其祖先记录器发出,策略3也阻止记录记录器的事件由后代伐木工(例如logging.getLogger("foo.bar"))被记录器的处理程序及其祖先记录器发出。

请注意。 -将记录器的属性disabled设置为True不是另一种策略,因为它不是公共API (cf. https://bugs.python.org/issue36318)的一部分:

import logging


logger = logging.getLogger("foo")
logger.disabled = True  # DO NOT DO THIS

这将防止来自它在这里所描述的第三个库的所有日志记录 https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library < / p >

logging.getLogger('somelogger').addHandler(logging.NullHandler())

您可以更改特定处理程序的调试模式级别,而不是完全禁用它。

因此,如果你有一个情况,你想停止调试模式的控制台,但你仍然需要保持其他级别,如错误。你可以像下面这样做

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


考虑到您已经创建了自己的处理程序,那么在将它们添加到日志记录器之前,您可以这样做:

logger.removeHandler(logger.handlers[0])
这将删除默认的StreamHandler。 当我在Python 3.8上遇到不需要的日志发送到stderr时,这对我来说是有效的,而这些日志本应该只记录到一个文件中

这不是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)