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

发布于 2024-08-21 13:12:04 字数 309 浏览 3 评论 0原文

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

import logging

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

How to disable logging on the standard error stream in Python? This does not work:

import logging

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(22

情痴 2024-08-28 13:12:04

我找到了一个解决方案:

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

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

I found a solution for this:

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

This will prevent logging from being sent to the upper logger that includes the console logging.

玩套路吗 2024-08-28 13:12:04

我用:

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

I use:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False
辞取 2024-08-28 13:12:04

您可以使用:

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 将显示所有级别的日志记录。

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

以相同的方式更改每个处理程序的级别,使用 处理程序.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)

You can use:

logging.basicConfig(level=your_level)

where your_level is one of those:

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

So, if you set your_level to logging.CRITICAL, you will get only critical messages sent by:

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

Setting your_level to logging.DEBUG will show all levels of logging.

For more details, please take a look at logging examples.

In the same manner to change level for each Handler use Handler.setLevel() function.

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)
灯下孤影 2024-08-28 13:12:04

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

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

如果您需要[更复杂]细粒度的解决方案,您可以查看高级记录器

Using Context manager - [ most simple ]

import logging 

class DisableLogger():
    def __enter__(self):
       logging.disable(logging.CRITICAL)
    def __exit__(self, exit_type, exit_value, exit_traceback):
       logging.disable(logging.NOTSET)

Example of use:

with DisableLogger():
    do_something()

If you need a [more COMPLEX] fine-grained solution you can look at AdvancedLogger

娜些时光,永不杰束 2024-08-28 13:12:04

(长久以来的死问题,但对于未来的搜索者)

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

IndexOutOfBound 修复: 如果在实例化 lhStdout 时遇到 IndexOutOfBound 错误,请将实例化移至添加文件处理程序之后,即

...
logger.addHandler(lh)

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

(long dead question, but for future searchers)

Closer to the original poster's code/intent, this works for me under 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")

The gotcha I had to work out was to remove the stdout handler after adding a new one; the logger code appears to automatically re-add the stdout if no handlers are present.

IndexOutOfBound Fix: If you get a IndexOutOfBound Error while instantiating lhStdout, move the instantiation to after adding your file handler i.e.

...
logger.addHandler(lh)

lhStdout = logger.handlers[0]
logger.removeHandler(lhStdout)
噩梦成真你也成魔 2024-08-28 13:12:04

完全禁用日志记录

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)

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

To fully disable logging:

logging.disable(sys.maxint) # Python 2

logging.disable(sys.maxsize) # Python 3

To enable logging:

logging.disable(logging.NOTSET)

Other answers provide work arounds which don't fully solve the problem, such as

logging.getLogger().disabled = True

and, for some n greater than 50,

logging.disable(n)

The problem with the first solution is that it only works for the root logger. Other loggers created using, say, logging.getLogger(__name__) are not disabled by this method.

The second solution does affect all logs. But it limits output to levels above that given, so one could override it by logging with a level greater than 50.

That can be prevented by

logging.disable(sys.maxint)

which as far as I can tell (after reviewing the source) is the only way to fully disable logging.

从来不烧饼 2024-08-28 13:12:04

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

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)

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

There are some really nice answers here, but apparently the simplest is not taken too much in consideration (only from infinito).

root_logger = logging.getLogger()
root_logger.disabled = True

This disables the root logger, and thus all the other loggers.
I haven't really tested but it should be also the fastest.

From the logging code in python 2.7 I see this

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)

Which means that when it's disabled no handler is called, and it should be more efficient that filtering to a very high value or setting a no-op handler for example.

谷夏 2024-08-28 13:12:04

日志记录具有以下结构

  • 记录器排列根据带有点分隔符的命名空间层次结构;
  • 每个记录器都有一个级别(根记录器默认为logging.WARNING,非根记录器默认为logging.NOTSET)和一个有效级别(记录器的第一个级别及其祖先不同于logging.NOTSET,否则为logging.NOTSET);
  • 每个记录器都有一个过滤器列表;
  • 每个记录器都有一个处理程序列表;
  • 每个处理程序都有一个级别(默认为logging.NOTSET);
  • 每个处理程序都有一个过滤器列表。过滤器

日志记录有以下过程(用流程图表示):

