Python logging.error()とは?エラーを記録する方法

Pythonのlogging.error()は、処理が失敗したことを記録する関数です。

例外処理とセットで使うことが多く、トレースバックを一緒に残せるかが調査効率を大きく左右します。


logging.error()の基本的な使い方

import logging

logging.basicConfig(format="%(levelname)s: %(message)s")

try:
    1 / 0
except ZeroDivisionError:
    logging.error("計算に失敗しました")

実行結果:

ERROR: 計算に失敗しました

ERRORはWARNINGより上のレベルなので、設定なしでも表示されます。


これだけでは原因が分からない

上のログには重大な欠陥があります。「計算に失敗した」ことは分かりますが、なぜ失敗したのか、どこで失敗したのかが記録されていません。

調査するには、例外の中身が必要です。


logging.exception()でトレースバックを残す

import logging

logging.basicConfig(format="%(levelname)s: %(message)s")

try:
    1 / 0
except ZeroDivisionError:
    logging.exception("計算に失敗しました")

実行結果:

ERROR: 計算に失敗しました
Traceback (most recent call last):
  File "<string>", line 5, in <module>
ZeroDivisionError: division by zero

トレースバックが付きました。ファイル名・行番号・例外の種類がすべて記録されます。

logging.exception()はレベルがERRORで、例外情報を自動で付けてくれる関数です。exceptブロックの中でしか使えません(外で呼ぶとNoneType: Noneと出ます)。


exc_info=Trueでも同じ

import logging

logging.basicConfig(format="%(levelname)s: %(message)s")

try:
    1 / 0
except ZeroDivisionError:
    logging.error("失敗", exc_info=True)

実行結果:

ERROR: 失敗
Traceback (most recent call last):
  File "<string>", line 5, in <module>
ZeroDivisionError: division by zero

結果はexception()と同じです。

書き方 特徴
logging.exception(msg) 短い。ERROR固定
logging.error(msg, exc_info=True) 同じ結果。明示的
logging.warning(msg, exc_info=True) ERROR以外でも例外を残せる

exc_info=Trueはどのレベルでも使えます。リトライ中の失敗をWARNINGで記録しつつ、原因も残したいときに便利です。


何をERRORに書くか

基準は「やろうとしたことが達成できなかった」かどうかです。

ERRORに向くもの 例
処理が失敗した 「注文の保存に失敗」
再試行しても駄目だった 「3回試して接続できず」
必須のデータがない 「必要な設定項目が未定義」
外部サービスが応答しない 「決済APIがタイムアウト」

逆に、代替手段で続行できたならWARNINGです。境界は「目的を達成できたか」で判断してください。


ログを出したあと、どうするか

よくある間違いが「ログを出して握りつぶす」ことです。

try:
    save(order)
except Exception:
    logging.exception("保存に失敗")
    # ← 何もせず先へ進んでしまう

呼び出し元は成功したと思い込みます。ログには残りますが、誰も見なければ気づきません。

選択肢は3つです。

方針 書き方
呼び出し元に知らせる ログを出してraiseで再送出
この場で処理を終える ログを出してsys.exit(1)
意図的に続行する なぜ続行してよいかをコメントで書く
import logging

try:
    save(order)
except Exception:
    logging.exception("注文の保存に失敗: order_id=%s", order.id)
    raise

ログを残しつつ、失敗は上に伝える——これが基本形です。


except Exceptionで全部受けない

try:
    process()
except Exception:
    logging.exception("エラー")

手軽ですが、想定していないバグまで飲み込みます。タイプミスによるNameErrorも同じ扱いになり、原因の切り分けが遅れます。

捕まえる例外は具体的に指定してください。

try:
    process()
except (OSError, ValueError):
    logging.exception("処理に失敗")
    raise

追跡できる情報を入れる

logging.exception("保存に失敗")                          # 何の保存か不明
logging.exception("注文の保存に失敗: order_id=%s", oid)   # 追跡できる

ERRORログは障害対応で真っ先に読まれます。「どのデータで」「どの処理が」失敗したかが分かるよう、識別子を必ず含めてください。

ただしパスワード・トークン・個人情報は書かない——これはどのレベルでも同じです。


5つのログレベル

レベル 数値 使う場面
DEBUG 10 開発中の詳細な記録
INFO 20 正常に進んだことの記録
WARNING 30 問題の予兆(既定はここから)
ERROR 40 処理が失敗した
CRITICAL 50 システムが続行できない

logging.error()のまとめ

  • error()は処理が失敗したことを記録する。設定なしで出る
  • メッセージだけでは原因が分からない
  • 例外処理ではlogging.exception()でトレースバックを残す
  • exc_info=Trueならどのレベルでも例外情報を付けられる
  • WARNINGとの境界は「目的を達成できたか」
  • ログを出して握りつぶさない——raiseで伝える
  • 追跡用のIDを入れ、機密情報は入れない

あわせて読みたい

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら