如何在Python中禁用和重新启用控制台日志记录?


153

我正在使用Python的日志记录模块,并且想在一段时间内禁用控制台日志记录,但是它不起作用。

#!/usr/bin/python
import logging

logger = logging.getLogger() # this gets the root logger
# ... here I add my own handlers 
#logger.removeHandler(sys.stdout)
#logger.removeHandler(sys.stderr)

print logger.handlers 
# this will print [<logging.StreamHandler instance at ...>]
# but I may have other handlers there that I want to keep

logger.debug("bla bla")

上面的代码显示了bla blaon stdout,我不知道如何安全地禁用控制台处理程序。如何确定我暂时删除了控制台StreamHandler而不是另一个?


对于那些想知道为什么有人想要禁用日志记录的人:您不想记录密码或API密钥之类的私有数据。
Stevoisiak

4
@StevenVascellaro。为什么首先将这些发送到记录器?听起来不对…
疯狂物理学家

1
@MadPhysicist我有一个将XML请求发送到外部API的应用程序。默认情况下,这些请求将记录到文件中。但是,初始登录需要使用用户名和密码进行身份验证,而我不想登录。
Stevoisiak

@StevenVascellaro。我懂了。感谢您的解释。
疯狂物理学家

您没有显示添加处理程序的方式/位置。如果将它们添加到root记录器中,这将阻止日志添加默认的StreamHandler,如docs.python.org/3/library/logging.html#logging.basicConfig中所述。此外,根据链接说明,默认的StreamHandler仅在第一次添加时添加呼叫发出日志消息,因此在打印logger.handlers时应为空(因为它在logger.debug()呼叫之前)。有问题的代码仅显示[](处理程序的空列表)。已通过Python 2.7.15和Python 3.6.6验证。
Piotr Dobrogost

Answers:


197

我找到了一个解决方案:

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

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


8
我认为这不是一个好的解决方案。不向高级记录员传播可能会带来其他不良后果。
lfk

2
如果您只想过滤某个日志级别以下的消息(例如,所有INFO消息),则可以将第二行更改为logger.setLevel(logging.WARNING)
Hartley Brody,

2
之后您将如何重新启用日志?
Stevoisiak

4
不是答案为阻止传播有效地禁止了根记录的所有处理程序和问题,明确规定(...),但我可能有其他的处理程序有,我想保留这表明其意图是禁止根记录器的默认StreamHandler中的唯一
Piotr Dobrogost

仅停止消息传播还不够。从Python 3.2开始,在没有其他处理程序的情况下,处理logging.lastResort程序仍将记录严重性logging.WARNING及更高级别的消息sys.stderr看我的回答
Maggyero

106

我用:

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

9
这在logging模块级别也可以完全禁用日志记录,例如import logging; logging.disable(logging.CRITICAL);::docs.python.org/2/library/logging.html#logging.disable
lsh

1
这比禁用传播要好得多。
Mátray马克

6
不是一个答案 -这个问题问如何禁用默认StreamHandler中的唯一
Piotr Dobrogost

1
disabled属性不属于公共API。参见bugs.python.org/issue36318
Maggyero '19

69

您可以使用:

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,则将仅收到以下发送的关键消息:

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

your_level设置为logging.DEBUG将显示所有级别的日志记录。

有关更多详细信息,请查看 日志记录示例。

以相同的方式更改每个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)

6
这通常是有用的信息,但问题是如何禁用控制台日志记录,而不是如何添加其他处理程序。如果要在将上面的代码应用于原始示例的情况下检查my_logger.handlers,则会看到两个处理程序-新文件处理程序和原始流处理程序。

关键是我一直在寻找的词。谢谢。
Nishant

我希望看到调试级别为OFF。它是明确而简单的。
不是机器,

46

(长期存在的问题,但对于未来的搜索者而言)

更接近原始海报的代码/意图,这在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处理程序;如果没有处理程序,记录器代码似乎会自动重新添加标准输出。


2
该顺序logger = logging.getLogger(); lhStdout = logger.handlers[0]是错误的,因为根记录器最初没有处理程序– python -c "import logging; assert not logging.getLogger().handlers"。已通过Python 2.7.15和Python 3.6.6验证。
Piotr Dobrogost '18年

42

上下文管理器

import logging 
class DisableLogger():
    def __enter__(self):
       logging.disable(logging.CRITICAL)
    def __exit__(self, a, b, c):
       logging.disable(logging.NOTSET)

使用示例:

with DisableLogger():
    do_something()

我真的很喜欢这个习惯用法,但是我宁愿能够禁用特定的名称空间。例如,我只想暂时禁用根记录器。尽管使用了这种习惯用法,但我们应该能够临时添加/删除处理程序等。
克里斯

1
该问题询问如何禁用默认的StreamHandler 。
Piotr Dobrogost '18年

1
您不需要滚动自己的类,可以使用contextlib中的@contextmanager并编写一个yield函数
KristianR

