【问题标题】:Pythonic way to log specific things to a file?将特定内容记录到文件中的 Pythonic 方式?
【发布时间】:2010-11-08 12:34:40
【问题描述】:

我了解 Python 记录器无法直接实例化,如文档所示:

请注意,Loggers 永远不会 直接实例化,但总是 通过模块级函数 logging.getLogger(name)

.. 这是合理的,因为您希望不会为每个类/模块创建记录器对象,因为有一个 better alternative

但是,在某些情况下,我想创建一个记录器对象并将一个文件附加到它,专门用于将一些特定于应用程序的输出记录到该文件;然后关闭日志文件。

例如,我有一个程序可以在PyPI 中构建所有包。所以基本上假设每个包都有一个for 循环。在循环内部,我想“创建”一个记录器,附加一个文件处理程序(例如:/var/logs/pypi/django/20090302_1324.build.log 并将python setup.py build 的输出(连同其他内容)发送到此日志文件。一旦完成,我想关闭/销毁记录器并continue以类似的方式构建其他包。

所以你看.. 调用logging.getLogger 的正常Pythonic 方式在这里不适用。需要创建临时记录器对象。

目前,我通过将文件名本身作为记录器名称传递来实现这一点:

>>> packagelog = logging.getLogger('/var/..../..34.log')
>>> # attach handler, etc..

我想问..有没有更好的方法来做到这一点?

【问题讨论】:

  • 您是在调用python setup.py build 作为子进程吗?
  • @cdleary,是的,当然。并将其输出(除其他外)发送到此日志文件。

标签: python logging


【解决方案1】:

假设您将 setup.py build 作为子进程调用,我认为您真的只是想要输出重定向,您可以通过子进程调用获得。

from subprocess import Popen
with open('/var/logs/pypi/django/%s.build.log' % time_str, 'w') as fh:
    Popen('python setup.py build'.split(), stdout=fh, stderr=fh).communicate()

如果您将 setup.py build 作为 Python 子例程调用(即导入该模块并调用它的主例程),那么您可以尝试将另一个 logging.Handler (FileHandler) 添加到该模块中的记录器,如果这样记录器存在。

更新

根据回答评论,听起来您只想将 add a new FileHandler 写入当前模块的记录器,然后将内容记录到该记录器中,然后是 remove it from the logger later on。这就是您要寻找的更多内容吗?

【讨论】:

  • 你没有理解我的问题。调用“python setup.py build”只是一个例子。我真正想做的是将一些特定的东西记录到文件中......暂时。
  • 哦,所以您想将当前程序的日志记录语句记录到该构建文件以及setup.py build 的输出?
  • 是的。您可以忽略该 setup.py 示例。这是一个普遍的问题。
  • 我可能误解了您所说的“特定事物”的含义。您的意思是您记录到当前模块记录器的一部分内容?例如,您希望将一些日志消息记录到该文件中,但同时也记录到模块的记录器中?
  • 我认为 Vinay 下面的阐述比我想要传达的更清楚。 :-)
【解决方案2】:

您可以使用一个记录器和多个处理程序,而不是使用多个记录器。例如:

log = logging.getLogger(name)
while some_condition:
    try:
        handler = make_handler(filename)
        log.addHandler(handler)
        # do something and log

    finally:
        log.removeHandler(handler)
        handler.close()

【讨论】:

  • +1: 完全正确--@srid: 而且我在原始代码中看不到任何非pythonic 的东西。将 logging.getLogger(...) 视为工厂方法,因此它实际上是为您创建一个记录器(或返回给定名称的现有记录器)。
  • "(...) 或返回已经存在的给定名称" - 这就是我的代码出现问题的地方.. 它希望 创建一个记录器。 ars 的解决方案正是我所需要的。
【解决方案3】:

这里有两个问题:

  1. 能够在流程的不同阶段将输出定向到不同的日志文件。
  2. 能够将任意命令的 stdout/stderr 重定向到相同的日志文件。

对于第 1 点,我会同意ars 的回答:他认为只使用多个处理程序和一个记录器。他的格式有点乱,所以我将在下面重申:

logger = logging.getLogger("pypibuild")
now_as_string = datetime.datetime.utcnow().strftime("%Y%m%d_%H%M")
for package in get_pypi_packages():
    fn = '/var/logs/pypi/%s/%s.log' % (package, now_as_string)
    h = logging.FileHandler(fn, 'w')
    logger.addHandler(h)
    perform_build(package)
    logger.removeHandler(h)
    h.close()

至于第 2 点,perform_build() 步骤,为了简单起见,我假设我们不需要担心多核环境。然后,subprocess 模块就是你的朋友。在下面的 sn-p 中,我省略了错误处理、花哨的格式和其他一些细节,但它应该给你一个公平的想法。

def perform_build(package):
    logger.debug("Starting build for package %r", package)
    command_line = compute_command_line_for_package(package)
    process = subprocess.Popen(command_line, shell=True,
                               stdin=PIPE, stdout=PIPE, stderr=PIPE)
    stdout, stderr = process.communicate()
    logger.debug("Build stdout contents: %r", stdout)
    logger.debug("Build stderr contents: %r", stderr)
    logger.debug("Finished build for package %r", package)

就是这样。

【讨论】:

  • 完美。对我来说已经足够好了 .. 并且不违反日志记录模块的设计。哦,有人会在 app/module.py 中使用logging.getLogger("app.module.pypibuild") .. 对吗?
  • 一个好的约定是在模块中使用logging.getLogger(__name__)
  • 好的,在这种情况下,我恢复使用logging.getLogger("__pypibuild__"),因为使用模块层次结构也会将日志消息发送到父处理程序。否则,通常..我使用__name__
猜你喜欢
  • 2020-04-27
  • 2014-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多