2013-10-17 9 views
17

Ich habe einen Logger, der eine RotatingFileHandler hat. Ich möchte alle Stdout und Stderr an den Logger umleiten. Wie geht das?Wie Umleitung stdout und stderr Logger in Python

+0

Haben Sie externe Module/Bibliotheken, die FDs 1 und 2 direkt schreiben? –

+0

@ IgnacioVazquez-Abrams Ich verstehe nicht wirklich, was du meintest, aber ich versuche es zu erklären. Ich benutze mehrere Python-Prozesse, und von allen möchte ich alle "stdout" und "stderr" Nachricht an meinen Logger umleiten. – orenma

+0

Mögliches Duplikat von [Wie kopiere ich sys.stdout in eine Protokolldatei in Python?] (Https://stackoverflow.com/questions/616645/how-do-i-duplicate-sys-stdout-to-a-log -file-in-python) – user

Antwort

8

Wenn es ein All-Python-System (dh keine C-Bibliotheken direkt an fds schreiben, als Ignacio Vazquez-Abrams gefragt), dann könnten Sie in der Lage sein, einen Ansatz zu verwenden, wie here vorgeschlagen:

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) 

und Setzen Sie dann sys.stdout und sys.stderr auf LoggerWriter Instanzen.

+0

danke, das hat den Job, aber aus irgendeinem Grund 'stderr' senden Sie es die Nachricht jedes Wort getrennt, weißt du warum? – orenma

+0

@orenma vermutlich weil Schreiben Wort für Wort heißt. Sie können meinen Beispielcode besser an Ihre Bedürfnisse anpassen. –

+0

Was passiert, wenn sys.stderr.flush() nach dem Umleiten von stderr aufgerufen wird? – Moberg

14

Nicht genug rep, um zu kommentieren, aber ich wollte die Version von dieser hinzufügen, die für mich arbeitete, falls andere in einer ähnlichen Situation sind.

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) 

und dies würde wie etwas aussehen:

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

Ich bekomme eine seltsame Ausgabe wegen der Flush-Methode: 'warning archan_pylint: 18: '. Es scheint, dass das stderr-Objekt gedruckt wird und nicht eine neue Zeile oder sonst. Daher habe ich einfach die Flush-Methode entfernt und es scheint jetzt zu funktionieren. – Pawamoy

1

Mit bündig hinzugefügt Vinay Sajip Antwort:

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

alle vorherigen Antworten scheinen Probleme Hinzufügen von zusätzlichen Zeilenumbrüche zu haben, wo sie werden nicht benötigt. Die Lösung, die für mich am besten funktioniert, ist von http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/, wo er zeigt, wie Fehler- und Standardausgaben an den Logger senden:

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

Die Ausgabe sieht so aus:

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 

Beachten Sie, dass self.linebuf = ' 'wird der Flush behandelt, anstatt eine Flush-Funktion zu implementieren.

+1

Dieser Code ist lizenziert [GPL] (https://www.electricmonk.nl/log/posting-license/). Ich bin nicht sicher, ob es sogar auf SO gepostet werden kann, was Kompatibilität mit [CC by-sa] erfordert (https://meta.stackexchange.com/help/licensing). – asmeurer

2

können Sie redirect_stdout Kontext-Manager verwenden:

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

oder ähnliche

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') 
Verwandte Themen