2009-06-10 16 views
11

Quindi adesso abbiamo un sacco di script Python e stiamo cercando di consolidarli e correggere e ridondanze. Una delle cose che stiamo cercando di fare è assicurarsi che tutto sys.stdout/sys.stderr entri nel modulo di registrazione di Python.reindirizzamento di sys.stdout alla registrazione python

Ora la cosa principale è, vogliamo che la segue stampato:

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

Ora tutti sys.stdout/msg sys.stderr praticamente in tutti i messaggi di errore di pitone sono nel formato di [LIVELLO ] - MSG, che sono tutti scritti usando sys.stdout/sys.stderr. Posso analizzare la multa, nel mio wrapper sys.stdout e nel wrapper sys.stderr. Quindi chiama il livello di registrazione corrispondente, a seconda dell'ingresso analizzato.

Quindi in pratica abbiamo un pacchetto chiamato foo e un sotto pacchetto chiamato log. In __init__.py definiamo la seguente:

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) 

oggi chiameremmo questo in uno dei nostri script, ad esempio:

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

che sarebbe convertito in un messaggio di log degli errori. Come:

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

Ora il problema è quando lo facciamo, il modulo in cui è stato registrato il messaggio di errore è ora il __init__ (corrispondente a foo.log.__init__.py file) che sconfigge l'intero scopo.

Ho provato a fare un deepCopy/shallowCopy degli oggetti stderr/stdout, ma questo non fa nulla, dice ancora il modulo il messaggio si è verificato in __init__.py. Come posso farlo in modo che questo non accada?

risposta

6

Il problema è che il modulo di registrazione cerca un singolo livello nello stack di chiamate per trovare chi lo ha chiamato, ma ora la tua funzione è un livello intermedio in quel punto (anche se mi sarei aspettato che segnalasse cleanMsg, non __init__, poiché è lì che si chiama log()). Invece, ne hai bisogno per salire di due livelli, oppure passare chi è il tuo interlocutore nel messaggio registrato. Puoi farlo controllando il telaio dello stack da solo e afferrando la funzione di chiamata, inserendola nel messaggio.

Per trovare la cornice di chiamata, è possibile utilizzare il modulo di ispezionare:

import inspect 
f = inspect.currentframe(N) 

cercherà N fotogrammi, e vi fa ritornare il puntatore. Ad esempio il tuo chiamante immediato è currentframe (1), ma potresti dover fare un altro frame se questo è il metodo stdout.write. Una volta ottenuto il frame di chiamata, è possibile ottenere l'oggetto codice in esecuzione e osservare il nome del file e della funzione ad esso associato.ad esempio:

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

Si può anche bisogno di mettere un po 'di codice per gestire codici a mettere in voi (cioè le funzioni C o comandi incorporati.) non python, in quanto questi possono mancare oggetti f_code.

In alternativa, seguendo lo mikej's answer, è possibile utilizzare lo stesso approccio in una classe di Logger personalizzata che eredita dalla registrazione. Registratore che sostituisce findCaller per spostarsi su diversi frame, anziché uno.

2

Credo che il problema è che i messaggi di log reali vengono ora create dai logy.error e logy.info chiamate in cleanMsg, quindi che il metodo è la fonte dei messaggi di log e si stanno vedendo questo come __init__.py

Se guardate nella fonte di Python lib/logging/__init__.py vedrete un metodo definito chiamato findCaller che è ciò che il modulo di registrazione utilizza per derivare il chiamante di una richiesta di registrazione.
Forse è possibile sovrascriverlo sull'oggetto di registrazione per personalizzare il comportamento?

Problemi correlati