ログの記録と解析

システム開発やアプリケーションの運用において、プログラムが「いつ」「どこで」「どのような処理を行ったか」、そして「どんなエラーが起きたか」を時系列で記録する仕組みをロギング(Logging)と呼びます。
開発中のデバッグ目的だけでなく、本番環境で予期せぬ不具合が発生した際に、原因を特定してトラブルシューティングを行うための唯一の手がかりとなるため、実務において極めて重要な技術です。Pythonでは標準ライブラリの logging モジュール を使うことで、高度なログ管理を簡単に実装できます。
ロギングの基本的な使い方
Pythonでログを出力する最もシンプルな実装例です。logging.basicConfig() を使って、出力するログの基準や見た目を一括で設定します。
import logging
# ログの基本設定
# level: DEBUG以上のすべてのログを出力対象にする
# format: 「日時 - ログの重要度 - メッセージ」の形式で表示するよう指定
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# さまざまな重要度のログを記録
logging.debug('【DEBUG】変数の値を確認: x = 10')
logging.info('【INFO】ユーザーのログイン処理が完了しました')
logging.warning('【WARNING】ディスク容量が残り20%未満です')
logging.error('【ERROR】ファイルの読み込みに失敗しました')
logging.critical('【CRITICAL】データベースに接続できません。システムを停止します')
ログレベル(重要度)の仕組みと適切な使い分け
logging モジュールには5段階のログレベル(重要度)が用意されています。開発時や運用時にログが溢れてしまわないよう、状況に応じて適切なレベルを使い分ける必要があります。
| ログレベル | 数値(優先度) | システム上の意味 | 実務での具体的なユースケース |
|---|---|---|---|
DEBUG |
10 | 詳細なデバッグ情報 | 開発中に、関数の引数の値やループの通過回数など、細かい挙動を追跡したいとき。 |
INFO |
20 | 通常の動作・進行情報 | 「サーバーが起動した」「バッチ処理が正常に終了した」など、順調に進んでいる記録。 |
WARNING |
30 | 想定外だが、動作は継続できる警告 | 「古い関数(非推奨)が使われた」「設定ファイルがないのでデフォルト値で代用した」とき。 |
ERROR |
40 | 問題が発生し、一部の処理が失敗した状態 | 「特定のデータ処理で例外が発生した」など、機能は失敗したがシステム自体は稼働し続けられるとき。 |
CRITICAL |
50 | 致命的なエラー、即時対応が必要な状態 | 「メモリ不足でクラッシュした」「メインのDBに繋がらない」など、アプリの継続が不可能なとき。 |
※ basicConfig(level=logging.WARNING) のように設定すると、それより数値(優先度)が低い DEBUG や INFO のログは自動的に無視され、画面やファイルに出力されなくなります。「開発環境では DEBUG、本番環境では INFO もしくは WARNING」 と切り替えるのが実務の標準です。
ログをファイルへ保存する(永続化)
画面(標準出力)に表示されたログは、コンソールを閉じると消えてしまいます。後から解析できるように、ログをファイルとして自動保存(永続化)するには、basicConfig の filename 引数 を指定します。
import logging
# filename を指定すると、画面ではなく指定ファイルに追記(Append)されるようになります
# encoding='utf-8' を指定することで、日本語の文字化けを防ぎます
logging.basicConfig(
filename='app.log',
filemode='a', # 'a'は追記、'w'は毎回上書き
encoding='utf-8',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logging.info('アプリケーションの実行を開始しました。')
logging.warning('外部APIの応答が遅延しています。')
【注意】実務での注意点(basicConfigの罠)
logging.basicConfig()は、プログラム内で「最初に呼び出された1回だけ」しか効果がありません。すでに別のライブラリなどでログ設定が走った後に呼び出しても、設定が完全に無視されてしまいます。そのため、ログの設定は必ずメインプログラムの最上部で行う必要があります。
エラー発生時の「スタックトレース」をログに残す方法
try-except 構文でエラー(例外)をキャッチした際、単に「エラーが起きました」とメッセージを残すだけでは不十分です。logging.error(..., exc_info=True) または logging.exception(...) を使用すると、エラーがプログラムの何行目で発生したかという詳細な形跡(スタックトレース)を丸ごとログに書き出すことができます。
import logging
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
try:
# 意図的にゼロ除算エラーを発生させる
result = 10 / 0
except ZeroDivisionError as e:
# logging.exception を使うと、自動的に詳細なエラーの裏付け(トレースバック)が記録されます
logging.exception("計算処理中に予期せぬエラーが発生しました。")
これを実行すると、ファイルやコンソールには以下のような極めて有益な解析データが書き出されます。
2026-07-10 11:35:00,000 - ERROR - 計算処理中に予期せぬエラーが発生しました。
Traceback (most recent call last):
File "script.py", line 6, in <module>
result = 10 / 0
ZeroDivisionError: division by zero
実践的なログ解析のステップ
大量に出力されたログファイルから問題の根本原因を特定(トラブルシューティング)する際の標準的な手順です。
- 致命的なエラーの抽出(フィルタリング):まずはログファイルを
ERRORやCRITICALというキーワードで検索(Linux環境であればgrepコマンド等を使用)し、どこで不具合が起きたかをピンポイントで特定します。 - 文脈(コンテキスト)の解析:エラーが見つかったら、その直前の時間帯(
asctime)に出力されているINFOやDEBUGのログを読み込みます。「エラーが起きる直前に、ユーザーがどんなボタンを押したか、どんなデータを入力したか」という前後の文脈を追うことで、再現手順が見えてきます。 - 時間帯ごとの発生頻度確認:特定の時間帯に一斉にエラーや警告が多発している場合、プログラムのバグではなく、「その時間にアクセスが集中してサーバーのメモリが限界に達した」「連携している外部システムがメンテナンスで落ちていた」といったインフラ・環境側の原因を導き出すことができます。
まとめ
- print文の代わりにログを使う:
print()での動作確認は本番環境に残せませんが、loggingであればレベル管理によって、本番環境のパフォーマンスを落とさずに安全な運用監視が行えます。 - 状況に応じたレベル分けの徹底:デバッグ、通常運用、システム異常を
DEBUG/INFO/ERRORと正しく分類して出力することで、ノイズの少ない綺麗なログ設計が実現します。 - 不具合時は
logging.exceptionで証拠を残す:例外処理(try-except)と組み合わせる際は、エラーメッセージだけでなくTraceback情報まで確実に記録させることで、デバッグ効率が劇的に向上します。
実務でより大規模なアプリケーション(DjangoやFastAPIなどのWebアプリなど)を構築する際は、ログを日付ごとに自動分割する「ローテーティング(RotatingFileHandler)」などの高度なハンドラー機能を取り入れることで、ファイル容量の肥大化を防ぎつつ、さらに堅牢なシステムログ基盤を構築できるようになります。