记录流程.

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

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

    • 使用主要 API:

      导入日志记录
      
      记录器=logging.getLogger(“foo”)
      logger.setLevel(logging.CRITICAL + 1)
      
    • 使用配置 API:

      导入logging.config
      
      日志记录.config.dictConfig({
          “版本”:1,
          “记录器”:{
              “富”:{
                  “级别”:logging.CRITICAL + 1
              }
          }
      })
      
  2. 向记录器添加过滤器 lambda record: False

    • 使用主要 API:

      导入日志记录
      
      记录器=logging.getLogger(“foo”)
      logger.addFilter(lambda 记录: False)
      
    • 使用配置 API:

      导入logging.config
      
      日志记录.config.dictConfig({
          “版本”:1,
          “过滤器”:{
              “全部”: {
                  “()”: lambda: (lambda 记录: False)
              }
          },
          “记录器”:{
              “富”:{
                  “过滤器”:[“全部”]
              }
          }
      })
      
  3. 删除记录器的现有处理程序,向记录器添加一个 logging.NullHandler() 处理程序(以防止记录传递到logging.lastResort 处理程序 当在记录器及其中找不到处理程序时祖先,它是一个 logging.StreamHandler 处理程序,具有发送到 sys.stderr 流的 logging.WARNING 级别)和 将记录器的 propagate 属性设置为 False (防止记录被传递给记录器祖先的处理程序)。

    • 使用主要 API:

      导入日志记录
      
      记录器=logging.getLogger(“foo”)
      对于 logger.handlers.copy() 中的处理程序:
          尝试:
              logger.removeHandler(处理程序)
          except ValueError: # 如果另一个线程已经删除了它
              经过
      logger.addHandler(logging.NullHandler())
      logger.propagate = False
      
    • 使用配置 API:

      导入logging.config
      
      日志记录.config.dictConfig({
          “版本”:1,
          “处理程序”:{
              “无效的”: {
                  "class": "logging.NullHandler"
              }
          },
          “记录器”:{
              “富”:{
                  “处理程序”:[“空”],
                  “传播”:错误
              }
          }
      })
      

警告。 - 与策略 1 和 2 相反,策略 1 和 2 仅阻止记录器记录记录(例如 logging.getLogger("foo")< /code>)避免由记录器及其祖先的处理程序发出,策略 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

Logging has the following structure:

  • loggers are arranged according to a namespace hierarchy with dot separators;
  • each logger has a level (logging.WARNING by default for the root logger and logging.NOTSET by default for non-root loggers) and an effective level (the first level of the logger and its ancestors different from logging.NOTSET, logging.NOTSET otherwise);
  • each logger has a list of filters;
  • each logger has a list of handlers;
  • each handler has a level (logging.NOTSET by default);
  • each handler has a list of filters.

Logging has the following process (represented by a flowchart):

Logging flow.

Therefore to disable a particular logger you can adopt one of the following strategies:

  1. Set the level of the logger to logging.CRITICAL + 1.

    • Using the main API:

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

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "loggers": {
              "foo": {
                  "level": logging.CRITICAL + 1
              }
          }
      })
      
  2. Add a filter lambda record: False to the logger.

    • Using the main API:

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

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "filters": {
              "all": {
                  "()": lambda: (lambda record: False)
              }
          },
          "loggers": {
              "foo": {
                  "filters": ["all"]
              }
          }
      })
      
  3. Remove the existing handlers of the logger, add a logging.NullHandler() handler to the logger (to prevent records from being passed to the logging.lastResort handler when no handler is found in the logger and its ancestors, which is a logging.StreamHandler handler with a logging.WARNING level that emits to the sys.stderr stream) and set the propagate attribute of the logger to False (to prevent records from being passed to the handlers of the logger’s ancestors).

    • Using the main 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
      
    • Using the config API:

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

Warning. — Contrary to strategies 1 and 2 which only prevent records logged by the logger (e.g. logging.getLogger("foo")) from being emitted by the handlers of the logger and its ancestors, strategy 3 also prevents records logged by the descendants of the logger (e.g. logging.getLogger("foo.bar")) to be emitted by the handlers of the logger and its ancestors.

