LASSIC Media らしくメディア
スタックトレースとは|エラーの発生箇所を追う
開発会社から不具合の報告を受けたとき、「スタックトレースを確認したところ〜」という説明を受けても、発注担当者やプロジェクトマネージャーには、それが何を示す情報なのか分かりにくいものです。エラーの重さや対応の緊急度を自分たちでも判断したい場面では、この読み方を知っておく価値があります。
この記事では、スタックトレースに何が書かれ、どう読み、原因調査でどう役立てるかを整理します。なお、発生した例外をプログラム内でどう捕まえて処理するかという例外処理の設計そのものは、別の記事で扱うテーマとし、本稿ではエラー発生時に出力される呼び出し履歴の読み方と使い方に絞って解説します。
「不具合報告のメールに、見慣れない英数字の羅列が貼られていた」という経験をお持ちの方もいるでしょう。中身が分からないまま開発会社任せにしてしまうと、対応の優先度や原因の見当を自分たちの言葉で説明できず、社内への報告にも時間がかかりがちです。仕組みを押さえておけば、報告を受けた段階で状況をつかみやすくなります。
細かな読み方まで自分たちで手を動かす必要はありません。ただ、開発会社からの説明を評価し、必要な対応を的確に依頼するためには、スタックトレースが何を語っているのかという全体像を把握しておくことが助けになるでしょう。
この記事のポイント
- スタックトレースは、エラー発生時にどの処理からどの処理を呼び出したかを、順に書き出した記録です。
- 呼び出し履歴の中で最初に例外が投げられた箇所に着目すると、根本原因の見当を付けやすくなります。
- 本番環境の調査では、エラー監視ツールでの集約とログ設計が、調査にかかる時間に関わってきます。
スタックトレースとは
スタックトレースとは、プログラムがエラーで停止したとき、どの処理からどの処理を呼び出してエラーに至ったかという呼び出しの経路を、順に書き出した記録です。
実行中のプログラムで、ある時点までにどの関数がどの順で呼び出されていたかを一覧にしたものであり、スタックバックトレースやバックトレースとも呼ばれます*4。統合開発環境のデバッガが処理を一時停止した際に、呼び出し履歴として表示する情報も、基本的には同じ性質のものです*4。
PythonやJavaScriptなど各言語では、例外(プログラムの実行中に起きた想定外の事態)が発生し、途中の処理で捕まえられないまま処理が終了すると、実行環境がエラーメッセージとあわせてスタックトレースを出力します。プログラムが途中で止まってしまう、いわゆるクラッシュが起きた場面でも、同様の記録が残ります。
Webアプリケーションのサーバー側処理で例外が発生した場合も、考え方は変わりません。利用者の画面には汎用的なエラー表示だけを返しつつ、サーバー側のログにはスタックトレースを記録しておく、という運用が広く採られています。
ブラウザ側で動くJavaScriptの処理でも、同じように例外が発生し、スタックトレースが記録されます。サーバー側とブラウザ側のどちらで起きたエラーかによって、原因調査の担当範囲や優先度の考え方が変わる点も、あわせて押さえておきたいところです。
「スタック」という呼び名は、処理を呼び出すたびに情報を積み上げ、戻るたびに取り出す、という積み重ね式のデータの持ち方に由来します。呼び出しが深くなるほど積み上がる量が増え、例外が起きた瞬間の積み上がり方をそのまま書き出したものが、スタックトレースというわけです。
単なるエラーメッセージ1行だけでは、どの機能のどの処理がきっかけだったのかまでは分かりません。呼び出しの経路が段階的に記録されている点が、原因を絞り込むうえでのスタックトレースの値打ちといえるでしょう。
プログラムの誤りには、書き方そのものの誤りで実行前に検出される種類と、実行してみて初めて表面化する種類とがあります。スタックトレースが関わるのは主に後者、つまりプログラムを動かした結果として起きるエラーです。前者(構文の誤りなど)は、開発環境がコードを保存した時点で指摘してくれる場合が一般的であり、本稿で扱う調査の対象とは性質が異なります。
なお、処理があまりに深く連鎖し続けた場合には、積み上がった呼び出しの記録そのものが上限を超えてしまい、専用のエラーとして扱われる場面もあります。この現象自体は本稿の主題であるエラー原因調査の範囲を超えるため、深追いはしませんが、スタックトレースが極端に長くなっている場合は、呼び出しの連鎖のどこかに繰り返しの誤りがないかを疑う手がかりにもなります。
エラーの種類・呼び出し履歴・行番号 — トレースの3要素
スタックトレースの中身は、大きく3つの情報から成ります。1つ目は、発生したエラーの種類とメッセージです。例外のクラス名(分類名)と、原因を短く示した文言が並びます。
2つ目は、呼び出し履歴です。どの関数(フレームと呼ばれる処理の単位)から、どの関数を呼び出したかが、発生した順に並びます*1。3つ目は、ファイル名と行番号です。どのソースコードの何行目で処理が呼ばれていたかを示し、修正対象を絞り込む手がかりになります*3。
このうち、1つ目のエラーの種類は見落とされがちですが、実は優先度の判断に直結する情報です。たとえば「入力値の形式が想定と違う」という種類の例外と、「外部のサーバーへ接続できない」という種類の例外とでは、疑うべき原因も、確認すべき担当範囲も変わってきます。呼び出し履歴を読み込む前に、まずこの種類とメッセージを確認する習慣をつけておくとよいでしょう。
読む向きは、言語やツールによって異なる点に注意が必要です。Pythonのトレースバックは呼び出した順に上から下へ並び、末尾の行が例外の発生元になります*2。JavaScriptのError.stackプロパティは反対に、最新の呼び出しを先頭に、元のグローバルスコープの呼び出しまで遡る順に並びます*1。
この違いを踏まえずに読むと、発生元を取り違えてしまいかねません。使用する言語やツールが、どちらの並び順を採るのかを、あらかじめ確認しておくとよいでしょう。
この3要素の並べ方は、PythonやJavaScriptに限った話ではありません。Java・PHP・Rubyなど、そのほかの主要な言語でも、エラーの種類・呼び出し履歴・行番号という基本構成そのものは共通しています。開発を委託する言語が変わっても、読み方の基本を押さえておけば応用が利くでしょう。
具体的な例で見てみます。注文画面の処理(関数A)が、在庫を確認する処理(関数B)を呼び出し、さらに在庫確認の中でデータベースへの問い合わせ処理(関数C)を呼び出したとします。関数Cで想定外のデータ形式に当たって例外が発生すると、スタックトレースには「関数C」「関数B」「関数A」という3段の呼び出しが、それぞれのファイル名と行番号つきで記録されるのです。この3段を追えば、どの画面のどの操作から問題が始まったのかを、コードを1行ずつ追わずに把握できます。
Traceback (most recent call last):
File "order.py", line 12, in place_order
check_stock(item)
File "stock.py", line 30, in check_stock
result = query_item(item.code)
File "db.py", line 45, in query_item
raise ValueError("unexpected data format")
ValueError: unexpected data format
この例では、最終行の「ValueError: unexpected data format」がエラーの種類とメッセージ、その上の3行が呼び出し履歴です。並び順は上から下(most recent call last、つまり最新の呼び出しが末尾)であり、末尾に近い「db.py」の行が発生元、いちばん上の「order.py」が最初の入り口ということになります*2。
自社コードの行を探し、発生元をたどる — 読み方の基本
実際にスタックトレースを読むときは、まず自分たちが開発したコードのファイル名を探します。フレームワークやライブラリ内部の行が並んでいても、修正すべき箇所は、実務上、自社の開発対象コードの中にあることがほとんどです。
根本原因は、最初に例外が投げられた箇所に表れます。並び順の末尾(Pythonのように新しい呼び出しほど下に並ぶ形式の場合)、あるいは先頭(JavaScriptのように新しい呼び出しほど上に並ぶ形式の場合)を見ると、発生元の見当が付きやすくなるでしょう*2*1。
フレームワーク内部の行は、いったん読み飛ばして構いません。自社コードのフレームに行き着くまで、履歴をたどっていく読み方が実務的です。
非同期処理(複数の処理を並行して進める書き方)を多用する画面では、実際の呼び出し元とスタックトレースの記載が一致しない場合もあります。処理を依頼した箇所と、実際に例外が発生した箇所が離れて記録されるケースがあるためです。この場合は、トレースだけに頼らず、処理の依頼元をログで別途たどる必要が出てくるでしょう。あわせて、例外が別の例外を連鎖的に引き起こす場面では、最初の例外(原因)と後から起きた例外の両方がトレースに含まれることもあり、どちらが引き金かを見分ける視点も欠かせません。
統合開発環境やエラー監視ツールでは、トレース中のファイル名・行番号をクリックすると、該当するソースコードへ直接移動できる機能を備えている場合があります。この機能を使えば、テキストとして書き出された情報を手作業で追わずに済み、調査の初動を早められるでしょう。
発注担当者やプロジェクトマネージャーがスタックトレースを直接読み解く場面は限られますが、開発会社から共有された内容を、社内のチケット管理ツールやメールで別部署に転送する機会はあるでしょう。その際は、内部のファイルパスや、まれに含まれる値の一部を、社外の関係者に見せてよい情報かどうかを一度確認してから共有するとよいでしょう。表に、読み取れる情報と調査での使い道をまとめました。
| 読み取れる情報 | 調査での使い道 |
|---|---|
| エラーの種類・メッセージ | 何が起きたかを短時間で把握し、対応の優先度を判断する材料にする |
| 呼び出し履歴(フレーム) | どの機能・画面の処理から発生したかを特定する |
| ファイル名・行番号 | 修正対象となるコード箇所を絞り込む |
| 呼び出し順(末尾または先頭) | 最初に例外が投げられた根本原因の候補を絞り込む |
| 自社コードか外部ライブラリか | 修正が可能な箇所か、設定や使い方の見直しで済む箇所かを見分ける |
再現・集約・ログ設計 — 調査と運用での使い道
スタックトレースは、本番環境で起きたエラーを再現し、原因を特定するための手がかりになります。発生箇所と呼び出し経路が分かれば、どの操作の組み合わせで問題が起きたのかを推測しやすくなるでしょう。
本番環境の調査では、おおむね次のような流れでスタックトレースを使います。
- エラー監視ツールの通知やログから、スタックトレースを含む記録を確認する
- 呼び出し履歴をたどり、発生元に近い自社コードの行を特定する
- 発生時の入力値や操作手順を、ログや利用者からの報告とあわせて推測する
- 開発環境や検証環境で同じ条件を再現し、修正内容を確かめる
近年は、Sentryなどのエラー監視ツール(発生した例外を集約し、通知やダッシュボード表示するサービス)を導入し、スタックトレースを自動で収集する運用も広がっています。同じ発生箇所のエラーをまとめて件数管理でき、頻発する不具合を見つけやすくなるのが利点です。
JavaScriptのconsole.trace()のように、任意のタイミングで呼び出し履歴を出力できる機能を使い、あらかじめログに残しておく設計も有効です*5。疑わしい処理の前後にログを仕込んでおくことで、後からの原因調査がしやすくなります。
ログの設計では、記録する重大度(エラー・警告・情報など)ごとにスタックトレースを残すかどうかを決めておくと、後から見返す量を絞り込めます。すべての警告にまでスタックトレースを付けると記録の量が膨らみ、肝心のエラーが埋もれてしまいかねません。エラー監視ツール側では、発生箇所やエラーの種類が同じものを1つのグループとしてまとめる機能を備えていることが一般的で、同じ原因によるエラーが繰り返し通知される事態を抑えられるでしょう。
一方で、注意したい点もあります。スタックトレースには、関数に渡された引数の値まで含まれる場合があり*1、個人情報や認証情報のような機密情報が意図せず記録される恐れがあるのです。ログや監視ツールへ送る前に、出力範囲を絞り込む設計が欠かせません。
とくに、外部の監視ツールへログを転送する構成では、送信前に機密情報を取り除く処理(マスキング)を挟んでおくと、社外のサービスに内部情報がそのまま渡ることを防げます。どの項目を対象にマスキングするかは、扱うデータの性質に応じて、開発初期のうちに決めておきたい設計事項です。
収集したスタックトレースは、蓄積したまま放置せず、定期的に見返す機会を設けることも大切です。発生頻度が高いまま残っているエラーや、同じ箇所で繰り返し発生しているエラーがないかを棚卸しすることで、優先して手当てすべき箇所が見えてきます。エラー監視ツールのダッシュボードを、定例の会議で確認する運用にしている現場も見られます。
記録・非表示・監視の3点 — 発注時に確認する内容
スタックトレースの扱いは、システムを開発会社に発注する段階でも確認しておきたい設計事項です。要件定義や仕様のレビューで細かく詰められることは少ないテーマですが、稼働後の障害対応の速さや、外部への情報流出リスクに直結します。押さえておきたい点を3つに整理しました。
記録・通知の仕組みの有無
1つ目は、エラー発生時にスタックトレースを記録・通知する仕組みがあるかどうかです。ログに残すだけの設計と、監視ツールで通知まで行う設計とでは、障害に気づくまでの早さに差が出ます。
発注段階では、「エラーが起きたことを、誰がどの手段で最初に知るのか」を具体的に確認しておくとよいでしょう。利用者からの問い合わせで初めて気づく体制と、監視ツールの通知で開発担当者が先に気づく体制とでは、対応にかかる時間の見通しが変わってきます。
利用者向け画面に内部情報を出さない設計
2つ目は、利用者向けの画面に内部情報を出していないかという点です。エラー画面にスタックトレースをそのまま表示すると、内部のファイルパスやコード構成が外部から見えてしまい、攻撃の手がかりを与えかねません。汎用的なメッセージにとどめ、詳細は内部ログで保護しておくのが実務的な設計といえます。
レビューの場では、実際にエラーを発生させた画面を見せてもらい、表示内容を確認するとよいでしょう。開発環境ではデバッグ用に詳細な情報を表示し、本番環境では汎用メッセージに切り替える、という出し分けができているかも、あわせて確かめておきたい点です。
監視ツールの活用状況
3つ目は、監視ツールの活用状況です。エラー監視ツールを導入し、頻度や影響範囲を可視化できているかどうかで、障害対応にかける工数は変わってきます。導入していない場合、ログの目視確認だけに頼ることになり、見落としのリスクが高まるでしょう。
導入済みの場合も、通知先が特定の担当者個人になっていないか、担当者が不在のときに気づける体制になっているかは確認しておく価値があります。仕組みを入れるだけでなく、日々の運用に組み込まれているかどうかが、実際の障害対応の速さを左右するのです。
これらの設計を内製で整えるには、対象言語の例外処理・ログ出力の仕様理解に加え、監視ツールの選定や運用体制の構築が必要になります。稼働中のシステムに後づけで組み込む場合は、ログ出力箇所の洗い出しから着手することになります。新規に組み込むときよりも、作業範囲が広がりやすい点に注意が必要でしょう。
保守運用の契約を結ぶ段階では、障害発生時の連絡フローや対応時間の目安とあわせて、スタックトレースをどこまで記録し、どの範囲を発注元と共有するのかを、あらかじめすり合わせておくことをおすすめします。運用が始まってから取り決めるよりも、契約時に明文化しておくほうが、実際に障害が起きたときの動きがスムーズになるでしょう。
まとめ
本稿では、スタックトレースの中身と読み方、調査・運用での活かし方、発注時に確認したい点を整理しました。要点を5つに集約すると、次の通りです。
- スタックトレースは、エラー発生時にどの処理からどの処理を呼び出したかを、順に書き出した記録である。
- エラーの種類・呼び出し履歴・ファイル名と行番号の3要素からなり、言語によって並ぶ向きが異なる。
- 自社コードの行を探し、最初に例外が投げられた箇所に着目すると、根本原因の見当を付けやすい。
- 本番環境の調査では、エラー監視ツールでの集約とログ設計が、調査にかかる時間に関わる。
- 発注時は、記録・通知の仕組み、利用者画面への内部情報の非表示、監視ツールの活用状況を確認しておきたい。
よくある質問
スタックトレースと例外処理は、どう違いますか。
例外処理は、発生した例外をプログラム内でどう捕まえて対応するかという設計の話です。一方でスタックトレースは、捕まえられなかった例外や、記録のために出力させた呼び出し履歴そのものを指します。両者は関係する概念ですが、扱う対象が異なるため、例外処理の設計そのものは別のテーマとして扱うのが整理しやすいでしょう。発注時にどちらの話をしているのかを意識すると、開発会社との会話もかみ合いやすくなります。
スタックトレースを読む向きは、言語やツールによって変わりますか。
はい、変わります。Pythonのトレースバックは呼び出した順に上から下へ並び、末尾の行が例外の発生元です*2。JavaScriptのError.stackプロパティは反対に、最新の呼び出しを先頭に、元のグローバルスコープの呼び出しまで遡る順に並びます*1。読む前に、そのツールの並び順を確認することが欠かせません。開発を委託している会社に、採用している言語での並び順を一度確認しておくと、報告を受けたときに読み違えずに済みます。
本番環境でスタックトレースをそのまま画面に表示してもよいですか。
内部のファイルパスやコード構成、場合によっては引数の値まで含まれるため、利用者向けの画面にそのまま出すことは避けたほうがよいでしょう。表示するとしても内部情報を含まない汎用的なメッセージにとどめ、詳細はログや監視ツール側に記録しておく設計が現実的です。開発環境と本番環境とで表示内容を切り替える設定になっているかも、あわせて確認しておくと判断の材料になります。
スタックトレースを見れば、原因は特定できますか。
呼び出し履歴やエラーメッセージから原因の見当を付けやすくなりますが、それだけで断定できるとは限りません。フレームワーク内部の処理や複数の要因が絡む不具合では、再現条件の確認やログとの突き合わせが欠かせない場面もあります。特定の利用者の環境やデータに依存する不具合では、スタックトレースだけでは再現条件までは分からないこともあるでしょう。スタックトレースは、調査の出発点となる手がかりの一つと捉えるのがよいでしょう。手がかりを積み重ねながら原因像を絞り込んでいく姿勢が、結果として調査の近道になります。
著者:テレリモ総研編集部 鈴木 亮佑
LASSICでは、国内ニアショア開発体制を活かし、エラー監視・ログ設計の整備からシステム開発・保守までを一貫して支援する体制です。要件定義の段階から実装、テスト、リリース後の運用・保守まで、工程を分けずに任せられる点も強みでしょう。障害対応の体制づくりを内製だけで進めるのが難しい場合も、設計から運用の定着までを継続して支援します。本番環境のエラー調査体制でお困りの際も、ご相談いただけます。
ご不明な点はお問い合わせフォームからもご連絡いただけます。
出典
- MDN Web Docs「Error: stack property」
- Python公式ドキュメント「8. Errors and Exceptions」
- Python公式ドキュメント「traceback — Print or retrieve a stack traceback」
- Wikipedia「スタックトレース」
- MDN Web Docs「console: trace() static method」