Pythonでアプリケーションを開発する際、ログ記録はプログラムの動作状況を把握し、問題発生時に詳細な情報を得るために欠かせない機能です。標準ライブラリとして提供されるloggingモジュールは、柔軟で強力なログ管理を実現します。本稿では、このモジュールの実践的な使い方と効率的な運用のポイントを解説します。
基本的なログ出力
まずはloggingモジュールの最小構成での利用方法を見てみましょう。
import logging
# 基本設定
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("application.log"),
logging.StreamHandler()
]
)
# ログ出力
logging.debug("デバッグ情報")
logging.info("情報メッセージ")
logging.warning("警告メッセージ")
logging.error("エラー発生")
logging.critical("致命的エラー")
このコードでは、ログレベルをINFOに設定し、ファイルとコンソールの両方に出力しています。フォーマットにはタイムスタンプとレベルを含めています。
ログレベルの管理
loggingモジュールが提供する5段階のログレベルは、DEBUG、INFO、WARNING、ERROR、CRITICALです。設定したレベル以上のメッセージのみが出力され、それより低いレベルは無視されます。例えばWARNINGを設定すると、DEBUGやINFOは記録されません。
カスタムフォーマットの適用
デフォルトのフォーマットではなく、プロジェクトの要件に合わせた出力形式を設定できます。
formatter = logging.Formatter(
"%(asctime)s | %(name)s | %(levelname)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger = logging.getLogger("myapp")
logger.addHandler(console_handler)
logger.setLevel(logging.DEBUG)
この設定では、日時の形式をISO 8601に変更し、ロガー名を追加しています。
ログファイルのローテーション
長時間稼働するアプリケーションでは、ログファイルが肥大化する問題に対処する必要があります。RotatingFileHandlerを使えば、ファイルサイズに基づくローテーションが実現できます。
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("rotating_logger")
handler = RotatingFileHandler(
"server.log",
maxBytes=1024 * 1024, # 1MB
backupCount=3
)
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
for i in range(5):
logger.info(f"テストログ #{i}")
maxBytesで最大サイズを指定し、backupCountで保持する古いファイルの数を制御します。サイズ超過時には自動的に新しいファイルが生成されます。
実運用でのベストプラクティス
- 集中管理された設定: プロジェクト全体でログ設定を一元化し、
logging.yamlやlogging.jsonで外部から制御できるようにする。 - 適切なレベルの選択: 開発中は
DEBUG、本番ではINFOorWARNINGを使用し、パフォーマンスと情報量のバランスを取る。 - 構造化ログの活用: JSON形式で出力し、ログ解析システム(ELK Stackなど)との連携を容易にする。
- 機密情報の除外: パスワードやAPIキーなどの機密データがログに出力されないよう注意する。
- 例外トレースの完全取得:
logger.exception()を使用してスタックトレースを確実に記録する。
構造化ログの例
import logging
import json
class JSONFormatter(logging.Formatter):
def format(self, record):
log_record = {
"time": self.formatTime(record, self.datefmt),
"level": record.levelname,
"message": record.getMessage(),
"module": record.module,
"line": record.lineno
}
return json.dumps(log_record)
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger = logging.getLogger("structured")
logger.addHandler(handler)
logger.setLevel(logging.INFO)
logger.info("リクエスト完了", extra={"user": "admin", "duration": 0.23})
この方法で出力されたログは、機械可読性が高く、後続の分析ツールで処理しやすくなります。