2013-04-08 16 views
15

ZadaniePython: zwyczaj rejestrowania we wszystkich modułach

Mam zbiór skryptów i chciałbym im produkować jednolite logu z minimalnymi zmianami modułów robi zalogowaniu aktualne wiadomości.

Napisałem mały moduł "custom_logger", który mam zamiar wywołać z głównej aplikacji, gdy zwrócę logger, który będę dalej używał.

Submoduły będę importowania do aplikacji należy tylko (albo raczej życzę im)

  • powinien jedynie „rejestrowanie import jako log” - tak, że wymagana jest nic konkretnego do mojej strony, aby one po prostu działają, gdy ktoś inny uzna je za przydatne.
  • powinien po prostu rejestrować wiadomości za pomocą log.info/error('message ") bez dodawania niczego związanego z konkretną witryną.
  • powinien używać już skonfigurowanego programu rejestrującego" root "z całym formatowaniem i handlerami i nie wpływać na program rejestrujący root konfiguracja

* 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 

Potem przychodzi submodule który ma kilka wiadomości testowych z przykładów tego, co działa, a co nie.

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

A głównym app: app.py Nic specjalnego tutaj:

#!/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 

wyjściowa, że ​​jestem po i że dostaję, po prostu w wyjątkowo brzydki sposób:

% ./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 

Chcę móc zdefiniować program rejestrujący w aplikacji app.py, a następnie w submodułach używać tylko standardowej biblioteki rejestrowania w języku Python, aby użyć już skonfigurowanego rejestratora w pliku app.py.

Również brzydki obejście: gdybym umieścić poniższy kod po importu w submodule.py:

log = custom_logger.getLogger('root') 

zostanie wykonany przed moja rejestrator jest skonfigurowany w app.py, skutecznie czyniąc submodule, nie moja rejestracja konfiguracji aplikacji.

Innym obejście Uważałem: w constuctor klasy podklasy, mógłbym określić

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

a następnie użyć self.log.error ('jakiś błąd'). Musi istnieć milsza droga - jeśli możesz zasugerować coś pożytecznego lub wskazać, gdzie źle zrozumiałem dokumentację, byłbym bardzo wdzięczny!

PS. Sporo czasu spędziłem czytając podręcznik logowania do Pythona (podstawowy i zaawansowany) oraz książkę kucharską, więc jeśli przegapiłem coś pożytecznego, proszę wskazać.

Dziękujemy!

Odpowiedz

3

Jeśli chcesz zmienić program rejestrujący root, możesz użyć wszędzie getLogger(), bez żadnych argumentów.

Odnośnie konfiguracji instancji tylko w module głównym, można utworzyć instancję rejestratora, dodać własny moduł obsługi i użyć go we wszystkich pozostałych modułach (tak jak to zrobiłem poniżej).

Stworzyłem klasę, która dziedziczy StreamHandler w 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) 

Następnie w submodule.py, kładę getLogger po imporcie i komentuje je w metodach:

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

Następnie w app.py utworzyłem instancję Loggera (która będzie taka sama we wszystkich modułach) i dodano mój program obsługi, który formatuje dane wyjściowe:

#!/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 

wyjściowa:

./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

Dziękuję Mihai. Myślałem, że będę miał więcej czasu, żeby się temu przyjrzeć, ale będę musiał poczekać na weekend. Do tej pory udało mi się uczynić moją klasę, która robi to wszystko (dołączanie formaterów, procedur obsługi i ustawiania języka), definiując log jako obiekt getLogger w submodule.py. Miałem nadzieję, że da się to zrobić bez umieszczania rzeczy poza definicją klasy. Moje pytanie brzmiało bardziej "dlaczego niektóre podejścia, które podjąłem, nie zadziałały zgodnie z oczekiwaniami, ponieważ chciałbym zrozumieć przyczynę tego, w przeciwieństwie do otrzymania przekazanego mi kodu, ale doceniam twoją pomoc:) –

+0

Zaktualizowałem moją odpowiedź, poza tym, że możesz zajrzeć do kodu modułu, możesz zobaczyć, że główny dziennik jest tworzony podczas importowania, ja też tak naprawdę nie rozumiem, dlaczego getLogger ("root") nie działa tak samo we wszystkich sytuacjach (możesz zrobić getLogger() w app.py i getLogger ("root") w submodule.py i otrzymasz to, czego potrzebujesz, ale nie na odwrót) – Mihai

+0

Jak zrobić to z przekierowaniem standardowym do pliku np. python ./app.py >> file.log? –

Powiązane problemy