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

2025-02-28 08:22:00
admin
原创
62
摘要:问题描述:如何在 Python 中禁用标准错误流上的日志记录?这不起作用:import logging logger = logging.getLogger() logger.removeHandler(sys.stderr) logger.warning('foobar') # emits 'fooba...

问题描述:

如何在 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.NOTSETlogging.NOTSET否则为1);

  • 每个记录器都有一个过滤器列表;

  • 每个记录器都有一个处理程序列表;

  • 每个处理程序都有一个级别logging.NOTSET默认);

  • 每个处理程序都有一个过滤器列表。

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

记录流程。

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

  1. 将记录器的级别设置为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
        }
    }
})
  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"]
        }
    }
})
  1. 删除logger现有的handlers,为logger添加一个logging.NullHandler()handler(当在logger及其祖先中都找不到handler时,为防止将记录传递给该handler,该handler是logging.lastResort具有向logging.StreamHandlerlogging.WARNING发射级别的handler sys.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几乎总是指向根记录器。但可以想象您可能想将记录器的设置parentNone)。

有必要了解机制,然后进行合理化。一旦您了解了机制,这真的很容易。


  • 源代码(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()
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2941  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1803  
  PLM(产品生命周期管理)系统在企业的产品研发、生产与管理过程中扮演着至关重要的角色。然而,在实际运行中,资源冲突是经常会遇到的难题。资源冲突可能导致项目进度延迟、成本增加以及产品质量下降等一系列问题,严重影响企业的效益与竞争力。因此,如何有效应对PLM系统中的资源冲突,成为众多企业关注的焦点。接下来,我们将详细探讨5...
plm项目管理系统   31  
  敏捷项目管理与产品生命周期管理(PLM)的融合,正成为企业在复杂多变的市场环境中提升研发效率、增强竞争力的关键举措。随着技术的飞速发展和市场需求的快速更迭,传统的研发流程面临着诸多挑战,而将敏捷项目管理理念融入PLM,有望在2025年实现研发流程的深度优化,为企业创造更大的价值。理解敏捷项目管理与PLM的核心概念敏捷项...
plm项目   31  
  模块化设计在现代产品开发中扮演着至关重要的角色,它能够提升产品开发效率、降低成本、增强产品的可维护性与可扩展性。而产品生命周期管理(PLM)系统作为整合产品全生命周期信息的关键平台,对模块化设计有着强大的支持能力。随着技术的不断发展,到 2025 年,PLM 系统在支持模块化设计方面将有一系列令人瞩目的技术实践。数字化...
plm软件   28  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用