2011-05-17 22 views
10

Próbuję użyć logowania Pythona z pliku konfiguracyjnego i własny moduł obsługi. Działa to w pewnym stopniu. To, co mnie naprawdę łamie, to __init__, które jest wywoływane dwa razy, a __del__ jest wywoływane raz. Po usunięciu całego pliku konfiguracyjnego i utworzeniu modułu obsługi bezpośrednio w kodzie __init__ jest wywoływana raz, a __del__ nigdy nie jest wywoływana.Logowanie w języku Python: Dlaczego __init__ wywoływane jest dwa razy?

Moje pytania:

  1. Dlaczego __init__ wywołana dwukrotnie?
  2. Dlaczego nazwa __del__ jest wywoływana rzadziej niż __init__?

Kod:

#!/bin/env python 

import logging 
import logging.handlers 
import logging.config 

class Test1TimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler): 
    def __init__(self,filename): 
     print "init called" 
     logging.handlers.TimedRotatingFileHandler.__init__(self,filename, when='S', interval=86400, backupCount=8, encoding=None) 

    def __del__(self): 
     print "del called" 
     if hasattr(logging.handlers.TimedRotatingFileHandler,"__del__"): 
      logging.handlers.TimedRotatingFileHandler.__del__(self) 

logging.config.fileConfig('/root/test1.conf') 
logger = logging.getLogger("test1") 

Plik konfiguracyjny:

[formatters] 
keys: simple 

[handlers] 
keys: file 

[loggers] 
keys: root 

[formatter_simple] 
format: "%(message)s" 

[handler_file] 
class: test1.Test1TimedRotatingFileHandler 
args: ("/root/test1.log",) 
level=INFO 

[logger_root] 
level: INFO 
handlers: file 
qualname: test1 

wyjściowe wygląda następująco:

init called 
init called 
del called 

Korzystanie debuggera aby uzyskać ślad stosu jak sugeruje Sentinal ujawnia to:

pierwsze wezwanie:

> /root/test1.py(12)__init__() 
-> print "init called" 
(Pdb) where 
    /root/test1.py(21)<module>() 
-> logging.config.fileConfig('/root/test1.conf') 
    /usr/local/python/2.6.4/lib/python2.6/logging/config.py(84)fileConfig() 
-> handlers = _install_handlers(cp, formatters) 
    /usr/local/python/2.6.4/lib/python2.6/logging/config.py(156)_install_handlers() 
-> klass = _resolve(klass) 
    /usr/local/python/2.6.4/lib/python2.6/logging/config.py(94)_resolve() 
-> found = __import__(used) 
    /root/test1.py(21)<module>() 
-> logging.config.fileConfig('/root/test1.conf') 
    /usr/local/python/2.6.4/lib/python2.6/logging/config.py(84)fileConfig() 
-> handlers = _install_handlers(cp, formatters) 
    /usr/local/python/2.6.4/lib/python2.6/logging/config.py(159)_install_handlers() 
-> h = klass(*args) 
> /root/test1.py(12)__init__() 
-> print "init called" 
(Pdb) c 
init called 

drugie wezwanie:

> /root/test1.py(12)__init__() 
-> print "init called" 
(Pdb) w 
    /root/test1.py(21)<module>() 
-> logging.config.fileConfig('/root/test1.conf') 
    /usr/local/python/2.6.4/lib/python2.6/logging/config.py(84)fileConfig() 
-> handlers = _install_handlers(cp, formatters) 
    /usr/local/python/2.6.4/lib/python2.6/logging/config.py(159)_install_handlers() 
-> h = klass(*args) 
> /root/test1.py(12)__init__() 
-> print "init called" 
+1

"" "Dlaczego init jest wywoływany dwa razy?" "" - weź debugger i sprawdź, dlaczego jest wywoływany dwa razy. Przeglądanie stosu wywołań może być pomocne ... –

Odpowiedz

11
  1. Dlaczego init, wywołana dwukrotnie?

Jeśli zastosujemy kod modułu logging, zobaczysz, że kiedy ładuje plik konfiguracyjny logowania, to wystąpienie wszystkie ładowarki (pierwszy instancji).

W twoim kodzie deklarujesz swojego handler'a jak test1.Test1TimedRotatingFileHandler, więc kiedy spróbujesz zaimportować swój handler, analizuje on kod w module test1 ... więc odtwarza handler'a !!

Poprawiono kod będzie strzec używając __name__ == '__main__':

#!/bin/env python 

import logging 
import logging.handlers 
import logging.config 

class Test1TimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler): 
    def __init__(self,filename): 
     print "init called" 
     logging.handlers.TimedRotatingFileHandler.__init__(self,filename, when='S', interval=86400, backupCount=8, encoding=None) 

    def __del__(self): 
     print "del called" 
     if hasattr(logging.handlers.TimedRotatingFileHandler,"__del__"): 
      logging.handlers.TimedRotatingFileHandler.__del__(self) 

if __name__ == "__main__": 
    logging.config.fileConfig('./test1.conf') 
    logger = logging.getLogger("test1") 

2. Dlaczego del jest wywoływany rzadziej niż init?

Ogólnie, operator __del__ nazywa po-python-chce, bardziej dokładnie, kiedy to się nazywa garbage collector zdecyduje się śmieci, zbierać obiektu; nie jest to konieczne zaraz po tym, jak ją zwolnisz.

+0

A destruktor nie jest koniecznie wywoływany, gdy Python się kończy. – jtniehof

+1

@Cedric: Szczególnie w przypadku obsługi logów rotacyjnych jest to trochę nieproduktywne, gdy konstruktor jest wywoływany dwa razy. Planowałem zrobić doRollover() w konstruktorze mojego tresera. Gdy init jest wywoływany dwa razy, powoduje to dwa najazdy ... więc co zrobić? Czy to błąd, funkcja czy co? – uli42

+0

Naprawdę nie jest to błąd, python utrzymuje tylko 1 handler, a drugi wydaje się być "zagubiony" ... –

5

Brakuje ochrony if __name__ == "__main__": wokół kodu konfiguracji logowania. Jest wykonywany po raz drugi, kiedy logging importuje twój moduł test1, aby znaleźć odwołanie do klasy.

Alternatywnie, użyj nazwy __main__.Test1TimedRotatingFileHandler w pliku konfiguracyjnym, lub umieść kod konfiguracji i klasę obsługi w różnych plikach.

+0

Świetnie! To jest to! Dziękuję Ci bardzo! – uli42