用Pythonic方式将特定信息记录到文件?
我明白Python的日志记录器不能直接创建,因为文档中提到:
注意,日志记录器从来不是直接实例化的,而是通过模块级别的函数
logging.getLogger(name)
来获取。
这很合理,因为你不应该为每个类或模块都创建日志记录器对象,因为有更好的方法。
不过,有些情况下我想要创建一个日志记录器对象,并专门为某个应用的输出附加一个文件,然后关闭这个日志文件。
举个例子,我有一个程序会构建所有在PyPI上的包。基本上可以想象有一个for
循环遍历每个包。在循环内部,我想要“创建”一个日志记录器,附加一个文件处理器(比如:/var/logs/pypi/django/20090302_1324.build.log),并将python setup.py build
的输出(还有其他内容)发送到这个日志文件。一旦完成,我想要关闭/销毁这个日志记录器,然后继续
以类似的方式构建其他包。
所以你看,正常的Python方式调用logging.getLogger
在这里不适用。我们需要创建临时的日志记录器对象。
目前,我通过将文件名本身作为日志记录器的名称来实现这一点:
>>> packagelog = logging.getLogger('/var/..../..34.log')
>>> # attach handler, etc..
我想问一下……有没有更好的方法来做到这一点?
3 个回答
假设你是通过子进程来调用 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
),前提是这个模块里有这样的日志记录器。
更新
根据回答的评论,听起来你只是想要 给当前模块的日志记录器添加一个新的 FileHandler,然后把日志记录到这个处理器里,最后再 从日志记录器中移除它。这样说更符合你的需求吗?
这里有两个问题:
- 在不同的处理阶段,能够将输出导向不同的日志文件。
- 能够将任意命令的标准输出和错误输出重定向到这些日志文件中。
关于第一个问题,我同意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()
至于第二个问题,在perform_build()
这一步,我假设为了简单起见,我们不需要考虑多核环境。那么,subprocess
模块就是你的好帮手。在下面的代码片段中,我省略了错误处理、复杂的格式和其他一些细节,但这应该能给你一个大致的概念。
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)
就这些了。
与其使用很多个日志记录器,不如用一个日志记录器配合多个处理器。举个例子:
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()