try-catchとログを組み合わせてエラー調査をしよう|Spring Bootの例外処理とログ出力を初心者向けに解説
新人
「Spring Bootのアプリでエラーが出たときに、どこで失敗したのか分からなくて困るんです……。print文で確認しても情報が少なくて。」
先輩
「それなら、try-catchとLoggerを組み合わせて使う方法を覚えるといいよ。Spring Bootでは、エラーをログに記録しておけば、原因の特定がずっと簡単になるんだ。」
新人
「try-catchは知っていますが、Loggerってどう使うんですか?System.out.printlnとは違うんですか?」
先輩
「System.out.printlnは単にコンソールに出すだけだけど、Loggerはレベル別に出力を管理できるんだ。これを使えば、本番環境でも安全にログを残せるよ。」
1. try-catchでエラーを捕まえる基本
JavaやSpring Bootでプログラムを実行していると、意図しない例外(エラー)が発生することがあります。例えば、ファイルの読み込みに失敗したり、データベース接続に問題が起きたりするケースです。こうしたときにアプリケーションが強制終了しないようにするのがtry-catch構文です。
tryブロックには「通常の処理」、catchブロックには「エラーが起きたときの処理」を書きます。Spring Bootのコントローラでも同じように利用できます。
@Controller
public class SampleController {
@GetMapping("/error-test")
public String errorTest() {
try {
int result = 10 / 0; // わざとエラーを発生させる
return "success";
} catch (Exception e) {
System.out.println("エラーが発生しました: " + e.getMessage());
return "error";
}
}
}
このコードでは、ゼロ除算の例外が発生したときに、catchブロックでエラーメッセージを出力しています。ただし、System.out.printlnだと、ログレベルの管理ができず、本番環境でのエラーログの確認には不便です。ここで、Loggerを使うと、より実用的なログ出力ができます。
Spring Bootの例外処理では、このようにtry-catchを組み合わせておくことで、アプリの異常を安全に検知し、開発中のデバッグや本番環境での障害調査にも役立ちます。
また、try-catchは複数の例外を区別して処理することも可能です。たとえば、IOExceptionやNumberFormatExceptionなどを個別にキャッチすれば、より詳細なエラー対応ができます。
try {
int num = Integer.parseInt("abc");
} catch (NumberFormatException e) {
System.out.println("数値変換に失敗しました: " + e.getMessage());
} catch (Exception e) {
System.out.println("その他の例外が発生しました: " + e.getMessage());
}
このように、エラーの種類に応じて複数のcatchブロックを用意しておくことで、後から原因を特定しやすくなります。Spring Bootのコントローラにおいても同じ考え方が使えます。
2. Loggerを使って例外を記録する方法
Spring Bootでは、標準でSLF4J(Simple Logging Facade for Java)という仕組みを利用してログを扱います。Loggerを使うことで、例外をレベル別(INFO、WARN、ERRORなど)に出力でき、システム全体の動作を追跡しやすくなります。
pleiades環境で新規にGradleプロジェクトを作成すると、自動的にspring-boot-starter-loggingが依存関係に追加されています。つまり、特別な設定をしなくてもすぐにLoggerが使える状態になっています。
次のコードは、Spring Bootのコントローラ内でLoggerを使って例外を記録するサンプルです。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LogController {
private static final Logger logger = LoggerFactory.getLogger(LogController.class);
@GetMapping("/log-test")
public String logTest() {
try {
int result = 10 / 0; // 例外を発生させる
return "success";
} catch (Exception e) {
logger.error("例外が発生しました: {}", e.getMessage(), e);
return "error";
}
}
}
このコードでは、LoggerFactoryからクラス専用のLoggerを取得しています。そして、例外発生時にはlogger.error()でメッセージを出力しています。logger.error()の第2引数にExceptionオブジェクトを渡すことで、スタックトレース(エラーの発生箇所)が自動的に出力されます。
実際にSpring Bootアプリをpleiades上で実行すると、コンソールに次のようなログが表示されます。
2025-10-25 12:45:32.154 ERROR 12345 --- [nio-8080-exec-1] c.e.demo.controller.LogController : 例外が発生しました: / by zero
java.lang.ArithmeticException: / by zero
at com.example.demo.controller.LogController.logTest(LogController.java:14)
このように、Loggerを使うとエラーの原因となったクラス名や行番号、スレッド情報まで自動的に表示されます。これはSpring Bootの例外処理を学ぶ上で非常に重要なポイントです。
さらに、application.propertiesでログレベルを設定すれば、開発中と本番環境で異なる出力を使い分けられます。
# ログレベルをINFOに設定(本番向け)
logging.level.root=INFO
# 特定のパッケージだけDEBUGに設定(開発向け)
logging.level.com.example.demo.controller=DEBUG
この設定により、開発中は詳細なデバッグ情報を確認し、本番では必要最低限のログだけを残すことができます。これがSpring Bootでのログ出力設計の基本です。
try-catchとLoggerを組み合わせることで、単なるエラー回避にとどまらず、「エラーの可視化」や「原因追跡」ができるようになります。特に、複数のコントローラやサービス層が関わる大規模なアプリでは、ログ出力がトラブルシューティングの命綱になります。
3. コントローラでtry-catchを使ってエラーをログ出力する実践例
ここからは、実際にSpring Bootの@Controllerクラス内でtry-catchとLoggerを組み合わせた実践的なコード例を見ていきましょう。pleiades環境でGradleプロジェクトを作成している前提なので、追加のライブラリ導入は不要です。Spring Bootには標準でログ機能(SLF4JとLogback)が統合されています。
下記のサンプルでは、ファイルの読み込み処理を行うメソッドを用意し、ファイルが存在しない場合に例外をキャッチしてログを出力します。これにより、エラー発生箇所や原因を簡単に調査できるようになります。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
@Controller
public class FileReadController {
private static final Logger logger = LoggerFactory.getLogger(FileReadController.class);
@GetMapping("/read-file")
public String readFile() {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("sample.txt"));
String line = reader.readLine();
logger.info("ファイルの最初の行: {}", line);
return "success";
} catch (IOException e) {
logger.error("ファイルの読み込み中にエラーが発生しました: {}", e.getMessage(), e);
return "error";
} finally {
try {
if (reader != null) reader.close();
} catch (IOException e) {
logger.warn("リソース解放時にエラーが発生しました: {}", e.getMessage());
}
}
}
}
このコードでは、tryブロック内でファイル読み込み処理を行い、catchでIOExceptionをキャッチしています。例外発生時には、logger.error()でエラーメッセージを出力し、さらに例外オブジェクトeを渡すことでスタックトレースも記録します。
Spring Bootのコンソールには、次のようなログが表示されます。
2025-10-25 15:22:11.018 ERROR 4321 --- [nio-8080-exec-1] c.e.demo.controller.FileReadController : ファイルの読み込み中にエラーが発生しました: sample.txt (No such file or directory)
java.io.FileNotFoundException: sample.txt (No such file or directory)
at com.example.demo.controller.FileReadController.readFile(FileReadController.java:17)
このように、エラー内容だけでなく、例外が発生した行番号やクラス名まで出力されるため、エラーの原因をすぐに特定できます。これがSpring Boot エラーログ設計の基本であり、開発効率の向上に欠かせません。
4. logger.error()で例外メッセージを出力するポイント
logger.error()を使うときには、単に文字列を出力するだけでなく、メッセージと一緒に例外オブジェクトを渡すのがポイントです。これによって、詳細なスタックトレースが自動的にログへ出力されます。初心者がやりがちなミスは、e.printStackTrace()を使ってしまうことですが、これは開発中のデバッグには使えても、本番環境では適していません。
Spring Bootのログフレームワーク(Logback)は、ログレベルを柔軟に管理できます。たとえば、logger.error()は致命的なエラーを、logger.warn()は注意が必要な事象を、logger.info()は通常の情報を出力します。このように使い分けることで、ログファイルを読みやすく整理できます。
次のサンプルでは、NumberFormatExceptionをキャッチしてlogger.error()で出力しています。
@Controller
public class NumberController {
private static final Logger logger = LoggerFactory.getLogger(NumberController.class);
@GetMapping("/convert")
public String convertNumber() {
try {
int num = Integer.parseInt("abc");
return "success";
} catch (NumberFormatException e) {
logger.error("数値変換に失敗しました: {}", e.getMessage(), e);
return "error";
}
}
}
このコードをpleiadesで実行すると、コンソールに「数値変換に失敗しました」というログが赤色(ERRORレベル)で表示され、どのクラスの何行目で問題が起きたかもわかります。Spring Bootのlogger.error 出力例を確認することで、アプリの不具合を効率的に調べられます。
また、プロジェクト全体で統一したログフォーマットを設定しておくと便利です。例えば、application.propertiesに次の設定を追加します。
# 日付、レベル、スレッド、クラス名を含むログフォーマット
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} : %msg%n
これにより、Spring Bootのアプリ全体で統一されたフォーマットでログが出力されるようになります。どのクラスで何が起きたのかが一目でわかり、チーム開発や運用時にも非常に役立ちます。
5. finallyでリソースを解放するときの注意点
例外処理では、tryとcatchだけでなく、finallyブロックも重要な役割を持ちます。finallyは、例外が発生しても必ず実行される部分であり、主にリソースの解放処理を記述します。たとえば、ファイルストリームやデータベース接続などを開いたままにすると、メモリリークの原因になります。
次のコードは、ファイル読み込み後にfinallyでリソースを確実に閉じる例です。
@Controller
public class FinallyController {
private static final Logger logger = LoggerFactory.getLogger(FinallyController.class);
@GetMapping("/finally-test")
public String testFinally() {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("data.txt"));
String line = reader.readLine();
logger.info("読み込んだ内容: {}", line);
} catch (IOException e) {
logger.error("ファイル処理中にエラーが発生しました: {}", e.getMessage(), e);
} finally {
try {
if (reader != null) {
reader.close();
logger.info("リソースを正常に解放しました。");
}
} catch (IOException e) {
logger.warn("リソース解放時に問題が発生しました: {}", e.getMessage());
}
}
return "done";
}
}
このようにfinallyブロックを利用することで、例外の有無に関わらず必ずリソースを閉じることが保証されます。これはSpring Boot 例外処理における基本的な安全対策です。実際のアプリケーション開発では、ファイル・ストリーム・データベースなど外部リソースを扱う箇所において欠かせません。
なお、Java 7以降ではtry-with-resources構文も利用でき、より簡潔にリソースを扱えます。ただし、Spring Boot初心者が最初に学ぶべきはtry-catch-finallyの流れを理解することです。これをしっかり習得すれば、将来的に複雑なトランザクション処理やサービス層の例外設計を行う際にも応用できます。
以上のように、try-catchとLoggerを組み合わせることで、開発中のデバッグから本番運用まで、Spring Bootアプリケーションの安定性を高めることができます。エラー発生時の挙動を把握し、適切なログ出力を行うことは、プログラミングにおける基礎的かつ重要なスキルです。
6. application.propertiesでログレベルを調整してデバッグを効率化
Spring Bootでは、application.propertiesファイルを使ってログレベルを簡単に調整できます。これにより、開発中と本番環境で出力する情報の詳細度を切り替えられます。特に、DEBUGやTRACEなどのレベルを設定することで、内部処理の流れを確認しやすくなります。
ログレベルには、TRACE、DEBUG、INFO、WARN、ERRORなどの段階があります。開発初期はDEBUGを使うと詳細な情報を確認でき、本番ではINFOやERRORに設定して不要なログを減らすのが一般的です。
設定方法は非常にシンプルで、次のようにapplication.propertiesに記述します。
# 全体のログレベルをDEBUGに設定
logging.level.root=DEBUG
# 特定パッケージだけ詳細表示
logging.level.com.example.demo.controller=TRACE
この設定をすると、指定したクラスやパッケージだけ詳細なログを出力するようになります。たとえば、コントローラでのメソッド呼び出し順やパラメータ値を確認したい場合、対象パッケージをTRACEに設定しておくと便利です。
pleiadesで実行すると、コンソールに次のようなログが出力されます。
2025-10-25 18:42:17.981 DEBUG 5678 --- [nio-8080-exec-1] c.e.demo.controller.LogController : メソッドが呼び出されました。
2025-10-25 18:42:17.983 TRACE 5678 --- [nio-8080-exec-1] c.e.demo.controller.LogController : 変数 result = 0
このように、Spring Bootのログレベル設定を使うことで、try-catch デバッグ方法をより効率化できます。特に、原因不明の例外を追跡するときにTRACEを一時的に有効化すると、内部処理の流れを詳細に確認できます。
なお、ログの出力先をファイルに変更することも可能です。例えば、次のように設定します。
# ログをファイルにも出力する
logging.file.name=app.log
logging.file.path=logs/
この設定を追加すると、logsディレクトリにapp.logファイルが作成され、コンソール出力と同じ内容が記録されます。本番環境では、ログファイルに記録しておくことで、あとからエラー調査を行いやすくなります。
7. よくあるエラーとログでの確認ポイント
Spring Bootアプリを開発していると、例外の種類によって対処方法や確認箇所が異なります。ここでは代表的なエラーと、それをログで確認するポイントを紹介します。
① NullPointerException
最もよくあるエラーです。オブジェクトがnullの状態でメソッドを呼び出したときに発生します。ログには「NullPointerException」と表示され、該当箇所の行番号も出力されます。
java.lang.NullPointerException: Cannot invoke "String.length()" because "name" is null
at com.example.demo.controller.UserController.getUser(UserController.java:22)
このようなログが出た場合、該当クラスと行番号を確認し、変数に値が入っているかをチェックします。特に、@Autowiredの依存注入漏れや、フォームからの入力値が欠けている場合に発生しやすいです。
② NumberFormatException
数値変換エラーです。Integer.parseInt()などで文字列を数値に変換できないときに起きます。ログには次のように表示されます。
java.lang.NumberFormatException: For input string: "abc"
at com.example.demo.controller.NumberController.convertNumber(NumberController.java:15)
この場合は、ユーザー入力値が数値かどうかをチェックし、try-catchでエラーメッセージをログに残しておきましょう。Spring Boot ログ出力を活用すると、原因をすぐに突き止められます。
③ FileNotFoundException
ファイルが存在しないときに発生するエラーです。特に、リソースファイルのパス指定ミスが原因でよく起こります。
java.io.FileNotFoundException: sample.txt (No such file or directory)
at com.example.demo.controller.FileReadController.readFile(FileReadController.java:18)
ログにファイル名が明示されるため、対象ファイルの配置場所を確認します。これもlogger.error()で記録しておくことで、後から調査が容易になります。
これらの例外を確認する際には、常に「例外の種類」「メッセージ」「行番号」「スレッド名」の4点をチェックする習慣をつけましょう。Spring Bootの例外ログは、これらの情報をすべて出力してくれるため、調査の手間を大きく減らせます。
8. 例外のスタックトレースを読み解くコツ
初心者が最初につまずきやすいのが、「スタックトレースの読み方」です。スタックトレースとは、例外発生時にどのクラス・メソッド・行でエラーが起きたかを示す履歴のことです。Spring Bootのログには自動的にスタックトレースが出力されます。
java.lang.ArithmeticException: / by zero
at com.example.demo.controller.LogController.logTest(LogController.java:15)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
この中で注目すべきは、最初の数行です。特に「自分のクラス名」と「行番号」が含まれている箇所を確認します。上記の例なら、LogController.java:15が問題箇所です。Spring Bootの内部処理(org.springframework...)の行は無視して構いません。
スタックトレースは上から下へ読むのではなく、最初の1行目と最上位の「自分のクラス」部分を優先して確認します。エラー原因はそこにあります。たとえば、算術エラーや変換エラーなら最初の例外メッセージにヒントが出ています。
また、スタックトレースをlogger.error()で出力しておくことで、コンソールだけでなくログファイルにも記録され、あとから運用担当者が確認できます。これは、システム運用時に非常に重要なポイントです。
次のようにlogger.error("例外発生", e);の形式で出力すれば、メッセージとスタックトレースをまとめて出せます。
catch (Exception e) {
logger.error("例外発生", e);
}
この書き方を覚えておけば、開発中のデバッグや本番障害対応で大きく役立ちます。特に、try-catch デバッグ方法を習得しておくと、原因不明のバグにも落ち着いて対応できるようになります。
さらに、application.propertiesでlogging.levelを調整すれば、必要な情報だけを抽出できます。たとえば、Spring内部の詳細なログを一時的に有効にすることで、エラー原因をより深く分析できます。
スタックトレースを読めるようになると、Spring Bootの例外処理全体を理解しやすくなります。単にエラーを解消するだけでなく、「なぜ起きたのか」を分析できるようになるのです。これが、プログラマーとしての成長に直結するスキルです。
最終的に、try-catchとLoggerを適切に使い分け、ログレベル設定を活用できるようになれば、Spring Bootのエラー調査は格段に効率化します。pleiades環境でもGradle構成でも同じように設定できるため、初心者のうちから正しいログ出力習慣を身につけておきましょう。