オフショアに出した成果物を確認していたら例外の間違った使い方をしていたため大きく修正したことがあります。
PHP + laravel ですが、他の言語/フレームワークでも原則は同じです。
間違った例外の使い方の例
こんなコードでした。
例1
例外をキャッチし、ログに出力し、処理結果をboolで返しています。
try {
何か例外が発生するかもしれない処理();
return true;
} catch (\Exception $e) {
Log::error($e->getMessage());
return false;
}
エラーメッセージはログに出力されます。
しかしスタックトレースが出力されません。
メッセージによりエラーが発生していることは分かりますが、発生したファイルや行番号が分からず原因を特定するのに時間がかかります。
例2
コントローラーのコード例です。
try {
何か例外が発生するかもしれない処理();
} catch (\Exception $e) {
return response($e->getMessage(), 500);
}
laravel を含む大抵のフレームワークでは、キャッチされなかった例外はフレームワークの例外処理機構がキャッチし、ログ出力、適したコードでレスポンスを返す、等をよろしくやってくれます。自力でやる必要はありません。
そもそも原則として例外はキャッチしない
例1のように例外に含まれる情報を握りつぶしたり、例2のようにフレームワークが提供している機能と重複してしまったり矛盾した処理を行ってしまいます。
原則として例外はキャッチしてはいけません。
例外をキャッチするべき場合
原則として例外はキャッチしてはいけません。
ただし以下のような場合は別です。目的がある時のみキャッチしましょう。
後処理が必要な場合
例えばトランザクションロールバックなど、例外発生時に後処理が必要な場合。
こういった場合は例外をキャッチするのが有効です。ただしキャッチした例外は再スローしましょう。
DB::beginTransaction();
try {
何か例外が発生するかもしれない処理();
DB::commit();
} catch (\Throwable $e) {
DB::rollback(); // 必要な後処理
throw $e; // 再スローしましょう
}
エラーが発生しても処理を続けたい場合
例えば独立した処理対象が複数あり、どれか一つが失敗した時でも後続の処理は続行させたい場合。
ただし続行させるとしても何らかのメッセージはログに出力しましょう。
foreach ($items as $item) {
try {
何か例外が発生するかもしれない処理();
} catch (\Throwable $e) {
Log::warn("エラーが発生しました。処理は続行します。");
}
}
エラーの関連情報を付与したい場合
例えば処理中の要素のIDをエラーメッセージに含めたい、などエラー情報に何らかを付与したい場合。
付与したい情報を含め再スローしましょう。
try {
何か例外が発生するかもしれない処理();
} catch (\Throwable $e);
throw new \Exception("エラーが発生しました。処理id:{$execution_id}", 0, $e);
}