2013-04-08 10 views
15

TaskPython: personalizzato di registrazione in tutti i moduli

Ho una collezione di script e mi piacerebbe loro di produrre messaggi di log unificati con alterazioni minime a moduli che fanno registrare i messaggi attuali.

Ho scritto un piccolo modulo 'custom_logger' che ho intenzione di chiamare dall'applicazione principale una volta, restituire un logger, che quindi continuerei a utilizzare.

I sottomoduli sarei importando in app devono solo (o meglio li augurare)

  • dovrebbe solo "la registrazione di importazione come log" - in modo che nulla di specifico per il mio sito è tenuto a li eseguono solo se qualcun altro li trova utili.
  • dovrebbe solo registrare i messaggi con log.info/error('message ') senza aggiungere nulla di specifico al sito
  • deve utilizzare il logger "root" già configurato con tutte le sue formattazioni e handler e non influisce sul logger del root configurazione

* * custom_logger.py

import logging 
import logging.handlers 
import os 
import sys 


def getLogger(name='root', loglevel='INFO'): 
    logger = logging.getLogger(name) 

    # if logger 'name' already exists, return it to avoid logging duplicate 
    # messages by attaching multiple handlers of the same type 
    if logger.handlers: 
    return logger 
    # if logger 'name' does not already exist, create it and attach handlers 
    else: 
    # set logLevel to loglevel or to INFO if requested level is incorrect 
    loglevel = getattr(logging, loglevel.upper(), logging.INFO) 
    logger.setLevel(loglevel) 
    fmt = '%(asctime)s %(filename)-18s %(levelname)-8s: %(message)s' 
    fmt_date = '%Y-%m-%dT%T%Z' 
    formatter = logging.Formatter(fmt, fmt_date) 
    handler = logging.StreamHandler() 
    handler.setFormatter(formatter) 
    logger.addHandler(handler) 

    if logger.name == 'root': 
     logger.warning('Running: %s %s', 
        os.path.basename(sys.argv[0]), 
        ' '.join(sys.argv[1:])) 
    return logger 

Poi viene il modulo che ha un paio di messaggi di prova con esempi di cosa funziona e cosa no.

submodule.py

import sys 
import custom_logger 
import logging 


class SubClass(object): 

    def __init__(self): 
    # NOK (no idea why since by default (no name parameter), it should return the root logger) 
    #log = logging.getLogger() 
    #log.info('message from SubClass/__init__') 

    # OK (works as expected) 
    #log = logging.getLogger('root') 
    #log.info('message from SubClass/__init__') 

    # OK (works as expected) 
    log = custom_logger.getLogger('root') 
    log.info('message from SubClass/__init__') 


    def SomeMethod(self): 
    # OK but I'd have to define `log` for every method, which is unacceptable 
    # Please see question below all code snippets 
    log = custom_logger.getLogger('root') 
    log.info('message from SubClass/SomeMethod') 

E l'applicazione principale: app.py Niente di speciale qui:

#!/usr/bin/python 

import custom_logger 
import submodule 

log = custom_logger.getLogger('root', loglevel='DEBUG') 

log.debug('debug message') 
log.info('info message') 
log.warning('warning message') 
log.error('error message') 

a = submodule.SubClass() # this should produce a log message 
a.SomeMethod()   # so should this 

uscita che sto cercando e che sto ricevendo, solo in un modo terribilmente brutto:

% ./app.py 
2013-04-08T03:07:46BST custom_logger.py WARNING : Running: app.py 
2013-04-08T03:07:46BST app.py    DEBUG : debug message 
2013-04-08T03:07:46BST app.py    INFO : info message 
2013-04-08T03:07:46BST app.py    WARNING : warning message 
2013-04-08T03:07:46BST app.py    ERROR : error message 
2013-04-08T03:07:46BST submodule.py  INFO : message from SubClass/__init__ 
2013-04-08T03:07:46BST submodule.py  INFO : message from SubClass/SomeMethod 

Desidero essere in grado di definire un logger in app.py e quindi nei sottomoduli utilizzare solo la libreria di registrazione Python standard per utilizzare un logger già configurato nell'app.py.

