ロギングの環境設定 という公式ドキュメントにも警告があったのですが、思いっきりハマってしまったので記事にしておきます。
Python の logging モジュールは、 addHandler() などのメソッドを呼び出して構成する以外に、 ini 形式の設定ファイルを元に構成する logging.config.fileConfig()
や、 dict 型の設定情報を元に構成する logging.config.dictConfig()
を使って構成することができます。
import logging.config
logging.config.dictConfig({
'version': 1,
'handlers': {
'default': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'stream': 'ext://sys.stderr',
}
},
'root': {
'level': 'DEBUG',
'handlers': ['default'],
}
})
logger = logging.getLogger('example')
logger.info('Hello') # 出力される
Python の logging を使うときは、 logger = logging.getLogger(__name__)
をモジュールの先頭に書いておくのがイディオムになっています。
しかし、先ほどの logging のセットアップではそういったコードを壊してしまいます。
import logging.config
logger = logging.getLogger('example')
logging.config.dictConfig({
'version': 1,
'handlers': {
'default': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'stream': 'ext://sys.stderr',
}
},
'root': {
'level': 'DEBUG',
'handlers': ['default'],
}
})
logger.info('Hello') # 出力されない
これは、 fileConfig や dictConfig がもともと logging 全体の構成をすることを目的としていて、デフォルトですでに生成された logger を無効化してしまうからです。
この動作をカスタマイズするためのオプションが disable_existing_loggers で True がデフォルトの挙動、 False が無効化しないという意味になります。
このオプションは fileConfig の場合はキーワード引数として、 dictConfig の場合は渡す dict の要素として指定することができます。
import logging.config
logger = logging.getLogger('example')
logging.config.dictConfig({
'version': 1,
'handlers': {
'default': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'stream': 'ext://sys.stderr',
}
},
'root': {
'level': 'DEBUG',
'handlers': ['default'],
},
'disable_existing_loggers': False,
})
logger.info('Hello') # 出力される
dictConfig はさらに incremental というオプションもあり、既存の logger についている handler や filter などを削除せずに追加だけを行います。