1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Takumi Akashiro ひとりAdvent Calendar 2020

Day 17

reload()を使ってもloggingのloggerオブジェクトにHanlderを多重登録させない

Posted at

この記事は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()

普通に使うと、
image.png

問題ないですね。

そしてインタプリタでモジュールをimportしてmain()を実行してみましょう。
image.png

これをreload()してからmain()してみましょう。

image.png

はい、1行多く書き出されましたね。
これはreload()でモジュールが再評価されるときに、再度log.addHandler(sh)が評価されたからです。

これを避ける為に、logger用のモジュールを分けてみます。

log.py
#! 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

logger_sample.py
#! python3
import log

def main():
    logger = log.get_logger()
    logger.debug("test")

if __name__ == '__main__':
    main()

image.png

これで重複しなくなりましたね!

モジュールを分割しない方法

……でも毎回、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()してるスクリプト絶滅しろ

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?