2009-06-10 5 views
11

So jetzt haben wir viele Python-Skripte und wir versuchen, sie zu konsolidieren und zu beheben und Redundanzen. Eines der Dinge, die wir versuchen zu tun, ist sicherzustellen, dass alle sys.stdout/sys.stderr in das Python Logging Modul gehen.umleiten von sys.stdout zu python logging

Nun ist die Hauptsache ist, wollen wir die folgenden ausgedruckt:

[<ERROR LEVEL>] | <TIME> | <WHERE> | <MSG> 

Jetzt sind alle sys.stdout/sys.stderr msgs so ziemlich in allen der Python-Fehlermeldungen sind im Format [LEVEL ] - MSG, die alle mit sys.stdout/sys.stderr geschrieben wurden. Ich kann die Geldbuße analysieren, in meinem sys.stdout-Wrapper und im sys.stderr-Wrapper. Rufen Sie dann die entsprechende Protokollierungsstufe auf, abhängig von der geparsten Eingabe.

Also im Grunde haben wir ein Paket namens foo, und ein Unterpaket namens log. In __init__.py definieren wir folgendes:

def initLogging(default_level = logging.INFO, stdout_wrapper = None, \ 
       stderr_wrapper = None): 
    """ 
     Initialize the default logging sub system 
    """ 
    root_logger = logging.getLogger('') 
    strm_out = logging.StreamHandler(sys.__stdout__) 
    strm_out.setFormatter(logging.Formatter(DEFAULT_LOG_TIME_FORMAT, \ 
              DEFAULT_LOG_TIME_FORMAT)) 
    root_logger.setLevel(default_level) 
    root_logger.addHandler(strm_out) 

    console_logger = logging.getLogger(LOGGER_CONSOLE) 
    strm_out = logging.StreamHandler(sys.__stdout__) 
    #strm_out.setFormatter(logging.Formatter(DEFAULT_LOG_MSG_FORMAT, \ 
    #          DEFAULT_LOG_TIME_FORMAT)) 
    console_logger.setLevel(logging.INFO) 
    console_logger.addHandler(strm_out) 

    if stdout_wrapper: 
     sys.stdout = stdout_wrapper 
    if stderr_wrapper: 
     sys.stderr = stderr_wrapper 


def cleanMsg(msg, is_stderr = False): 
    logy = logging.getLogger('MSG') 
    msg = msg.rstrip('\n').lstrip('\n') 
    p_level = r'^(\s+)?\[(?P<LEVEL>\w+)\](\s+)?(?P<MSG>.*)$' 
    m = re.match(p_level, msg) 
    if m: 
     msg = m.group('MSG') 
     if m.group('LEVEL') in ('WARNING'): 
      logy.warning(msg) 
      return 
     elif m.group('LEVEL') in ('ERROR'): 
      logy.error(msg) 
      return 
    if is_stderr: 
     logy.error(msg) 
    else: 
     logy.info(msg) 

class StdOutWrapper: 
    """ 
     Call wrapper for stdout 
    """ 
    def write(self, s): 
     cleanMsg(s, False) 

class StdErrWrapper: 
    """ 
     Call wrapper for stderr 
    """ 
    def write(self, s): 
     cleanMsg(s, True) 

Jetzt würde nennen wir dies in einem unserer Skripte zum Beispiel:

import foo.log 
foo.log.initLogging(20, foo.log.StdOutWrapper(), foo.log.StdErrWrapper()) 
sys.stdout.write('[ERROR] Foobar blew') 

, die in einem Fehlerprotokoll Nachricht umgewandelt werden würde. Wie:

[ERROR] | 20090610 083215 | __init__.py | Foobar Blew 

Nun das Problem ist, wenn wir das tun, das Modul, in dem die Fehlermeldung protokolliert wurde, ist jetzt die __init__ (entsprechend foo.log.__init__.py-Datei), die den ganzen Zweck besiegt.

Ich habe versucht, eine deepCopy/shallowCopy der stderr/stdout Objekte, aber das tut nichts, es sagt immer noch das Modul die Nachricht in __init__.py aufgetreten ist. Wie kann ich es so machen, dass dies nicht passiert?

Antwort

6

Das Problem ist, dass das Logging-Modul eine einzelne Schicht auf der Call-Stack sucht, um zu finden, wer es angerufen hat, aber jetzt Ihre Funktion ist eine Zwischenschicht an diesem Punkt (Obwohl ich hätte erwartet, dass es melden cleanMsg, nicht __init__, da rufen Sie in log()) an. Stattdessen müssen Sie zwei Ebenen nach oben gehen oder den Anrufer an die protokollierte Nachricht weiterleiten. Sie können das tun, indem Sie den Stapelrahmen selbst untersuchen und die aufrufende Funktion ergreifen und sie in die Nachricht einfügen.

Ihre Berufung Rahmen zu finden, können Sie das Modul überprüfen verwenden:

import inspect 
f = inspect.currentframe(N) 

wird N Rahmen nachzuschlagen, und kehren Sie den Rahmen-Pointer. dh Ihr unmittelbarer Aufrufer ist currentframe (1), aber Sie müssen möglicherweise ein anderes Bild hochgehen, wenn dies die stdout.write-Methode ist. Sobald Sie den aufrufenden Rahmen haben, können Sie das ausführende Code-Objekt abrufen und den zugehörigen Datei- und Funktionsnamen betrachten.zB:

code = f.f_code 
caller = '%s:%s' % (code.co_filename, code.co_name) 

Sie müssen möglicherweise auch einige Code setzen Nicht-Python-Code ruft in Ihnen zu behandeln (dh C-Funktionen oder builtins.), da diese f_code Objekte fehlen kann.

Alternativ wird nach oben mikej's answer, können Sie den gleichen Ansatz in einer benutzerdefinierten Logger-Klasse verwenden, von logging.Logger vererben, die findCaller überschreibt bis mehrere Frames zu navigieren, anstatt ein.

2

Ich denke, das Problem ist, dass Ihre tatsächlichen Log-Meldungen nun durch die logy.error und logy.info Anrufe in cleanMsg geschaffen werden, damit diese Methode ist die Quelle der Log-Meldungen und Sie sehen dies als __init__.py

Wenn Sie In der Quelle von Pythons lib/logging/__init__.py schauen Sie eine Methode namens findCaller, die das Protokollierungsmodul verwendet, um den Aufrufer einer Protokollanforderung ableiten.
Vielleicht können Sie dies auf Ihrem Protokollierungsobjekt überschreiben, um das Verhalten anzupassen?

Verwandte Themen