簡體   English   中英

將 Python 'print' 輸出重定向到 Logger

[英]Redirect Python 'print' output to Logger

我有一個使用“打印”打印到標准輸出的 Python 腳本。 我最近通過 Python Logger 添加了日志記錄,並希望這樣做,如果啟用了日志記錄,這些打印語句將轉到記錄器。 我不想修改或刪除這些打印語句。

我可以通過執行'log.info("some info msg")'來登錄。 我希望能夠做這樣的事情:

if logging_enabled:
  sys.stdout=log.info
print("test")

如果啟用日志記錄,“test”應該被記錄,就像我做了 log.info("test") 一樣。 如果未啟用日志記錄,則應將“測試”打印到屏幕上。

這可能嗎? 我知道我可以以類似的方式將標准輸出定向到文件(請參閱: 將打印重定向到日志文件

您有兩個選擇:

  1. 打開一個日志文件並用它替換 sys.stdout,而不是一個函數:

     log = open("myprog.log", "a") sys.stdout = log >>> print("Hello") >>> # nothing is printed because it goes to the log file instead.
  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

當然,您既可以打印到標准輸出,也可以附加到日志文件,如下所示:

# 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!')

另一種方法是將記錄器包裝在一個對象中,該對象將調用轉換為write記錄器的log方法。

Ferry Boender 就是這樣做的,根據 GPL 許可他的網站的帖子中提供。 下面的代碼基於此,但解決了原始代碼的兩個問題:

  1. 該類沒有實現程序退出時調用的flush方法。
  2. 該類不會緩沖換行符上的寫入,因為 io.TextIOWrapper 對象應該會在奇數點產生換行符。
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):
        temp_linebuf = self.linebuf + buf
        self.linebuf = ''
        for line in temp_linebuf.splitlines(True):
            # From the io.TextIOWrapper docs:
            #   On output, if newline is None, any '\n' characters written
            #   are translated to the system default line separator.
            # By default sys.stdout.write() expects '\n' newlines and then
            # translates them so this is still cross platform.
            if line[-1] == '\n':
                self.logger.log(self.log_level, line.rstrip())
            else:
                self.linebuf += line

    def flush(self):
        if self.linebuf != '':
            self.logger.log(self.log_level, self.linebuf.rstrip())
        self.linebuf = ''


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

這使您可以輕松地將所有輸出路由到您選擇的記錄器。 如果需要,您可以保存本線程中其他人提到的sys.stdout和/或sys.stderr ,然后再替換它(如果您以后需要恢復它)。

一個更簡單的選擇,

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

你真的應該用另一種方式來做:通過調整你的日志配置來使用print語句或其他東西,具體取決於設置。 不要覆蓋print行為,因為將來可能引入的某些設置(例如,由您或使用您的模塊的其他人)實際上可能會將其輸出到stdout並且您會遇到問題。

有一個處理程序應該將您的日志消息重定向到正確的流(文件、 stdout或任何其他類似文件的文件)。 它被稱為StreamHandler ,它與logging模塊捆綁在一起。

所以基本上我認為你應該做,你說你不想做的事情:用實際的日志記錄替換print語句。

一旦你定義了你的記錄器,即使有多個打印參數,也可以使用它來使打印重定向到記錄器。

print = lambda *tup : logger.info(str(" ".join([str(x) for x in tup]))) 

下面的 snipped 在我的 PySpark 代碼中完美地工作。 如果有人需要理解-->

import os
import sys
import logging
import logging.handlers

log = logging.getLogger(__name_)

handler = logging.FileHandler("spam.log")
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
log.addHandler(handler)
sys.stderr.write = log.error 
sys.stdout.write = log.info 

(將在同一目錄中的“spam.log”中記錄每個錯誤,控制台/標准輸出上不會有任何內容)

(將在同一目錄中的“spam.log”中記錄所有信息,控制台/標准輸出上不會有任何信息)

在兩個文件中以及在控制台中打印輸出錯誤/信息刪除以上兩行。

快樂編碼干杯!!!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM