在运行logging.basicConfig之前进行Python日志记录?

发布于 2024-08-15 19:49:56 字数 186 浏览 5 评论 0原文

看来,如果您调用 logging.info() BEFORE 运行 logging.basicConfig,则 logging.basicConfig调用没有任何效果。事实上,没有发生任何日志记录。

此行为记录在哪里?我实在不明白。

It appears that if you invoke logging.info() BEFORE you run logging.basicConfig, the logging.basicConfig call doesn't have any effect. In fact, no logging occurs.

Where is this behavior documented? I don't really understand.

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

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

发布评论

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

评论(7

夜司空 2024-08-22 19:49:56

您可以删除默认处理程序并重新配置日志记录,如下所示:

# if someone tried to log something before basicConfig is called, Python creates a default handler that
# goes to the console and will ignore further basicConfig calls. Remove the handler if there is one.
root = logging.getLogger()
if root.handlers:
    for handler in root.handlers:
        root.removeHandler(handler)
logging.basicConfig(format='%(asctime)s %(message)s',level=logging.DEBUG)

You can remove the default handlers and reconfigure logging like this:

# if someone tried to log something before basicConfig is called, Python creates a default handler that
# goes to the console and will ignore further basicConfig calls. Remove the handler if there is one.
root = logging.getLogger()
if root.handlers:
    for handler in root.handlers:
        root.removeHandler(handler)
logging.basicConfig(format='%(asctime)s %(message)s',level=logging.DEBUG)
×纯※雪 2024-08-22 19:49:56

是的。

您已要求记录一些内容。因此,日志记录必须创建默认配置。一旦配置了日志记录...好吧...它就配置好了。

“配置记录器对象后,
以下方法创建日志
消息:“

此外,您可以阅读有关创建处理程序以防止虚假日志记录的信息。但这更多的是对不良实施的黑客攻击,而不是有用的技术。

这有一个技巧。

  1. 除了 logging.getLogger 之外,没有模块可以做任何事情() 全球级别的请求。

  2. 只有if __name__ == "__main__":可以进行日志配置。

如果您在模块中进行全局级别的日志记录,那么您可以强制日志记录以伪造其默认配置。

不要在任何模块中全局执行logging.info。如果您绝对认为模块中必须在全局级别拥有 logging.info,那么您必须在执行导入之前配置日志记录。这会导致脚本看起来不舒服。

Yes.

You've asked to log something. Logging must, therefore, fabricate a default configuration. Once logging is configured... well... it's configured.

"With the logger object configured,
the following methods create log
messages:"

Further, you can read about creating handlers to prevent spurious logging. But that's more a hack for bad implementation than a useful technique.

There's a trick to this.

  1. No module can do anything except logging.getLogger() requests at a global level.

  2. Only the if __name__ == "__main__": can do a logging configuration.

If you do logging at a global level in a module, then you may force logging to fabricate it's default configuration.

Don't do logging.info globally in any module. If you absolutely think that you must have logging.info at a global level in a module, then you have to configure logging before doing imports. This leads to unpleasant-looking scripts.

同展鸳鸯锦 2024-08-22 19:49:56

Carlos A. Ibarra 的回答原则上是正确的,但是该实现可能会中断,因为您正在迭代可能通过调用removeHandler() 来更改的列表。这是不安全的。
两个替代方案是:

while len(logging.root.handlers) > 0:
    logging.root.removeHandler(logging.root.handlers[-1])
logging.basicConfig(format='%(asctime)s %(message)s',level=logging.DEBUG)

或:

logging.root.handlers = []
logging.basicConfig(format='%(asctime)s %(message)s',level=logging.DEBUG)

其中使用循环的这两个中的第一个是最安全的(因为可以在日志记录框架内显式调用处理程序的任何销毁代码)。尽管如此,这仍然是一个黑客行为,因为我们依赖logging.root.handlers作为一个列表。

This answer from Carlos A. Ibarra is in principle right, however that implementation might break since you are iterating over a list that might be changed by calling removeHandler(). This is unsafe.
Two alternatives are:

while len(logging.root.handlers) > 0:
    logging.root.removeHandler(logging.root.handlers[-1])
logging.basicConfig(format='%(asctime)s %(message)s',level=logging.DEBUG)

or:

logging.root.handlers = []
logging.basicConfig(format='%(asctime)s %(message)s',level=logging.DEBUG)

where the first of these two using the loop is the safest (since any destruction code for the handler can be called explicitly inside the logging framework). Still, this is a hack, since we rely on logging.root.handlers to be a list.

我一直都在从未离去 2024-08-22 19:49:56

这是上面的答案没有提到的难题之一......然后一切都会有意义:“根”记录器 - 如果您在记录之前调用logging.info(),则会使用它。 basicConfig(level=logging.DEBUG) -- 默认日志记录级别为 WARNING。

这就是为什么logging.info()和logging.debug()不做任何事情:因为你已经配置它们,通过...嗯...不配置它们。

可能相关(这一点对我来说):当不调用 basicConfig 时,即使我将处理程序设置为 DEBUG 级别,我似乎也没有收到调试消息。经过一番绞尽脑汁后,我发现您还必须将自定义记录器的级别设置为“DEBUG”。如果您的记录器设置为 WARNING,则将处理程序设置为 DEBUG(单独)不会在 logger.info() 和 logger.debug() 上获得任何输出。

Here's the one piece of the puzzle that the above answers didn't mention... and then it will all make sense: the "root" logger -- which is used if you call, say, logging.info() before logging.basicConfig(level=logging.DEBUG) -- has a default logging level of WARNING.

That's why logging.info() and logging.debug() don't do anything: because you've configured them not to, by... um... not configuring them.

Possibly related (this one bit me): when NOT calling basicConfig, I didn't seem to be getting my debug messages, even though I set my handlers to DEBUG level. After a bit of hair-pulling, I found you have to set the level of the custom logger to be DEBUG as well. If your logger is set to WARNING, then setting a handler to DEBUG (by itself) won't get you any output on logger.info() and logger.debug().

番薯 2024-08-22 19:49:56

今天遇到了同样的问题,作为上述答案的替代方案,这是我的解决方案。

import logging
import sys

logging.debug('foo') # IRL, this call is from an imported module

if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO, force=True)
    logging.info('bar') # without force=True, this is not printed to the console

