2011-05-17 16 views
10

私は設定ファイルと独自のハンドラでPythonロギングを使用しようとしています。これはある程度は機能します。本当に私を困惑させるのは、__init__が2回呼び出され、__del__が1回呼び出されることです。 configファイル全体を削除してコード内でハンドラを直接作成すると、__init__が一度呼び出され、__del__が呼び出されることはありません。Pythonロギング:__init__が2回呼び出されるのはなぜですか?

私の質問は:

  1. なぜ__init__が2回呼び出されますか?
  2. なぜ__del____init__よりも頻繁に呼び出されますか?

コード:

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

設定ファイル:

init called 
init called 
del called 

により示唆されるようにスタックトレースを取得するには、デバッガを使用した:

[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 

出力は次のようになりますセンチナルはこれを明らかにする:

最初の呼び出し:

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

第二コール:

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

"" "なぜinitは2回呼び出されましたか?" " - デバッガを取り出し、2回呼び出された理由を確認します。コールスタックを見ると役に立つかもしれません... –

答えて

11
  1. なぜ二度呼ばれているinit?

あなたがloggingモジュールのコードに従っている場合、あなたは、ロギングコンフィギュレーションファイルをロードしているとき、それはすべてのハンドラ(第一のインスタンス)をインスタンス化していることがわかります。

test1.Test1TimedRotatingFileHandlerのようにハンドラを宣言すると、ハンドラをインポートしようとすると、test1モジュールのコードが解析されます。ハンドラが再作成されます。

修正コードが__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。なぜdelはinitよりも頻繁に呼ばれますか?一般に

は、__del__オペレータは、より正確に、それは、ガベージコレクタがに決定したときに呼び出されるオブジェクトをガベージコレクトたい場合、パイソンと呼ばれます。これは必ずしもあなたがそれをリリースした直後ではありません。

+0

そして、Pythonが終了するときにデストラクタが必ずしも呼び出されるとは限りません。 – jtniehof

+1

@Cedric:特に、回転ログファイルを扱うときは、コンストラクタが2回呼び出されたときにカウンタ生産性があります。私はハンドラのコンストラクタでdoRollover()を実行する予定でした。 initが2回呼び出されると、2回のロールオーバーが発生します...どうすればいいですか?これはバグですか、機能ですか、何ですか? – uli42

+0

実際にはバグではなく、Pythonは1ハンドラだけを保持し、もう1つは "失われている"ようです... –

5

ログ設定コードの周囲にif __name__ == "__main__":ガードがありません。 loggingtest1モジュールをインポートしてクラス参照を見つけると、2回目の実行が行われます。

また、コンフィグレーションファイルに__main__.Test1TimedRotatingFileHandlerという名前を使用するか、コンフィグレーションコードとハンドラクラスを別のファイルに入れます。

+0

素晴らしい!それでおしまい!どうもありがとうございました! – uli42

関連する問題