HOWTO · Python

Python での stdout へのロギング

logging.basicConfig()関数と logging.StreamHandler()関数を使用して、Python で stdout にログを記録できます。

このページの内容

Python のログメッセージを標準出力(stdout)に送るには、logging.basicConfig() の stream に sys.stdout を指定します。出力先を細かく制御する場合は、logging.StreamHandler(sys.stdout) をロガーに追加します。同じログをコンソールとファイルの両方に出すには、ストリームハンドラーと別の FileHandler を併用します。

Python で logging.basicConfig() 関数を使って stdout にログを出力する

アプリケーション全体に簡単な設定を適用するには、logging.basicConfig() でルートロガーを設定します。出力先のストリームは stream 引数で指定します。既定ではログは sys.stderr に出力されます。標準出力に送るには、sys.stdout を指定します。level はルートロガーを通過するログレベルを制御し、format は出力する各行の書式を指定します。

import logging
import sys

logging.basicConfig(
    level=logging.INFO,
    format="%(levelname)s: %(message)s",
    stream=sys.stdout,
)

logging.info("Application started")
INFO: Application started

basicConfig() は、ルートロガーを一度だけ手軽に設定するための関数です。アプリケーションの別の箇所ですでにルートロガーにハンドラーが追加されている場合、後から呼び出してもその設定は変更されません。そのため、ここで stream を変えても出力先は切り替わりません。Python 3.8 以降では、force=True を指定すると、既存のルートロガーのハンドラーを削除してから設定を適用できます。アプリケーションのログ設定を置き換える責任がある場合に限って使用してください。それ以外の場合は、アプリケーションのエントリーポイントで一度だけログを設定します。

filename と filemode はファイル出力を設定するオプションです。filename はファイル名を指定し、filemode はファイルの開き方を指定します。既定値は追記モード("a")です。basicConfig() に filename と stream を同時に渡してはいけません。この関数では出力先をどちらか一方に選びます。標準出力とファイルの両方にログを送る方法は、後述のように別々のハンドラーを使うことです。また、force=True(Python 3.8 以降で使用可能)を指定しない限り、basicConfig() がルートロガーを設定するのは、まだハンドラーが追加されていない場合だけです。

Python で logging.StreamHandler() 関数を使って stdout にログを出力する

名前付きロガーを制御したい場合や、複数の出力先を組み合わせる場合は、ハンドラーを明示的に設定します。logging.StreamHandler() の既定の出力先は sys.stderr なので、sys.stdout を明示して渡します。ロガーとハンドラーの両方にレベルを設定でき、ログレコードは両方のレベル条件を満たした場合に出力されます。この例では、標準出力用とファイル用のハンドラーを1つずつ作成し、同じロガーに追加します。

import logging
import sys
import tempfile
from pathlib import Path

log_path = Path(tempfile.gettempdir()) / "python-logging-example.log"
logger = logging.getLogger("example")
logger.setLevel(logging.INFO)
logger.propagate = False

formatter = logging.Formatter("%(levelname)s: %(message)s")
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setLevel(logging.INFO)
stdout_handler.setFormatter(formatter)
file_handler = logging.FileHandler(log_path, mode="w", encoding="utf-8")
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)

logger.addHandler(stdout_handler)
logger.addHandler(file_handler)
logger.info("Application started")

for handler in (stdout_handler, file_handler):
    handler.close()
    logger.removeHandler(handler)
INFO: Application started

各ハンドラーは、ログレコードを書式化して、それぞれの出力先に書き込みます。ファイルはシステムの一時ディレクトリに作成され、mode="w" によって実行のたびに例のファイルが上書きされます。長時間稼働するアプリケーションでは、ハンドラーを繰り返し追加せず、一度だけ設定してください。同じハンドラーが重複すると、1つのログレコードが複数回出力されることがあります。本番環境では、アプリケーションに合わせてファイルのパスとハンドラーのライフサイクルを管理してください。

まとめ

ルートロガーを手早く設定するには、logging.basicConfig() の stream に sys.stdout を指定します。名前付きロガーを制御したい場合や、ハンドラーの設定をカスタマイズしたい場合、複数の出力先を使いたい場合は、logging.StreamHandler(sys.stdout) を選びます。ストリームを明示しない StreamHandler は stderr に出力します。ファイルとコンソールの両方にログを出すには、ストリーム用とファイル用の別々のハンドラーが必要です。