プログラミングをしていると、エラーが発生したときに「スタックトレース」という長いメッセージが表示されることがあります。
スタックトレースには、ファイル名や関数名、行番号などが大量に表示されるため、プログラミング初心者の場合、
- 何が書かれているのかわからない
- どこを見ればいいのかわからない
と感じることもあるでしょう。
しかし、スタックトレースは、プログラムのどこでエラーが発生したのかを調べるための重要な情報です。
この記事では、スタックトレースの基本的な意味から、コールスタックとの関係、実際の見方まで、初心者向けにわかりやすく解説します。
スタックトレースとは?
スタックトレース(Stack Trace)とは、プログラムでエラーが発生したときなどに、エラーが発生した場所と、そこに至るまでの関数やメソッドの呼び出し関係を表示した情報です。
簡単にいうと、
「どの処理を通って、どこでエラーが発生したのか」を確認するための記録
です。
例えば、次のような順番で関数が呼び出されたとします。
main()
↓
showUser()
↓
getUserName()
↓
エラー発生このときスタックトレースを見ることで、
main()
↓
showUser()
↓
getUserName()というように、エラーが発生するまでにどの処理が呼び出されていたのかを確認できます。
スタックトレースには、一般的に次のような情報が含まれます。
- エラーの種類
- エラーメッセージ
- 関数名・メソッド名
- ファイル名
- 行番号と列番号
- 関数やメソッドの呼び出し関係
そのため、プログラムで問題が発生したときの原因調査に役立ちます。
スタックトレースの「スタック」とは?
スタックトレースを理解するには、まず「スタック」という言葉を簡単に理解しておくとわかりやすくなります。
スタック(Stack)とは、データを積み重ねるように管理する仕組みです。
例えば、お皿を重ねる場面をイメージしてください。
3枚目のお皿
2枚目のお皿
1枚目のお皿新しいお皿は上に積み、取り出すときも一番上のお皿から取り出します。
このように、「最後に追加したものを最初に取り出す仕組み」をスタックと呼びます。
この考え方は、プログラムで関数を呼び出すときにも使われています。
コールスタックとは?
プログラムでは、現在どの関数が実行されているのかを管理するために「コールスタック(Call Stack)」が使われます。
例えば、次のようなJavaScriptのプログラムを考えてみます。
function main() {
showUser();
}
function showUser() {
getUserName();
}
function getUserName() {
console.log("Taro");
}
main();最初にmain()が呼び出されます。
main()main()の中からshowUser()が呼び出されると、コールスタックには次のように積み重なります。
showUser()
main()さらに、showUser()からgetUserName()が呼び出されると、コールスタックには次のように積み重なります。
getUserName()
showUser()
main()getUserName()の処理が終わると、コールスタックから取り除かれます。
その後、showUser()、main()という順番で処理が終わり、それぞれコールスタックから取り除かれます。
このように、現在実行中の関数や、その関数を呼び出した関数の情報を管理しているものがコールスタックです。
スタックトレースは、このコールスタックの情報をもとに、どのような順番で処理が呼び出されたのかを確認できるようにしたものです。
スタックトレースの具体例
実際にエラーを発生させて、スタックトレースを確認してみましょう。
JavaScriptで次のプログラムを実行するとします。getUserName()では、throw new Error()を使用して意図的にエラーを発生させています。
function main() {
showUser();
}
function showUser() {
getUserName();
}
function getUserName() {
throw new Error("ユーザー名を取得できません");
}
main();実行すると、環境によって表示内容は異なりますが、次のようなスタックトレースが表示されます。
Error: ユーザー名を取得できません
at getUserName (app.js:10:9)
at showUser (app.js:6:3)
at main (app.js:2:3)
at Object.<anonymous> (app.js:13:1)最初は難しそうに見えますが、1行ずつ確認すると意味はそれほど複雑ではありません。
スタックトレースの見方
先ほどのスタックトレースを詳しく見てみます。
Error: ユーザー名を取得できません
at getUserName (app.js:10:9)
at showUser (app.js:6:3)
at main (app.js:2:3)
at Object.<anonymous> (app.js:13:1)スタックトレース自体がエラーではありません。エラーが発生したときに、その原因を調べるために表示される情報です。
最初にエラーメッセージを確認する
まず確認したいのが、最初の行です。
Error: ユーザー名を取得できませんここには、発生したエラーの種類やエラーメッセージが表示されます。
今回の場合は、ユーザー名を取得できませんというエラーが発生したことがわかります。
実際の開発では、
TypeError
ReferenceError
SyntaxError
RangeErrorなど、プログラミング言語やエラー内容によってさまざまな種類のエラーが表示されます。
まずは、このエラー名やメッセージを確認することが重要です。
エラーが発生した場所を確認する
次に確認したいのが、Error: ユーザー名を取得できませんの下の行です。
at getUserName (app.js:10:9)これは、次の箇所でエラーが発生したことを表しています。
- 関数名:
getUserName - ファイル名:
app.js - 行番号:10行目
- 列番号:9列目付近
つまり、まずはapp.jsの10行目付近を確認すればよいことがわかります。
スタックトレースを見るときは、エラーが発生した場所として表示されている、自分が書いたプログラムのファイル名や行番号を探すのが基本です。
どこから呼び出されたのかを確認する
at getUserName (app.js:10:9)の下には以下の情報が表示されています。
at showUser (app.js:6:3)
at main (app.js:2:3)
at Object.<anonymous> (app.js:13:1)これは、処理がおおよそ次の順番で呼び出されたことを表しています。
app.js
↓
main()
↓
showUser()
↓
getUserName()
↓
エラー発生つまり、「main()からshowUser()が呼ばれ、showUser()からgetUserName()が呼ばれ、getUserName()の中でエラーが発生した」という流れを確認できます。
このように、スタックトレースを見ることで、エラーが発生した場所だけでなく、そこに至るまでの処理の流れも確認できます。
スタックトレースは下から読むの?
スタックトレースについて調べると、「下から読む」と説明されることがあります。
ただし、必ず下から読むというより、何を調べたいのかによって見る場所が変わると考えるとわかりやすいでしょう。
例えば、次のスタックトレースがあります。
Error: ユーザー名を取得できません
at getUserName (app.js:10:9)
at showUser (app.js:6:3)
at main (app.js:2:3)
at Object.<anonymous> (app.js:13:1)エラーが直接発生した場所を確認したい場合は、エラーメッセージの直後にある、
at getUserName (app.js:10:9)から確認します。
一方、「そもそもどこからこの処理が始まったのか」を確認したい場合は、下側から、
app.js
↓
main()
↓
showUser()
↓
getUserName()という順番で追っていくと、処理の流れを理解しやすくなります。
そのため初心者の場合は、以下の順番で確認するのがおすすめです。
- エラーメッセージを見る
- エラーが発生した場所を見る
- 必要であれば呼び出し元を追っていく
スタックトレースを見るときのポイント
実際のスタックトレースは、数行ではなく数十行以上になることもあります。
そのため、すべての行を最初から理解しようとする必要はありません。
まずは次のポイントを確認しましょう。
エラーの種類とメッセージを確認する
最初に、どのようなエラーなのかを確認します。
例えば、
TypeError: Cannot read properties of undefinedと表示されていれば、JavaScriptでundefinedになっている値に対して、何らかのプロパティへアクセスしようとしている可能性があります。
エラー名とエラーメッセージは、原因を調べるための重要な手がかりになります。
自分が書いたファイルを探す
ライブラリやフレームワークを利用している場合、スタックトレースには大量のファイルが表示されることがあります。
例えば、Node.jsのアプリケーションでは、node_modules配下のファイルが大量に表示されることがあります。
しかし、必ずしもライブラリそのものに問題があるとは限りません。まずは、src/app.jsなど、自分が作成したファイルが表示されていないか確認すると原因を見つけやすくなります。
行番号を確認する
スタックトレースには、次のような情報が表示されることがあります。
app.js:10:9これは一般的に、
ファイル名:行番号:列番号を表します。
エディターによっては、スタックトレースのファイル名や行番号をクリックするだけで該当箇所を開ける場合もあります。
スタックトレースが役立つ場面
スタックトレースは、主にプログラムの不具合を調査するときに使用します。
例えば、次のような場面です。
- プログラムが突然エラーになった
- どのファイルで問題が発生したのかわからない
- どの関数から問題の処理が呼ばれたのか確認したい
- 本番環境で発生したエラーの原因を調査したい
- ログに残されたエラーから処理の流れを確認したい
特に規模の大きなシステムでは、1つの処理を実行するまでに多くの関数やクラスを経由します。
そのため、エラーが発生した場所だけでなく、どの処理から呼び出されてその場所まで到達したのかを確認できるスタックトレースは、原因調査において非常に重要です。
スタックトレースを見るときの注意点
スタックトレースの一番上に表示されるエラー発生箇所が、必ずしも不具合の根本原因とは限りません。
例えば、
showUser()
↓
getUserName()
↓
エラーとなっていたとしても、getUserName()の処理自体ではなく、showUser()から渡された値が間違っていたことが原因かもしれません。
そのため、「エラーが発生した行だけを見る」のではなく、必要に応じて呼び出し元を確認していくことが重要です。
補足
スタックトレースの形式や表示される内容は、JavaScript、Python、Javaなどのプログラミング言語や、実行環境、フレームワークによって異なります。
ただし、「エラーが発生した場所と、そこまでの呼び出し関係を確認する」という基本的な考え方は共通しています。
また、非同期処理を含むプログラムでは、実行環境によってスタックトレースの表示方法や呼び出し関係の見え方が異なる場合があります。
スタックトレースとログの違い
スタックトレースとログは、どちらもプログラムの状態や問題を調べるために使われる情報ですが、役割が異なります。
簡単にいうと、スタックトレースは「エラーがどのような呼び出し経路で発生したのか」を確認するための情報で、ログは「プログラムで何が起きたのか」を記録するための情報です。
| 比較項目 | スタックトレース | ログ |
| 主な目的 | エラーが発生した場所や、そこまでの関数・メソッドの呼び出し関係を確認する | プログラムの処理内容や状態、発生した出来事を記録する |
| 主な内容 | エラー名、エラーメッセージ、関数名、ファイル名、行番号、呼び出し関係など | 処理の開始・終了、ユーザー操作、エラー内容、データの状態など |
| 出力されるタイミング | 主にエラーや例外が発生したとき | プログラムで設定したタイミング |
| 主な用途 | エラーの発生箇所や呼び出し経路の調査 | システムの動作確認、不具合調査、監視など |
例えば、ログに次のような情報が記録されていたとします。
10:00:01 ユーザー情報の取得を開始
10:00:02 ユーザー情報の取得に失敗
10:00:02 Error: ユーザー名を取得できませんこのログを見ることで、「いつ、どの処理で問題が発生したのか」を確認できます。
一方、同じエラーについてスタックトレースを見ると、次のように表示されます。
Error: ユーザー名を取得できません
at getUserName (app.js:10:9)
at showUser (app.js:6:3)
at main (app.js:2:3)
at Object.<anonymous> (app.js:13:1) スタックトレースを見ることで、main()からshowUser()、getUserName()という順番で処理が呼び出され、getUserName()でエラーが発生したことを確認できます。
ログの中にスタックトレースを出力することもあります。そのため、「ログ」と「スタックトレース」は完全に別々のものではなく、ログの一部としてスタックトレースが記録されることもあります。
このように、ログで「何が起きたのか」を確認し、スタックトレースで「どこを通ってエラーが発生したのか」を確認すると考えると、違いを理解しやすいでしょう。
本記事のまとめ
この記事では「スタックトレース」について、以下の内容を説明しました。
- スタックトレースとは、エラー発生時などに関数やメソッドの呼び出し履歴を確認できる情報
- コールスタックは、現在実行している関数や呼び出し元を管理する仕組み
- スタックトレースから、エラーの種類・ファイル名・行番号・呼び出し元などを確認できる
- まずはエラーメッセージと、自分が書いたファイルの行番号を確認するとわかりやすい
- スタックトレース自体がエラーなのではなく、エラーの原因を調査するための情報
- スタックトレースはエラーの発生箇所や呼び出し関係を確認する情報で、ログはプログラムで起きた出来事を記録する情報
スタックトレースは最初は難しく見えますが、すべての行を理解する必要はありません。
まずは「どんなエラーなのか」「自分のプログラムのどこで発生したのか」を確認し、その後、必要に応じて呼び出し元を追っていくと、エラーの原因を見つけやすくなります。
お読みいただきありがとうございました。