Note. — Setting the disabled attribute of the logger to True is not yet another strategy, as it is not part of the public API (cf. https://bugs.python.org/issue36318):

import logging

logger = logging.getLogger("foo")
logger.disabled = True  # DO NOT DO THIS
咋地 2024-08-28 13:12:04

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

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

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

更简单的方法是:

logging.getLogger().setLevel(100)

No need to divert stdout. Here is better way to do it:

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

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

An even simpler way is:

logging.getLogger().setLevel(100)
日记撕了你也走了 2024-08-28 13:12:04

这里的答案很混乱。 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') 

我查看了源代码*。当框架发现 Logger 没有处理程序时,它会以一种有趣的方式表现。在这种情况下,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没有处理程序,并调用< code>lastResort:在上面的代码片段中尝试一下。

NB2 如果您确实想抑制根记录器的控制台输出,解决方案 2) 或 3) 可以使用。但它们不一定会抑制其他记录器的控制台输出。 (注意,通常他们会这样做,因为 parent ... parent ... parent 几乎总是导致根记录器。但可以想象,您可能想要将记录器的parent 设置为None)。
有必要了解其机制,然后进行合理化。一旦你理解了这个机制,事情就非常简单了。


* 源代码(注意 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\"\n" % self.name)
        self.manager.emittedNoHandlerWarning = True

这个 lastResort 是它本身是一个StreamHandler,它输出到stream方法中的sys.stderr

The answers here are confused. The OP could hardly be clearer: he wants to stop output to the console for a given logger. In his example, this is actually the root logger, but for most purposes this won't be the case. This is not about disabling handlers, or whatever. Nor about changing from stderr to stdout.

The confusing truth is that a non-root logger with ZERO handlers where the root logger also has ZERO handlers will still output to console (stderr rather than stdout). Try this:

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

I looked at the source code*. When the framework discovers that a Logger has no handlers it behaves in a funny way. In this case the logging framework will simply use a lastResort handler with any logger where it is found to have no handlers (including any from its parent or higher loggers). lastResort.stream() outputs to sys.stderr.

A first solution, therefore, is to give your non-root logger a non-console handler such as FileHandler (NB not all StreamHandlers necessarily output to console!)...

A second solution (for the simple example above) is to stop this lastResort output by giving your root logger a handler which does not output to console. In this case it is not necessary to stop propagation, my_logger.propagate = False. If there is a complex hierarchy of loggers, of course, you may have to follow the path of propagation upwards to identify any loggers with handlers outputting to console.

A third solution would be to substitute logging.lastResort:

logging.lastResort = logging.NullHandler()

Again, in a complex hierarchy, there may be handlers in higher loggers outputting to console.

NB just setting my_logger.propagate = False is NOT sufficient: in that case the framework will see that my_logger has no handlers, and call upon lastResort: try it in the above snippet.

NB2 if you did want to suppress console output for the root logger, solutions 2) or 3) would work. But they wouldn't suppress console output for other loggers, necessarily. (NB usually they would because parent ... parent ... parent almost always leads to the root logger. But conceivably you might want to set a logger's parent to None).
It's necessary to understand the mechanism, and then to rationalise. Once you understand the mechanism it's really quite easy.


* Source code (NB Python 3.10) is not in fact that difficult to follow. If you look at method Logger._log (where all messages get sent) this ends with self.handle(record), which calls self.callHandlers(record) which counts the number of handlers found, including by climbing upwards using Logger.parent, examining the handlers of ancestor loggers ... and then:

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\"\n" % self.name)
        self.manager.emittedNoHandlerWarning = True

This lastResort is itself a StreamHandler, which outputs to sys.stderr in method stream.

记忆消瘦 2024-08-28 13:12:04

这将阻止来自第三个库的所有日志记录,如此处所述
https://docs.python.org/ 3/howto/logging.html#configuring-logging-for-a-library

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

This will prevent all logging from a third library which it used as decribed here
https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library

logging.getLogger('somelogger').addHandler(logging.NullHandler())
七禾 2024-08-28 13:12:04

考虑到您已经创建了自己的处理程序,那么在将它们添加到记录器之前,您可以执行以下操作:

logger.removeHandler(logger.handlers[0])

这将删除默认的 StreamHandler。
在遇到不需要的日志发送到 stderr 后,这在 Python 3.8 上对我有用,而这些日志应该只记录到文件中。

Considering you have created your own handlers, then right before you add them to the logger, you can do:

logger.removeHandler(logger.handlers[0])

Which will remove the default StreamHandler.
This worked for me on Python 3.8 after encountering unwanted emitting of logs to stderr, when they should have been only recorded onto a file.

扛刀软妹 2024-08-28 13:12:04

这不是 100% 的解决方案,但这里的答案都没有解决我的问题。我有自定义日志记录模块,它根据严重性输出彩色文本。我需要禁用标准输出输出,因为它复制了我的日志。我同意将关键日志输出到控制台,因为我几乎不使用它。我没有测试它的 stderr,因为我不在日志记录中使用它,但应该与 stdout 一样工作。它将 CRITICAL 设置为标准输出(如果需要,则为标准错误)的最低严重性。

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)

It is not 100% solution, but none of the answers here solved my issue. I have custom logging module which outputs colored text according to severity. I needed to disable stdout output since it was duplicating my logs. I'm OK with critical logs being outputted to console since I almost don't use it. I didn't test it for stderr since I don't use it in my logging, but should work same way as stdout. It sets CRITICAL as minimal severity just for stdout (stderr if requested).

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)
我的痛♀有谁懂 2024-08-28 13:12:04
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
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')

Console output:

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 file content:

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
殊姿 2024-08-28 13:12:04

原因是您创建的所有记录器都

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 行它使用父级。

The reason is all loggers you create with

my_logger = logging.getLogger('some-logger')

have a parent field/attribute set to the root logger (which is the logger you get with:

root_logger = logging.getLogger()

when you call

my_logger.debug('something')

It will call all the handlers of your logger, and also, the handlers of the parent logger of your logger (in a recursive manner). And so, in few words it will call the root logger which will print in the std.err. How do you solve it? two solutions, global:

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 preferred solution is to mute root logger only for my logger:

my_logger = logging.getLogger('some-logger')
my_logger.parent = None

This is the code that gets called when you call .info, .debug, etc:
https://github.com/python/cpython/blob/44bd3fe570da9115bec67694404b8da26716a1d7/Lib/logging/init.py#L1758

notice how it goes through all handlers of your logger, and parents loggers too. Line 1766 it uses the parent.

回忆追雨的时光 2024-08-28 13:12:04

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

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

I don't know the logging module very well, but I'm using it in the way that I usually want to disable only debug (or info) messages. You can use Handler.setLevel() to set the logging level to CRITICAL or higher.

Also, you could replace sys.stderr and sys.stdout by a file open for writing. See http://docs.python.org/library/sys.html#sys.stdout. But I wouldn't recommend that.

薄荷梦 2024-08-28 13:12:04

您还可以:

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

You could also:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers
狼亦尘 2024-08-28 13:12:04

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

})

By changing one level in the "logging.config.dictConfig" you'll be able to take the whole logging level to a new level.

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,
    },
}

})

生来就爱笑 2024-08-28 13:12:04

使用装饰器找到了一个优雅的解决方案,它解决了以下问题:如果您正在编写一个具有多个函数的模块,每个函数都有多个调试消息,并且您希望禁用除您目前正在关注的一个?

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

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 的调试消息也不会被展示。
这可确保您只能看到您所关注的函数的调试消息。

希望有帮助!

Found an elegant solution using decorators, which addresses the following problem: what if you are writing a module with several functions, each of them with several debugging messages, and you want to disable logging in all functions but the one you are currently focusing on?

You can do it using decorators:

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

Then, you can do:

@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")
    ...

Even if you call function_already_debugged from within function_being_focused, debug messages from function_already_debugged won't be showed.
This ensures you will see only the debug messages from the function you are focusing on.

Hope it helps!

白云不回头 2024-08-28 13:12:04

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

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

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

You can change the level of debug mode for specific handler instead of completely disable it.

So if you have a case you want to stop the debug mode for console only but you still need to keep the other levels like the Error. you can do this like the following

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

2024-08-28 13:12:04

执行此操作的另一种方法(至少在 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")

Yet another way of doing this (at least in Python 3, I didn't check Python 2), is to first create your FileHandler and then call the basicConfig method, like this:

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")
山人契 2024-08-28 13:12:04

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

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

subclass the handler you want to be able to disable temporarily:

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)

finding the handler by name is quite easy:

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

once found:

_handler.disable()
doStuff()
_handler.enable()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文