gpt4 book ai didi

Python 日志记录和子进程输出和错误流

转载 作者:太空宇宙 更新时间:2023-11-03 14:32:01 25 4
gpt4 key购买 nike

我想启动一个 python 进程并将子进程错误消息记录到父脚本的日志记录对象中。理想情况下,我希望将日志流统一到一个文件中。我能以某种方式访问​​日志记录类的输出流吗?我知道的一种解决方案是使用 proc log 进行日志记录。如下面的答案所述,我可以从 proc.stdin 和 stderr 中读取,但我会有重复的日志记录头。我想知道是否有办法将日志类底层的文件描述符直接传递给子进程?

logging.basicConfig(filename="test.log",level=logging.DEBUG)
logging.info("Started")
procLog = open(os.path.expanduser("subproc.log"), 'w')
proc = subprocess.Popen(cmdStr, shell=True, stderr=procLog, stdout=procLog)
proc.wait()
procLog.flush()

最佳答案

基于 Adam Rosenfield's code ,你可以

  1. 使用select.select 阻塞直到有输出可读proc.stdoutproc.stderr,
  2. 读取并记录输出,然后
  3. 重复直到过程完成。

请注意,以下内容写入 /tmp/test.log 并运行命令 ls -laR/tmp。更改以满足您的需求。

(PS:通常/tmp 包含普通用户无法读取的目录,因此运行 ls -laR/tmp 会向 stdout 和 stderr 生成输出。下面的代码正确地交错了这两个流因为它们是生产出来的。)

import logging
import subprocess
import shlex
import select
import fcntl
import os
import errno
import contextlib

logger = logging.getLogger(__name__)

def make_async(fd):
'''add the O_NONBLOCK flag to a file descriptor'''
fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)

def read_async(fd):
'''read some data from a file descriptor, ignoring EAGAIN errors'''
try:
return fd.read()
except IOError, e:
if e.errno != errno.EAGAIN:
raise e
else:
return ''

def log_fds(fds):
for fd in fds:
out = read_async(fd)
if out:
logger.info(out)

@contextlib.contextmanager
def plain_logger():
root = logging.getLogger()
hdlr = root.handlers[0]
formatter_orig = hdlr.formatter
hdlr.setFormatter(logging.Formatter('%(message)s'))
yield
hdlr.setFormatter(formatter_orig)

def main():
# fmt = '%(name)-12s: %(levelname)-8s %(message)s'
logging.basicConfig(filename = '/tmp/test.log', mode = 'w',
level = logging.DEBUG)

logger.info("Started")
cmdStr = 'ls -laR /tmp'

with plain_logger():
proc = subprocess.Popen(shlex.split(cmdStr),
stdout = subprocess.PIPE, stderr = subprocess.PIPE)
# without `make_async`, `fd.read` in `read_async` blocks.
make_async(proc.stdout)
make_async(proc.stderr)
while True:
# Wait for data to become available
rlist, wlist, xlist = select.select([proc.stdout, proc.stderr], [], [])
log_fds(rlist)
if proc.poll() is not None:
# Corner case: check if more output was created
# between the last call to read_async and now
log_fds([proc.stdout, proc.stderr])
break

logger.info("Done")

if __name__ == '__main__':
main()

编辑:

您可以将 stdoutstderr 重定向到 logfile = open('/tmp/test.log', 'a')。然而,这样做的一个小困难是任何也在写入 /tmp/test.log 的记录器处理程序都不知道子进程正在写入什么,因此日志文件可能会乱码。

如果您在子进程执行其业务时不进行日志记录调用,那么唯一的问题是子进程完成后记录器处理程序在文件中的位置错误。这可以通过调用来修复

handler.stream.seek(0, 2)

因此处理程序将在文件末尾恢复写入。


import logging
import subprocess
import contextlib
import shlex

logger = logging.getLogger(__name__)

@contextlib.contextmanager
def suspended_logger():
root = logging.getLogger()
handler = root.handlers[0]
yield
handler.stream.seek(0, 2)

def main():
logging.basicConfig(filename = '/tmp/test.log', filemode = 'w',
level = logging.DEBUG)

logger.info("Started")
with suspended_logger():
cmdStr = 'test2.py 1>>/tmp/test.log 2>&1'
logfile = open('/tmp/test.log', 'a')
proc = subprocess.Popen(shlex.split(cmdStr),
stdout = logfile,
stderr = logfile)
proc.communicate()
logger.info("Done")

if __name__ == '__main__':
main()

关于Python 日志记录和子进程输出和错误流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9114217/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com