この記事はTakumi Akashiro ひとり Advent Calendar 2020の17日目の記事です。
未だにloggingの綺麗な使い方が分からねェ……って思いながら書いた記事です。
ご了承ください。
始めに
普通のPythonを書いているとまず、reload
関数なんて使わないですよね。
だが、DCCツールの開発を行っているTAやデザイナーならreload()
を目にしたことはあるとおもいます。
(今回の記事はPython3で書くので、3系で非推奨になったreload()
に代わって、importlib.reload()
を使います。)
でも安易なreload()
を使うとロガーがぶっ壊れます。
#! python3
import logging
logger= logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
sh = logging.StreamHandler()
sh.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
logger.addHandler(sh)
def main():
pass
logger.debug("test")
if __name__ == '__main__':
main()
問題ないですね。
そしてインタプリタでモジュールをimport
してmain()
を実行してみましょう。
これをreload()
してからmain()
してみましょう。
はい、1行多く書き出されましたね。
これはreload()
でモジュールが再評価されるときに、再度log.addHandler(sh)
が評価されたからです。
これを避ける為に、logger用のモジュールを分けてみます。
#! python3
import logging
def __gen_logger():
# これだとlog階層に集まってしまうので間違いですが、、
# 時間がないので見逃してください!
logger_ = logging.getLogger(__name__)
logger_.setLevel(logging.DEBUG)
sh = logging.StreamHandler()
sh.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
logger_.addHandler(sh)
return logger_
logger = __gen_logger()
def get_logger():
return logger
#! python3
import log
def main():
logger = log.get_logger()
logger.debug("test")
if __name__ == '__main__':
main()
これで重複しなくなりましたね!
モジュールを分割しない方法
……でも毎回、log.pyみたいなロガー用サブモジュールを作るのもな……と思っていましたが、
最近になって良い方法を見つけました。
#! python3
import logging
def get_logger():
logger_ = logging.getLogger(__name__)
if logger_.hasHandlers() is False:
logger_.setLevel(logging.DEBUG)
sh = logging.StreamHandler()
sh.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
logger_.addHandler(sh)
return logger_
def main():
logger = get_logger()
logger.debug("test")
if __name__ == '__main__':
main()
締め
安易なreload()してるスクリプト絶滅しろ