Python traceback.format_exc()とは?トレースバックを文字列で取得する方法

traceback.format_exc()は、トレースバックを文字列として取得する関数です。

画面に出すのではなくログ・DB・通知サービスに送りたいときに使います。


format_exc()の基本

import traceback

def f():
    return 1 / 0

try:
    f()
except ZeroDivisionError:
    s = traceback.format_exc()
    print("型:", type(s).__name__)
    print(s, end="")

実行結果:

型: str
Traceback (most recent call last):
  File "demo2.py", line 7, in <module>
    f()
  File "demo2.py", line 4, in f
    return 1 / 0
ZeroDivisionError: division by zero

戻り値はstrです。末尾に改行が含まれるため、print()にはend=""を付けると二重改行を防げます。


print_exc()との違い

print_exc() format_exc()
動き 表示する 文字列を返す
出力先 標準エラー出力 なし
加工 できない できる

内容は同じです。「出す」か「受け取る」かの違いだけです。

文字列で受け取れば、ログに書く・DBに保存する・Slackに送る——何でもできます。


ログに残す

import traceback
import logging

for item in items:
    try:
        process(item)
    except Exception:
        logging.error("失敗: %s\n%s", item, traceback.format_exc())

どの項目で失敗したかを一緒に残せます。

これがformat_exc()の典型的な使い方です。トレースバックだけでは「どのデータで落ちたのか」が分かりません。

ただし、loggingには専用の書き方もあります。

        logging.exception("失敗: %s", item)

こちらのほうが簡潔です。format_exc()を呼ばなくてもトレースバックが付きます。

ではformat_exc()はいつ使うのか——ログ以外に送るときです。


通知やDBに送る

import traceback

try:
    処理()
except Exception as e:
    record = {
        "type": type(e).__name__,
        "message": str(e),
        "traceback": traceback.format_exc(),
    }
    save_to_db(record)
    notify_slack(record)

構造化して保存できます。

例外の種類・メッセージ・トレースバックを分けて持てば、あとから種類ごとに集計できます。

JSONにするなら、そのままjson.dumps()に渡せます。

「Python json.dumps()とは?辞書をJSON文字列に変換する方法」を参照してください。


ユーザーには見せない

重要な注意点です。

トレースバックには次の情報が含まれます。

  • サーバー上のファイルパス(ディレクトリ構成が分かる)
  • 使っているライブラリとその位置
  • ソースコードの一部
  • 変数名や処理の流れ

Webアプリでこれをそのまま返すと、攻撃の手がかりを与えることになります。

try:
    処理()
except Exception:
    logging.exception("内部エラー")          # 詳細はログへ
    return "エラーが発生しました", 500       # 利用者には最小限

詳細はログに、利用者には一般的なメッセージを。これが原則です。


行ごとに扱う

import traceback
import sys

try:
    f()
except ZeroDivisionError:
    et, ev, tb = sys.exc_info()
    lines = traceback.format_exception(et, ev, tb)
    print("型:", type(lines).__name__, "要素数:", len(lines))
    for i, l in enumerate(lines):
        print(f"[{i}]", repr(l))

実行結果:

型: list 要素数: 4
[0] 'Traceback (most recent call last):\n'
[1] '  File "demo3.py", line 5, in <module>\n    f()\n'
[2] '  File "demo3.py", line 2, in f\n    return 1/0\n'
[3] 'ZeroDivisionError: division by zero\n'

format_exception()はリストで返します。最終行だけ取り出すといった加工ができます。

    print(lines[-1].strip())

実行結果:

ZeroDivisionError: division by zero

Python 3.10以降はformat_exception(e)と例外オブジェクト1つだけでも呼べます。3.9以前は3引数が必要です。


format_exc()のまとめ

  • トレースバックを文字列で返す
  • 内容はprint_exc()と同じ——出すか受け取るかの違い
  • ログだけならlogging.exception()が簡潔
  • DB・通知に送るときに真価を発揮
  • 利用者に見せてはいけない(パスやコードが漏れる)
  • 行ごとに扱うならformat_exception()

あわせて読みたい

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

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