[英]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") 一樣。 如果未啟用日志記錄,則應將“測試”打印到屏幕上。
這可能嗎? 我知道我可以以類似的方式將標准輸出定向到文件(請參閱: 將打印重定向到日志文件)
您有兩個選擇:
打開一個日志文件並用它替換 sys.stdout,而不是一個函數:
log = open("myprog.log", "a") sys.stdout = log >>> print("Hello") >>> # nothing is printed because it goes to the log file instead.
用您的日志功能替換打印:
# 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 許可在他的網站上的帖子中提供。 下面的代碼基於此,但解決了原始代碼的兩個問題:
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.