Inoltre, una brutta soluzione: se metto il codice qui sotto dopo le importazioni in submodule.py:

log = custom_logger.getLogger('root') 

verrà eseguito prima del mio registratore è configurato in app.py, di fatto rendendo il modulo, non è la mia app a configurare la registrazione.

Un'altra soluzione ho considerato: all'interno del constuctor della classe SubClass, potrei definire

self.log = custom_logger.getLogger('root')

e poi usare self.log.error ('un errore'). Ci deve essere un modo più carino - se puoi suggerire qualcosa di utile o indicare dove ho frainteso la documentazione, sarei molto grato!

PS. Ho passato un bel po 'di tempo a leggere il log di Python howto (base e avanzato) e il ricettario, quindi se mi sono perso qualcosa di utile lì, per favore segnalalo.

Grazie!

risposta

3

Se si desidera modificare il root logger, è possibile utilizzare semplicemente getLogger() ovunque, senza argomenti.

Per quanto riguarda la configurazione dell'istanza solo nel modulo principale, è possibile creare un'istanza del registratore, aggiungere il proprio gestore e utilizzarlo in tutti gli altri sottomoduli (come ho fatto in seguito).

ho creato una classe che eredita lo StreamHandler in custom_logger.py:

class MyHandler(logging.StreamHandler): 

    def __init__(self): 
     logging.StreamHandler.__init__(self) 
     fmt = '%(asctime)s %(filename)-18s %(levelname)-8s: %(message)s' 
     fmt_date = '%Y-%m-%dT%T%Z' 
     formatter = logging.Formatter(fmt, fmt_date) 
     self.setFormatter(formatter) 

Poi, nel submodule.py, ho messo il getLogger dopo le importazioni e commentati nelle metodi:

import sys 
import logging 

log = logging.getLogger('root') 

class SubClass(object): 

    def __init__(self): 
     log.info('message from SubClass/__init__') 

    def SomeMethod(self): 
     log.info('message from SubClass/SomeMethod') 

Poi, in app.py ho creato un'istanza di Logger (che sarà la stessa in tutti i moduli) e ho aggiunto il mio gestore, che formatta l'output:

#!/usr/bin/python 

import logging 
import custom_logger 
import submodule 

log = logging.getLogger('root') 
log.setLevel('DEBUG') 
log.addHandler(custom_logger.MyHandler()) 

log.debug('debug message') 
log.info('info message') 
log.warning('warning message') 
log.error('error message') 

a = submodule.SubClass() # this should produce a log message 
a.SomeMethod()   # so should this 

uscita:

./app.py 
2013-04-08T15:20:05EEST app.py    DEBUG : debug message 
2013-04-08T15:20:05EEST app.py    INFO : info message 
2013-04-08T15:20:05EEST app.py    WARNING : warning message 
2013-04-08T15:20:05EEST app.py    ERROR : error message 
2013-04-08T15:20:05EEST submodule.py  INFO : message from SubClass/__init__ 
2013-04-08T15:20:05EEST submodule.py  INFO : message from SubClass/SomeMethod 
+0

Grazie Mihai. Pensavo di avere più tempo per esaminarlo, ma dovrò aspettare il weekend. Finora sono stato in grado di far funzionare la mia classe che fa tutto (allegando formattatori, gestori e impostazione di loglevel) definendo il log come oggetto getLogger nel submodule.py. Speravo che ciò potesse essere fatto senza mettere le cose al di fuori della definizione della classe. La mia domanda era più di "perché alcuni approcci che ho fatto non funzionavano come previsto, come mi piacerebbe capire il motivo dietro di esso, al contrario di ottenere un codice consegnato a me, ma apprezzo il tuo aiuto: –

+0

Ho aggiornato la mia risposta.Altre quindi che è possibile esaminare il codice del modulo.Possibile vedere che il logger root è istanziato durante l'importazione.Non capisco davvero perché getLogger ("root") non funzioni lo stesso in tutte le situazioni (puoi fare getLogger() in app.py e getLogger ("root") in submodule.py e ottieni ciò che ti serve, ma non viceversa). – Mihai

+0

Come farlo funzionare con il reindirizzamento stdout al file es. python ./app.py >> file.log? –

Problemi correlati