こんにちは、さるまりんです 🐒🔧
Javaで開発をしていると、例外処理は避けて通れません。
前回は「try-catchは設計の話」というテーマで、
どこで例外を扱うべきかを整理しました。
では、その次に考えるべきことは何でしょうか?
それは、
「何が起きたのかを、あとから分かるようにすること」
です。
そのために必要なのが、ログ設計です。
ログは未来の自分へのメッセージ
ログはただ出せばいいものではありません。
本当によく思います。
- エラーが起きたことを知る
- 何が原因かを追える
- 他の人でも調査できる
つまりログは、
未来の自分やチームへのメッセージ
です。
よくある落とし穴
最初はここから始まります。
こんなコード、見たことありませんか?
try {
service.execute();
} catch (Exception e) {
e.printStackTrace();
}
これでもエラーは出力されますが、
- ログとして管理されない
- 検索できない
- 環境によっては消える
という問題があります。
さらに、
catch (Exception e) {
logger.error("エラー発生");
}
これもよくあるのですが、
👉 情報が足りません
「なんのエラーよ?」ってなります。
ログに残すべき情報
最低限、次の情報は残しておきたいです。
- 何が起きたか(メッセージ)
- どの処理か
- 入力情報(必要な範囲で)
- 例外そのもの(stack trace)
try {
service.execute(request);
} catch (Exception e) {
logger.error("会員連携処理で例外が発生しました。requestId={}", requestId, e);
throw e;
}
👉 ポイントは 例外オブジェクトをそのまま渡すこと
これでstack traceも含めて記録されます。
ログレベルの考え方
ログにはレベルがあります。
- INFO:通常の処理
- WARN:想定内だが注意が必要
- ERROR:明確な問題
Loggingライブラリやツールによっては、
- FATAL(致命的な問題)
- TRACE(詳細な追跡用)
などもあります。
重要なのは、
👉 全部ERRORにしないこと
ERRORが多すぎると、本当に重要な問題が埋もれます。
どこでログを出すべきか?
ここは設計と深く関係する部分です。
基本は:
👉 例外を処理する場所でログを出す
- 途中でcatchして握りつぶさない
- 最終的にユーザーに返す直前で記録する
前回の記事ともつながるポイントです。
通知はログの代わりではない
ログを整えたうえで、必要なら通知を行います。
記録するだけでなく、「気づける」ことも大切ですよね。
ここで重要なのは:
👉 すべての例外を通知しないこと
です。
通知は、
- サービス停止レベル
- 重要な業務エラー
などに限定します。
やたらめったら通知していると、それ自体がうるさくなり、
本当に大切な通知を見逃してしまうこともあります。
JavaからSlackに障害通知する
以前、PHPでSlackに通知する方法を書きました。
同じように、JavaでもWebhookを使って通知できます。
ただし、
👉 通知は「短く」「分かりやすく」
が大切です。
サンプルコード
try {
service.execute(request);
} catch (Exception e) {
logger.error("会員連携処理で例外が発生しました。requestId={}", requestId, e);
slackNotifier.notifyError(
"会員連携処理で障害が発生しました",
requestId,
e
);
throw e;
}
通知メソッド
public void notifyError(String title, String requestId, Exception e) {
String text = """
%s
requestId: %s
exception: %s
message: %s
""".formatted(
title,
requestId,
e.getClass().getName(),
e.getMessage()
);
send(text);
}
stack traceはそのまま貼らない
stack traceをそのまま通知に載せると、
- 長すぎる
- 読みにくい
- 本質が見えない
という問題があります。
そこでおすすめなのが、
👉 自分のコード部分だけを抜き出す
private String extractApplicationStackTrace(Throwable e, String packagePrefix) {
return Arrays.stream(e.getStackTrace())
.filter(element -> element.getClassName().startsWith(packagePrefix))
.limit(5)
.map(StackTraceElement::toString)
.collect(Collectors.joining("\n"));
}
長いstack traceの中でも、
自分たちのコードに集中できると、原因特定がぐっと楽になります。
まとめ
ログは、
- 出すためのものではなく
- あとから分かるためのもの
です。
そして、
- ログで記録する
- 必要なものだけ通知する
このバランスがとても大切です。
例外処理の次に考えるべきは、
「どう残すか」。
開発の段階でここを意識しておくだけで、
運用の負担はぐっと軽くなります。
安全に、安心して、開発も運用も楽しむために。
少しずつ整えていきたいですね。
読んでくださってありがとうございました。
それではまた!