pythonのloggingを使うときのメモ
背景
loggerインスタンスにStreamHandlerやFileHandlerを設定する処理は使いまわしたいので、関数にしたい。
その際、単純にその処理を関数にまとめると、その関数が複数回呼ばれて同じ名前のloggerが複数回参照された場合はそのたびに毎回addHandler
でStreamHandler
を設定してしまうため、ログ出力が重複する。
# 悪い例 import logging def get_logger(name, level=logging.DEBUG): """loggerを設定する (悪い例)""" logger = logging.getLogger(name) logger.setLevel(level) # 毎回Handlerを設定してしまう場合 ch = logging.StreamHandler() ch.setLevel(level) formatter = logging.Formatter( fmt="[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S" ) ch.setFormatter(formatter) logger.addHandler(ch) return logger class Horse: def __init__(self): self.logger = get_logger(self.__class__.__name__) def scream(self): self.logger.debug("ヒヒーン!") if __name__ == "__main__": h1 = Horse() h2 = Horse() h2.scream()
この場合、2回__init__
が呼び出されたために同じloggerに2つのStreamHandlerが設定されているため、次のように2つ重複してログが出力される
[2021-01-27 08:32:59] [Horse] [DEBUG] ヒヒーン! [2021-01-27 08:32:59] [Horse] [DEBUG] ヒヒーン!
提案
そこでhasHandlers
を使ってHandlerの存在を判定して、Handlerがなければ追加するようにすればよさそう。
import logging def get_logger(name, level=logging.DEBUG): logger = logging.getLogger(name) logger.setLevel(level) if not logger.hasHandlers(): ch = logging.StreamHandler() ch.setLevel(level) formatter = logging.Formatter( fmt="[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S" ) ch.setFormatter(formatter) logger.addHandler(ch) return logger class Horse: def __init__(self): self.logger = get_logger(self.__class__.__name__) def scream(self): self.logger.debug("ヒヒーン!") if __name__ == "__main__": h1 = Horse() h2 = Horse() h2.scream()
[2021-01-27 08:35:54] [Horse] [DEBUG] ヒヒーン!
このようにすれば重複したログ出力は避けられる。