logの出力を止めるため、継承しているすべてのLoggerHandlerを削除した。にもかかわらず、ログが表示されてしまった。

解決するまでかなり時間がかかったのでメモ。


問題の再現

import sys
from logging import (
    NullHandler,
    StreamHandler,
    lastResort,
    getLogger,
    INFO,
    CRITICAL,
)
print(sys.version)
3.11.6 | packaged by conda-forge | (main, Oct  3 2023, 10:37:07) [Clang 15.0.7 ]
# loggerの生成
logger = getLogger(__name__)
logger
<Logger __main__ (WARNING)>

Handlerは持っていない。

logger.hasHandlers()
False

親ロガー (root logger) もHandlerは持っていない。

root_logger = logger.parent
print(f"{root_logger=}")
root_logger.hasHandlers()
root_logger=<RootLogger root (WARNING)>





False

root loggerより上位のロガーは存在しない。

print(root_logger.parent)
None

Handlerがないので何も表示されないはず、、と思ったがwarning以上のものが表示されてしまう。

def log_all_level() -> None:
    logger.debug("debug")
    logger.info("info")
    logger.warning("warning")
    logger.error("error")
    logger.critical("critical")
log_all_level()
warning
error
critical

もちろん親にもHandlerがないので、propagate=Falseにして伝搬をオフにしても結果は変わらない。

logger.propagate = False
log_all_level()
warning
error
critical

もちろんloggerlevelCRITICALより高くしてしまえば表示されない。しかし本質的な解決ではない。

# CRITICALより高いレベルしか表示できないように設定する
logger.setLevel(CRITICAL + 1)
log_all_level()

手動で高いレベルを設定してしまえば表示できてしまう。

logger.log(1000, "level1000")
level1000

逆にレベルを引き下げてもWARNING以上しか表示されない。

logger.setLevel(INFO)
log_all_level()
warning
error
critical

なぜ表示されるのか

1つもhandlerが設定されていないとき、logging.lastResortというHandlerにわたされる仕様になっているから。

このロガー (および Logger.propagate 属性を考慮した上で実効的にイベントが伝播する祖先のロガー) にハンドラが接続されていない場合、メッセージは lastResort に設定されたハンドラーに送られます。

logging — Python 用のログ記録手段 - docs.python.org

lastResort
<_StderrHandler stderr (WARNING)>
isinstance(lastResort, StreamHandler)
True

解決策

「何も表示しない」というHandlerNullHandlerを設定する。

logger.addHandler(NullHandler())
log_all_level()