如果您喜欢披萨上的异国水果。当然。
user3504575 '19

34

要完全禁用日志记录

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)

第一个解决方案的问题在于它仅适用于root记录器。例如,使用logging.getLogger(__name__)此方法创建的其他记录器不会被此方法禁用。

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

可以通过以下方式避免

logging.disable(sys.maxint)

据我所知(在查看源代码之后),这是完全禁用日志记录的唯一方法。


1
当问题询问如何
投票

27

这里有一些非常好的答案,但是显然最简单的答案没有太多考虑(仅来自infinito)。

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)

这意味着禁用该功能时,不会调用任何处理程序,例如,将其过滤到很高的值或设置一个无操作处理程序应该会更有效率。


1
除非我做错了,否则这只会禁用根记录器,不会禁用任何类似的东西log = logging.getLogger(__name__)
starfry

2
如果您要处理多个记录器或多个处理程序,则可能会出现问题。例如,如果您仍然想登录到文件,但想在特定情况下禁用流处理程序。

1
这将禁用根记录器,从而禁用所有其他记录器 -严格来说,禁用根记录器不会禁用任何其他记录器。此外,问题询问有关禁用默认StreamHandler的问题。
Piotr Dobrogost '18年

disabled属性不属于公共API。参见bugs.python.org/issue36318
Maggyero '19

10

无需转移标准输出。这是更好的方法:

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

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

一个更简单的方法是:

logging.getLogger().setLevel(100)

4
在Python 2.7+这可作为NullHandler()
皮埃尔·

1
读取logging.basicConfig()功能说明(强调我的意思)时,可以看到其工作原理(禁用默认的StreamHandler):通过使用默认的Formatter创建StreamHandler并将其添加到根记录器中,对记录系统进行基本配置。如果没有为root logger定义处理程序,则 debug(),info(),warning(),error()和critical()函数将自动调用basicConfig()。docs.python.org/3/library/logging.html#logging.basicConfig
Piotr Dobrogost

2

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

同样,您可以用打开的写入文件替换sys.stderr和sys.stdout。请参阅http://docs.python.org/library/sys.html#sys。标准输出。但我不建议这样做。


如果logger.handlers将包含某些内容(当前为),则此方法可行[]
索林2010年

2

您还可以:

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

为什么使用app.logger您甚至没有指定的问题而不是问题(logging.getLogger())和大多数答案中明确提到的root记录程序?您怎么知道可以安全地修改handlers属性而不是调用Logger.addHandler方法?
Piotr Dobrogost '18年

2
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

2
添加有关代码的一些描述。这会好得多
Mathews Sunny Sunny

2

通过在“ 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,
    },
}

})


1

找到了一个使用装饰器的优雅解决方案,该解决解决了以下问题:如果您要编写一个具有多个功能的模块,每个模块都具有多个调试消息,并且要禁用除当前关注的功能之外的所有功能,该怎么办?

您可以使用装饰器来做到这一点:

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也不会显示。这样可以确保您仅看到正在关注的功能中的调试消息。

希望能帮助到你!


0

如果要暂时禁用某个记录器,请执行以下操作。

日志示例

2019-10-02 21:28:45,663 django.request PID: 8  Internal Server Error: /service_portal/get_all_sites

django_request_logger = logging.getLogger('django.request')
django_request_logger.disabled = True
django_request_logger.disabled = False

0

在Logging Python库中,可以通过执行以下任一操作来完全禁用特定记录器的日志记录(对于所有级别):

  1. 向记录器添加一个logging.NullHandler()处理程序(以防止logging.lastResort处理程序将严重性logging.WARNING大于的事件记录到sys.stderr),并将该记录器propagate属性设置为False(以防止记录器将事件传递给其祖先记录器的处理程序)。

    • 使用主要API:

      import logging
      
      logging.getLogger("foo").addHandler(logging.NullHandler())
      logging.getLogger("foo").propagate = False
    • 使用配置API:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "handlers": {
              "null": {
                  "class": "logging.NullHandler"
              }
          },
          "loggers": {
              "foo": {
                  "handlers": ["null"],
                  "propagate": False
              }
          }
      })
  2. 向记录器添加lambda record: False过滤器。

    • 使用主要API:

      import logging
      
      logging.getLogger("foo").addFilter(lambda record: False)
    • 使用配置API:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "filters": {
              "all": {
                  "()": lambda: (lambda record: False)
              }
          },
          "loggers": {
              "foo": {
                  "filters": ["all"]
              }
          }
      })

警告。—与第一种解决方案相反,第二种解决方案不会禁用子记录器的记录(例如logging.getLogger("foo.bar")),因此,应仅使用它来禁用单个记录器的日志记录。

注意。—将记录器的disabled属性设置True为并不是第三种解决方案,因为它不是公共API的一部分。参见https://bugs.python.org/issue36318

import logging

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

-1

子类化您希望能够暂时禁用的处理程序:

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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.