スタックトレース文字列化は「エラーの足跡をテキストとして残す」技
例外が起きたとき、Java は「スタックトレース」という“足跡”を持っています。
これは「どのメソッドからどのメソッドへ呼ばれて、最終的にどこでエラーになったか」を示す、とても重要な情報です。
普段はコンソールに e.printStackTrace() で出力されますが、業務システムでは「ログに文字列として残したい」「外部サービスに送信したい」「画面に整形して表示したい」といったニーズが出てきます。
そのときに必要になるのが「スタックトレースを文字列化するユーティリティ」です。
まずは基本:printStackTrace を String に変える
StringWriter + PrintWriter を使う定番パターン
Java でスタックトレースを文字列にする、一番オーソドックスな方法はこれです。
import java.io.PrintWriter;
import java.io.StringWriter;
public final class StackTraces {
private StackTraces() {}
public static String toString(Throwable t) {
if (t == null) {
return "";
}
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
pw.flush();
return sw.toString();
}
}
Java使い方はとてもシンプルです。
try {
doSomething();
} catch (Exception e) {
String stack = StackTraces.toString(e);
System.out.println(stack);
}
Javaここで押さえておきたいポイントは二つです。
一つ目は、「Throwable#printStackTrace(PrintWriter) を使うと、出力先を自由に差し替えられる」ということです。
通常は標準エラー出力に出ますが、StringWriter をかませることで「文字列として受け取る」ことができます。
二つ目は、「Throwable を受け取るようにしている」ことです。Exception だけでなく、Error も含めて扱えるようにしておくと、ユーティリティとして汎用性が高くなります。
ログフレームワークと組み合わせるときの使いどころ
ログフレームワークは「例外を渡すだけ」でスタックトレースを出してくれる
SLF4J や Logback, Log4j2 などのログフレームワークは、例外オブジェクトを渡すだけでスタックトレースを出力してくれます。
log.error("エラーが発生しました", e);
Javaこの場合、わざわざ文字列化する必要はありません。
ログファイルには、メッセージと一緒にスタックトレースがきれいに出力されます。
つまり、「ログに出すだけなら、スタックトレース文字列化ユーティリティは不要」です。
ここを勘違いして、何でもかんでも toString(e) してログに流すと、逆に見づらくなることもあります。
それでも「文字列化」が必要になる場面
それでもユーティリティが活きるのは、例えばこんな場面です。
監視サービス(SaaS)に JSON でエラー情報を送るとき、スタックトレースを文字列として詰め込みたい。
DB のエラーテーブルに「スタックトレース全文」を文字列として保存したい。
画面の「詳細表示」ボタンを押したときに、スタックトレースをテキストエリアに表示したい。
こういう「ログ以外の場所」にスタックトレースを載せたいときに、StackTraces.toString(e) のようなユーティリティが効いてきます。
実務で使えるスタックトレース文字列化ユーティリティの工夫
最大長を決めて「長すぎるスタックトレース」を切る
スタックトレースは、場合によってはとても長くなります。
それをそのまま DB に入れたり、外部サービスに送ったりすると、サイズ制限に引っかかったり、ログがノイズだらけになったりします。
そこで、「最大長を決めて切り詰める」ユーティリティを用意しておくと実務的です。
public static String toStringLimited(Throwable t, int maxLength) {
String full = toString(t);
if (full.length() <= maxLength) {
return full;
}
return full.substring(0, maxLength) + "\n... (truncated)";
}
Java使い方はこうです。
String stack = StackTraces.toStringLimited(e, 4000);
Javaここで深掘りしたいのは、「保存先・送信先の制約を意識して、スタックトレースを“扱いやすいサイズ”に整える」という発想です。
「全部残したい」気持ちは分かりますが、現実には「4KB まで」「1 万文字まで」といった制約があることが多いので、ユーティリティ側で制御しておくと安全です。
先頭数行だけを抜き出すユーティリティ
「とりあえず原因の場所だけ知りたい」というときは、先頭数行だけを抜き出すのも有効です。
public static String head(Throwable t, int lines) {
String full = toString(t);
String[] split = full.split("\\R"); // 改行で分割
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Math.min(lines, split.length); i++) {
sb.append(split[i]).append(System.lineSeparator());
}
if (split.length > lines) {
sb.append("... (").append(split.length - lines).append(" lines more)");
}
return sb.toString();
}
Java例えばこう使います。
String shortStack = StackTraces.head(e, 5);
Javaこれは、Slack 通知やメール通知など、「全文はうるさいけれど、入口と原因だけ知りたい」場面でとても便利です。
セキュリティとプライバシーの観点も忘れない
スタックトレースは「内部構造の情報の塊」
スタックトレースには、クラス名・メソッド名・パッケージ構成・ファイル名・行番号など、システム内部の情報が大量に含まれています。
これをそのまま外部のユーザーに見せると、「内部構造の暴露」になり、セキュリティリスクになることがあります。
例えば、Web アプリでエラーが起きたときに、画面にスタックトレース全文を表示するのは典型的なアンチパターンです。
攻撃者にとっては、「どんなフレームワークを使っているか」「どのライブラリのどのバージョンか」といったヒントになります。
「どこまで誰に見せるか」をユーティリティ側で意識する
スタックトレース文字列化ユーティリティは、「誰に見せるか」を意識して使い分けるべきです。
開発者向けのログ・監視サービス・内部用管理画面:全文または制限付き全文を使う。
一般ユーザー向けの画面:スタックトレースは見せず、「問い合わせ ID」や「簡単なメッセージ」だけを表示する。
ユーティリティ自体は単に文字列を返すだけですが、「どこでどう使うか」のルールをチームで決めておくと、安全な運用につながります。
例外ラップと組み合わせたときのスタックトレースの見え方
ラップしても cause をつなげておけば情報は失われない
前回話した「例外ラップ」と組み合わせるとき、new AppException("...", e) のように cause を渡していれば、
スタックトレース文字列化をしても、ラップされた階層ごとの情報がすべて含まれます。
try {
doIo();
} catch (IOException e) {
throw new AppException("I/O エラー", e);
}
Javaこの AppException を StackTraces.toString(e) すると、
AppException: I/O エラー
at …
Caused by: java.io.IOException: …
at …
という形で、「どこでラップされたか」「元の例外は何か」が全部見えます。
つまり、「ラップしても cause をつなげておけば、スタックトレース文字列化で情報はちゃんと追える」ということです。
まとめ:スタックトレース文字列化で初心者が身につけるべき感覚
スタックトレース文字列化は、「例外の足跡を、ログや外部サービスに載せられる形に変換する」ための小さなユーティリティです。
押さえておきたいポイントは次の通りです。
StringWriter + PrintWriter + Throwable#printStackTrace で、スタックトレースを文字列にできる。
ログフレームワークに渡すだけなら文字列化は不要で、「ログ以外の場所」に載せたいときにユーティリティが活きる。
最大長や先頭行数を制限するメソッドを用意しておくと、DB や通知先の制約に対応しやすい。
スタックトレースは内部情報の塊なので、「誰にどこまで見せるか」を意識して使う。
例外ラップと組み合わせるときは、cause をつなげておけば、文字列化しても情報は失われない。
