将Python'print'输出重定向到Logger

Rau*_*fle 25 python logging stdout

我有一个Python脚本,它使用'Print'打印到stdout.我最近通过Python Logger添加了日志记录,并希望如果启用了日志记录,这些打印语句将转到logger.我不想修改或删除这些打印语句.

我可以通过'log.info("some info msg")'来记录.我希望能够做到这样的事情:

if logging_enabled:
  sys.stdout=log.info
print("test")
Run Code Online (Sandbox Code Playgroud)

如果启用了日志记录,则应记录"test",就像我执行log.info("test")一样.如果未启用日志记录,则只应将"test"打印到屏幕上.

这可能吗?我知道我可以以类似的方式将stdout指向文件(请参阅:重定向打印到日志文件)

C0d*_*ker 20

您有两种选择:

  1. 打开一个日志文件并用它替换sys.stdout,而不是函数:

    log = open("myprog.log", "a")
    sys.stdout = log
    
    >>> print("Hello")
    >>> # nothing is printed because it goes to the log file instead.
    
    Run Code Online (Sandbox Code Playgroud)
  2. 用您的日志功能替换打印:

    # If you're using python 2.x, uncomment the next line
    #from __future__ import print_function
    print = log.info
    
    >>> print("Hello!")
    >>> # nothing is printed because log.info is called instead of print
    
    Run Code Online (Sandbox Code Playgroud)

  • 在第一个例子中,是否可以在文件和屏幕上同时使用stdout? (8认同)
  • 传递给 log.info 的参数与传递给 print 的参数具有不同的含义。例如,file= 参数允许您打印到任何文件。如果用记录仪整体替换打印功能,则该功能将丢失。仅应拦截打印到 stdout/stderr 的内容。 (2认同)
  • 警告!!如果 `print` 包含任何在 `log.info` 中不合适的参数,则此设置将失败,例如: `print("Hello", file = sys.stderr)` (2认同)

mgm*_*ros 11

当然,您可以打印到标准输出并附加到日志文件,如下所示:

# Uncomment the line below for python 2.x
#from __future__ import print_function

import logging

logging.basicConfig(level=logging.INFO, format='%(message)s')
logger = logging.getLogger()
logger.addHandler(logging.FileHandler('test.log', 'a'))
print = logger.info

print('yo!')
Run Code Online (Sandbox Code Playgroud)

  • 警告!!如果 `print` 包含任何在 `logger.info` 中不合适的参数,则此设置将失败,例如: `print("yo!", file = sys.stderr)` (3认同)

Cor*_*ein 11

还有一种方法是将记录器包装在一个对象中,该对象将调用转换write为记录器的log方法.

Ferry Boender就是这样做的,根据GPL许可证他的网站的帖子中提供:

import logging
import sys

class StreamToLogger(object):
   """
   Fake file-like stream object that redirects writes to a logger instance.
   """
   def __init__(self, logger, log_level=logging.INFO):
      self.logger = logger
      self.log_level = log_level
      self.linebuf = ''

   def write(self, buf):
      for line in buf.rstrip().splitlines():
         self.logger.log(self.log_level, line.rstrip())

logging.basicConfig(
   level=logging.DEBUG,
   format='%(asctime)s:%(levelname)s:%(name)s:%(message)s',
   filename="out.log",
   filemode='a'
)

stdout_logger = logging.getLogger('STDOUT')
sl = StreamToLogger(stdout_logger, logging.INFO)
sys.stdout = sl

stderr_logger = logging.getLogger('STDERR')
sl = StreamToLogger(stderr_logger, logging.ERROR)
sys.stderr = sl
Run Code Online (Sandbox Code Playgroud)

这使您可以轻松地将所有输出路由到您选择的记录器.如果需要,您可以在替换之前保存sys.stdout和/或sys.stderr按照其他人的提及,如果您需要稍后恢复它.


aid*_*ald 6

一个更简单的选择

import logging, sys
logging.basicConfig(filename='path/to/logfile', level=logging.DEBUG)
logger = logging.getLogger()
sys.stderr.write = logger.error
sys.stdout.write = logger.info
Run Code Online (Sandbox Code Playgroud)

  • 仅当您不使用logging.StreamHandler()还将日志打印到屏幕上时,此解决方案才有效。因为如果这样做,您将在无限循环中发送消息:流处理程序尝试将其写入sys.stdout.write,在此该消息被重定向到记录器,然后再次重定向到流处理程序。 (7认同)
  • 您可以使用“sys.__stderr__”作为“StreamHandler”,这一点永远不会改变。 (2认同)