以下是文档关于force<的内容/代码> 参数。

如果此关键字参数指定为 true,则任何现有处理程序
在携带之前,连接到根记录器的设备被移除并关闭
输出其他参数指定的配置。

Ran into this same issue today and, as an alternative to the answers above, here's my solution.

import logging
import sys

logging.debug('foo') # IRL, this call is from an imported module

if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO, force=True)
    logging.info('bar') # without force=True, this is not printed to the console

Here's what the docs say about the force argument.

If this keyword argument is specified as true, any existing handlers
attached to the root logger are removed and closed, before carrying
out the configuration as specified by the other arguments.

〗斷ホ乔殘χμё〖 2024-08-22 19:49:56

@paul-kremer 给出的答案的更清晰版本是:

while len(logging.root.handlers):
    logging.root.removeHandler(logging.root.handlers[-1])

注意:通常可以安全地假设logging.root.handlers 始终是一个列表(请参阅:https://github.com/python/cpython/blob/cebe9ee988837b292f2c571e194ed11e7cd4abbb/Lib/logging/初始化.py#L1253)

A cleaner version of the answer given by @paul-kremer is:

while len(logging.root.handlers):
    logging.root.removeHandler(logging.root.handlers[-1])

Note: it is generally safe to assume logging.root.handlers will always be a list (see: https://github.com/python/cpython/blob/cebe9ee988837b292f2c571e194ed11e7cd4abbb/Lib/logging/init.py#L1253)

如梦 2024-08-22 19:49:56

这就是我所做的。

我想记录到一个在配置文件中配置了名称的文件,并获取配置解析的调试日志。

TL;博士;这会记录到缓冲区中,直到配置记录器的所有内容都可用

# Log everything into a MemoryHandler until the real logger is ready.
# The MemoryHandler never flushes (flushLevel 100 is above CRITICAL) automatically but only on close.
# If the configuration was loaded successfully, the real logger is configured and set as target of the MemoryHandler
# before it gets flushed by closing.
# This means, that if the log gets to stdout, it is unfiltered by level
root_logger = logging.getLogger()
root_logger.setLevel(logging.NOTSET)
stdout_logging_handler = logging.StreamHandler(sys.stderr)
tmp_logging_handler = logging.handlers.MemoryHandler(1024 * 1024, 100, stdout_logging_handler)
root_logger.addHandler(tmp_logging_handler)

config: ApplicationConfig = ApplicationConfig.from_filename('config.ini')

# because the records are already logged, unwanted ones need to be removed
filtered_buffer = filter(lambda record: record.levelno >= config.main_config.log_level, tmp_logging_handler.buffer)
tmp_logging_handler.buffer = filtered_buffer

root_logger.removeHandler(tmp_logging_handler)
logging.basicConfig(filename=config.main_config.log_filename, level=config.main_config.log_level, filemode='wt')
logging_handler = root_logger.handlers[0]
tmp_logging_handler.setTarget(logging_handler)
tmp_logging_handler.close()
stdout_logging_handler.close()

Here is what I did.

I wanted to log to a file which has a name configured in a config-file and also get the debug-logs of the config-parsing.

TL;DR; This logs into a buffer until everything to configure the logger is available

# Log everything into a MemoryHandler until the real logger is ready.
# The MemoryHandler never flushes (flushLevel 100 is above CRITICAL) automatically but only on close.
# If the configuration was loaded successfully, the real logger is configured and set as target of the MemoryHandler
# before it gets flushed by closing.
# This means, that if the log gets to stdout, it is unfiltered by level
root_logger = logging.getLogger()
root_logger.setLevel(logging.NOTSET)
stdout_logging_handler = logging.StreamHandler(sys.stderr)
tmp_logging_handler = logging.handlers.MemoryHandler(1024 * 1024, 100, stdout_logging_handler)
root_logger.addHandler(tmp_logging_handler)

config: ApplicationConfig = ApplicationConfig.from_filename('config.ini')

# because the records are already logged, unwanted ones need to be removed
filtered_buffer = filter(lambda record: record.levelno >= config.main_config.log_level, tmp_logging_handler.buffer)
tmp_logging_handler.buffer = filtered_buffer

root_logger.removeHandler(tmp_logging_handler)
logging.basicConfig(filename=config.main_config.log_filename, level=config.main_config.log_level, filemode='wt')
logging_handler = root_logger.handlers[0]
tmp_logging_handler.setTarget(logging_handler)
tmp_logging_handler.close()
stdout_logging_handler.close()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文