2013-10-17 21 views
17

Tôi có một logger có RotatingFileHandler. Tôi muốn chuyển hướng tất cả StdoutStderr tới trình ghi nhật ký. Làm thế nào để làm như vậy?Cách chuyển hướng stdout và stderr sang logger bằng Python

+0

Bạn có module bên ngoài/thư viện mà viết thư cho FD 1 và 2 trực tiếp? –

+0

@ IgnacioVazquez-Abrams Tôi thực sự không hiểu ý bạn là gì nhưng tôi sẽ cố gắng giải thích. Tôi đang sử dụng một số quy trình python, và từ tất cả chúng tôi muốn chuyển hướng tất cả các thông báo 'stdout' và' stderr' tới logger của tôi. – orenma

+0

Có thể trùng lặp [Làm cách nào để sao chép sys.stdout vào tệp nhật ký trong python?] (Https://stackoverflow.com/questions/616645/how-do-i-duplicate-sys-stdout-to-a-log -file-in-python) – user

Trả lời

8

Nếu nó là một hệ thống hoàn toàn Python (tức là không có thư viện C bằng văn bản cho FDS trực tiếp, khi Ignacio Vazquez-Abrams được hỏi về) sau đó bạn có thể sử dụng một cách tiếp cận như đề xuất here:

class LoggerWriter: 
    def __init__(self, logger, level): 
     self.logger = logger 
     self.level = level 

    def write(self, message): 
     if message != '\n': 
      self.logger.log(self.level, message) 

và sau đó đặt sys.stdoutsys.stderr thành LoggerWriter trường hợp.

+0

cảm ơn bạn, đã làm công việc, nhưng vì lý do nào đó 'stderr' gửi thông điệp của nó mỗi từ một cách riêng biệt, bạn có biết tại sao không? – orenma

+0

@orenma có lẽ vì viết được gọi là từng từ. Bạn có thể điều chỉnh mã ví dụ của tôi để phù hợp với nhu cầu của bạn chặt chẽ hơn. –

+0

Nếu sys.stderr.flush() được gọi sau khi chuyển hướng stderr thì sao? – Moberg

14

Không đủ đại diện để nhận xét, nhưng tôi muốn thêm phiên bản này làm việc cho tôi trong trường hợp những người khác đang ở trong tình huống tương tự.

class LoggerWriter: 
    def __init__(self, level): 
     # self.level is really like using log.debug(message) 
     # at least in my case 
     self.level = level 

    def write(self, message): 
     # if statement reduces the amount of newlines that are 
     # printed to the logger 
     if message != '\n': 
      self.level(message) 

    def flush(self): 
     # create a flush method so things can be flushed when 
     # the system wants to. Not sure if simply 'printing' 
     # sys.stderr is the correct way to do it, but it seemed 
     # to work properly for me. 
     self.level(sys.stderr) 

và điều này sẽ giống như thế:

log = logging.getLogger('foobar') 
sys.stdout = LoggerWriter(log.debug) 
sys.stderr = LoggerWriter(log.warning) 
+0

Tôi nhận được một đầu ra lạ vì phương pháp tuôn ra: 'warning archan_pylint: 18: '. Có vẻ như đối tượng stderr được in chứ không phải là một dòng mới hoặc khác, vì vậy tôi chỉ cần loại bỏ phương pháp tuôn ra và nó có vẻ làm việc ngay bây giờ. – Pawamoy

1

Với tuôn thêm vào Vinay Sajip của câu trả lời:

class LoggerWriter: 
    def __init__(self, logger, level): 
     self.logger = logger 
     self.level = level 

    def write(self, message): 
     if message != '\n': 
      self.logger.log(self.level, message) 

    def flush(self): 
     pass 
5

Tất cả các câu trả lời trước dường như có vấn đề thêm dòng mới thêm nơi họ không cần thiết. Các giải pháp phù hợp nhất đối với tôi là từ http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/, nơi ông trình bày cách gửi cả stdout và stderr đến logger:

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 

print "Test to standard out" 
raise Exception('Test to standard error') 

Kết quả trông giống như:

2011-08-14 14:46:20,573:INFO:STDOUT:Test to standard out 
2011-08-14 14:46:20,573:ERROR:STDERR:Traceback (most recent call last): 
2011-08-14 14:46:20,574:ERROR:STDERR: File "redirect.py", line 33, in 
2011-08-14 14:46:20,574:ERROR:STDERR:raise Exception('Test to standard error') 
2011-08-14 14:46:20,574:ERROR:STDERR:Exception 
2011-08-14 14:46:20,574:ERROR:STDERR:: 
2011-08-14 14:46:20,574:ERROR:STDERR:Test to standard error 

Lưu ý rằng self.linebuf = ' 'là nơi tuôn ra đang được xử lý, thay vì thực hiện một chức năng tuôn ra.

+1

Mã này được cấp phép [GPL] (https://www.electricmonk.nl/log/posting-license/). Tôi không chắc liệu nó có thể được đăng trên SO hay không, yêu cầu khả năng tương thích với [CC by-sa] (https://meta.stackexchange.com/help/licensing). – asmeurer

2

Bạn có thể sử dụng redirect_stdout quản lý bối cảnh:

import logging 
from contextlib import redirect_stdout 

logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) 
logging.write = lambda msg: logging.info(msg) if msg != '\n' else None 

with redirect_stdout(logging): 
    print('Test') 

hay như thế này

import logging 
from contextlib import redirect_stdout 


logger = logging.getLogger('Meow') 
logger.setLevel(logging.INFO) 
formatter = logging.Formatter(
    fmt='[{name}] {asctime} {levelname}: {message}', 
    datefmt='%m/%d/%Y %H:%M:%S', 
    style='{' 
) 
ch = logging.StreamHandler() 
ch.setLevel(logging.INFO) 
ch.setFormatter(formatter) 
logger.addHandler(ch) 

logger.write = lambda msg: logger.info(msg) if msg != '\n' else None 

with redirect_stdout(logger): 
    print('Test') 
Các vấn đề liên quan