では以下に、
「例外階層 × ロギング設計ベストプラクティス」
〜例外種別に応じたログレベル設計・運用テンプレート〜
を、実務教材スタイル(クラス構造+設計思想+コード表+運用方針表) でまとめます。
1. 全体像:例外階層 × ロギング方針の考え方
アプリ全体で例外とログを統一管理する目的は:
- 異常の重要度を可視化(INFO / WARN / ERROR)
- スタックトレースを必要なものだけ残す
- 開発・運用・監視ツール(例:CloudWatch, ELK, Datadog)に正しい粒度で送信
- チーム全員が統一ルールで例外・ログを扱える
基本設計思想:例外階層ツリー
AppException (業務共通例外 - abstract)
├── BusinessException // 業務ロジック上の異常(想定内)
│ ├── ValidationException // 入力・チェック系
│ └── DomainException // 業務ルール違反
│
└── SystemException // システム的異常(想定外)
├── InfrastructureException // DB/外部API/ネットワーク
└── FatalException // 致命的(プログラミングバグ等)
2. ログレベル設計マッピング表
| 例外クラス | 発生原因の性質 | ログレベル | ログ出力方針 |
|---|
ValidationException | 想定内(ユーザー入力ミス) | INFO | UIで通知、スタックトレース不要 |
BusinessException | 想定内(業務ルール違反) | WARN | 原因を業務ログに出力(例:注文在庫なし) |
SystemException | 想定外(システム異常) | ERROR | スタックトレース+アラート通知 |
InfrastructureException | 想定外(外部依存失敗) | ERROR | 再試行・フォールバック設計も検討 |
FatalException | 致命的バグ・NullPointerなど | FATAL | 即時停止、通知システム連携(例:Slack, PagerDuty) |
3. コード構造テンプレート(実務標準)
Exception階層構造(Java実装例)
// 抽象基底クラス(全例外の親)
public abstract class AppException extends RuntimeException {
private final String errorCode;
public AppException(String message, String errorCode) {
super(message);
this.errorCode = errorCode;
}
public String getErrorCode() { return errorCode; }
}
Java
// 想定内の業務例外
public class BusinessException extends AppException {
public BusinessException(String message, String errorCode) {
super(message, errorCode);
}
}
// 想定外のシステム例外
public class SystemException extends AppException {
public SystemException(String message, String errorCode, Throwable cause) {
super(message, errorCode);
initCause(cause);
}
}
Java
4. ロギング設計テンプレート(SLF4J)
業務層での例外ハンドリング例
@Service
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
public void placeOrder(OrderRequest request) {
try {
validate(request);
executeOrder(request);
} catch (ValidationException e) {
log.info("入力エラー: {}", e.getMessage());
throw e; // 想定内 → ログは INFO
} catch (BusinessException e) {
log.warn("業務エラー: {}", e.getMessage());
throw e; // WARN
} catch (SystemException e) {
log.error("システムエラー: {}", e.getMessage(), e);
throw e; // ERROR
} catch (Exception e) {
log.error("致命的エラー: {}", e.getMessage(), e);
throw new FatalException("致命的エラー発生", "E9999", e);
}
}
}
Java
5. 運用テンプレート(アプリ共通)
| 障害レベル | ログ出力 | 通知先 | 対応例 |
|---|
| INFO | ローカル/アクセスログのみ | – | ユーザー操作ログ |
| WARN | ファイル+監視システム | 担当チーム(翌営業日) | 想定内異常 |
| ERROR | 監視システム+Slack/PagerDuty | 当日対応 | システム異常 |
| FATAL | 監視+緊急通知 | 全開発者 | サービス停止級異常 |
6. 実務Tips:例外×ロギングの落とし穴
| アンチパターン | 問題点 | 修正指針 |
|---|
catch(Exception e){} で無視 | 原因を握りつぶす | 最低限ログ出力する |
| 想定内の異常を ERROR で出力 | アラート過多・誤検知 | 業務異常は WARN まで |
| 同じ例外を複数階層でログ出力 | 二重ログ・冗長 | 最上位層でのみログ |
全例外を System.out.println | 運用で解析不可 | ロガー統一 (SLF4J/Logback) |
7. まとめ:例外階層 × ロギング早見表
例外種別 ─┬─> ログレベル ─┬─> 通知優先度
Validation │ INFO │ 低
Business │ WARN │ 中
System │ ERROR │ 高
Infrastructure │ ERROR │ 高
Fatal │ FATAL │ 緊急