Python 记录交互式会话

发布于 2024-10-17 23:07:21 字数 1141 浏览 3 评论 0原文

我正在尝试在我的 Python 2.7 应用程序中实现日志记录,并发现它非常有用。然而,我注意到,当交互运行 Python 时,每条日志消息都会打印多次。打印消息的次数与我之前运行脚本的次数相同,因此似乎记录器在脚本末尾没有被正确清理(我猜)。考虑以下示例:

import sys
import logging

def main(argv=None):

    log = logging.getLogger('test')
    log.setLevel(logging.DEBUG)

    console_handler = logging.StreamHandler()
    console_handler.setFormatter(logging.Formatter("%(message)s"))
    log.addHandler(console_handler)

    log.info('Starting something...')
    log.info('Doing something...')
    log.info('Finished something.')

    logging.shutdown()

if __name__=='__main__':
    sys.exit(main(sys.argv[1:]))

键入会

>>> import file.py
>>> file.main()

产生以下结果:

Starting something...
Doing something...
Finished something.

然后第二次键入 file.main() 会产生:

Starting something...
Starting something...
Doing something...
Doing something...
Finished something.
Finished something.

重复第三次将给出三条或每条消息,依此类推。有谁知道为什么会发生这种情况 - 这是日志记录模块的预期行为吗?如果是,我该如何更改?如果按照预期作为脚本 (python file.py) 运行,上述脚本仅打印每条消息之一。

I am trying to implement logging in my Python 2.7 applications and have found it very useful. However, I have noticed that when running Python interactively each logging message prints multiple times. The number of times the message is printed is the same as the number of times I have previously run the script, so it seems that the logger is not being cleaned up properly at the end of the script (I would guess). Consider the following example:

import sys
import logging

def main(argv=None):

    log = logging.getLogger('test')
    log.setLevel(logging.DEBUG)

    console_handler = logging.StreamHandler()
    console_handler.setFormatter(logging.Formatter("%(message)s"))
    log.addHandler(console_handler)

    log.info('Starting something...')
    log.info('Doing something...')
    log.info('Finished something.')

    logging.shutdown()

if __name__=='__main__':
    sys.exit(main(sys.argv[1:]))

Typing

>>> import file.py
>>> file.main()

yields the following:

Starting something...
Doing something...
Finished something.

Then typing file.main() a second time yields:

Starting something...
Starting something...
Doing something...
Doing something...
Finished something.
Finished something.

Repeating a third time would give three or each message and so on. Does anyone know why this is happening - is this the expected behaviour of the logging module and, if so, how can I change this? The above script only prints one of each message if run as a script (python file.py), as expected.

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

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

发布评论

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

评论(4

分开我的手 2024-10-24 23:07:21

是的,您正在创建并重用记录器的单个实例。添加到该记录器的每个处理程序也会记录一条消息。

您可能希望日志记录设置在模块级别或在单独的函数中,这样您只需运行一次。

也许是这样的:

import atexit
import sys
import logging

log = logging.getLogger('test')
log.setLevel(logging.DEBUG)

console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter("%(message)s"))
log.addHandler(console_handler)

def shutdown_logging():
    logging.shutdown()    

atexit.register(shutdown_logging)

def main(argv=None):
    log.info('Starting something...')
    log.info('Doing something...')
    log.info('Finished something.')


if __name__=='__main__':
    sys.exit(main(sys.argv[1:]))

Yes, you are creating and reusing a single instance of your logger. Each handler added to that logger is also logging a message.

You probably want logging setup to be at the module level or in a separate function so you only run it once.

Maybe something like this:

import atexit
import sys
import logging

log = logging.getLogger('test')
log.setLevel(logging.DEBUG)

console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter("%(message)s"))
log.addHandler(console_handler)

def shutdown_logging():
    logging.shutdown()    

atexit.register(shutdown_logging)

def main(argv=None):
    log.info('Starting something...')
    log.info('Doing something...')
    log.info('Finished something.')


if __name__=='__main__':
    sys.exit(main(sys.argv[1:]))
横笛休吹塞上声 2024-10-24 23:07:21

尝试将此作为解决方法:

if len(logging.root.handlers) == 0:
     log.add_handler(console_handler)

日志记录模块使用全局静态记录器对象,当您在解释器中时,该对象在整个会话中持续存在。因此,每次调用 add_handler 时,您都会添加一个全新的流处理程序,但不会删除旧的流处理程序。日志记录只是迭代其处理程序并将输出发送到每个处理程序,因此每次运行时都会将相同内容的新副本发送到控制台。

Try this as a workaround:

if len(logging.root.handlers) == 0:
     log.add_handler(console_handler)

The logging module uses a globally static logger object that persists across your session when you are in the interpreter. So every time you call add_handler you're adding a brand new stream handler but not removing the old one. Logging just iterates through its handlers and sends the output to each, so you have a new copy of the same thing going to the console every time you run.

痕至 2024-10-24 23:07:21

您可以在每次重新加载配置时删除所有处理程序,在您的情况下,在调用 file.main() 之前:

file.logging.getLogger('test').handlers = []

注意(主观):

正如 @stderr 所写,鼓励在模块级别定义记录器。但是,我认为在应用程序入口点设置它们也是一个很好的做法,因此在这里我只会在 if __name__=='__main__' 之后添加处理程序,或者在您的情况下,在你的(I)Python 控制台。这样,导入模块不会创建各种处理程序,而这些处理程序仅由选择执行模块的某些功能的人创建。

You could delete all handlers every time you reload your configuration, in your case before calling file.main():

file.logging.getLogger('test').handlers = []

Note (subjective):

As @stderr wrote, it's encouraged to define your loggers at module level. However, I think it's also a good practice to set them up at the application entry point, so here I would only add the handlers after the if __name__=='__main__', or in your case, in your (I)Python console. That way, importing the module does not create all sorts of handlers, but those are only created by the person who chooses to execute some functions of your module.

无尽的现实 2024-10-24 23:07:21

这是 @beer_monk 解决方案的替代方案,即使您在其他地方接触了根处理程序,它也可以工作。我在这里使用了 __name__ 而不是 'test',这样可以更轻松地在其他模块中重用代码,也许可以将其制作成一个采用 __name__ 的函数 作为参数。

if not __name__ in logging.Logger.manager.loggerDict:
    new_logger = logging.getLogger(__name__)
    new_logger.setLevel(logging.DEBUG)

    console_handler = logging.StreamHandler()
    console_handler.setFormatter(logging.Formatter("%(message)s"))
    new_logger.addHandler(console_handler)
log = logging.getLogger(__name__)

Here's an alternative to @beer_monk's solution, which will work even if you've touched the root handler somewhere else. I've used __name__ instead of 'test' here so that it's easier to reuse the code in other modules, perhaps by making this into a function that takes __name__ as an argument.

if not __name__ in logging.Logger.manager.loggerDict:
    new_logger = logging.getLogger(__name__)
    new_logger.setLevel(logging.DEBUG)

    console_handler = logging.StreamHandler()
    console_handler.setFormatter(logging.Formatter("%(message)s"))
    new_logger.addHandler(console_handler)
log = logging.getLogger(__name__)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文