85555


JupyterLabやGoogleColabで、以下のコードを複数回実行すると、ログも重複して出力されるようになります。  
addHandlerが何度も呼び出されるのが原因と思われます。  
Kernel(Pythonプロセス)をリセットせずに、セルを何度実行しても、  
ログが重複出力されないよう防止する方法はないでしょうか？  
（独自に設定したloggerかそうでないか判定することができると助かります。）

[code]

    from logging import getLogger, StreamHandler, Formatter, shutdown
    from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL
    
    def init_logger(name='test.sample'):
        # Streamハンドラー
        stream_formatter = Formatter(
            fmt='[%(asctime)s] <%(levelname)s> - %(message)s',
            datefmt="%Y-%m-%d %H:%M:%S %Z" # なぜか%zは機能しない
        )
        stream_handler = StreamHandler()
        stream_handler.setLevel(DEBUG)
        stream_handler.setFormatter(stream_formatter)
    
        logger = getLogger(name)
        logger.setLevel(DEBUG)
        logger.addHandler(stream_handler)
        logger.propagate = False
    init_logger()
    
    # ログ出力部
    logger = getLogger('test.sample')
    logger.debug(777)
    
[/code]


CC BY-SA 4.0
2022-01-07T03:04:30.193
85540
2022-01-07T20:07:39.307
2022-01-07T03:08:56.803
3060
48481
question
python python3
loggingのaddHandlerの多重実行を防止する方法


`logger.addHandler` に設定したハンドラは `logger.handlers` 変数に格納されます。  
handlersへは、「同じハンドラは2重登録しない」ようになっていますが、別のハンドラインスタンスを作成してaddHandlerに渡した場合、「違うハンドラ」なので登録されてしまいます。

コード  
[`https://github.com/python/cpython/blob/v3.10.1/Lib/logging/__init__.py#L1448`](https://github.com/python/cpython/blob/v3.10.1/Lib/logging/__init__.py#L1448)  
[`https://github.com/python/cpython/blob/v3.10.1/Lib/logging/__init__.py#L1640-L1649`](https://github.com/python/cpython/blob/v3.10.1/Lib/logging/__init__.py#L1640-L1649)

環境全体のロギング設定を一定に保つことが目的であれば、[logging.basicConfig(...)](https://docs.python.org/ja/3/library/logging.html#logging.basicConfig)や[logging.dictConfig(...）](https://docs.python.org/ja/3/library/logging.config.html#logging.config.dictConfig)を使って、起動時に一度だけ初期化するように実装するべきです。

そうできない事情がある場合、今回のケースで一番簡単な対処方法は、logger.handlersをリセットしてしまうことです。

[code]

        logger = getLogger(name)
        for h in logger.handlers[:]:
            logger.removeHandler(h)
            h.close()
        logger.setLevel(DEBUG)
        logger.addHandler(stream_handler)
        logger.propagate = False
    
[/code]

参考コード:
[`https://github.com/python/cpython/blob/v3.10.1/Lib/logging/__init__.py#L2022-L2025`](https://github.com/python/cpython/blob/v3.10.1/Lib/logging/__init__.py#L2022-L2025)

> （独自に設定したloggerかそうでないか判定することができると助かります。）

rootロガー以外は後から追加されるロガーのため、自分のコードで追加したものか、ライブラリで追加したものかの見分けが付きません。  
例えば、getLoggerしたあとに独自の属性をセットする方法で初期化済みか判定する方法が考えられます。

[code]

        logger = getLogger(name)
        if not getattr(logger, 'my_init', False):
            logger.setLevel(DEBUG)
            logger.addHandler(stream_handler)
            logger.propagate = False
            logger.my_init = True
    
[/code]


CC BY-SA 4.0
2022-01-07T20:07:39.307
85555
2022-01-07T20:07:39.307
806
85540
answer