C++17例外メッセージ設計とロギングレビュー|診断可能性を高める情報量とレビュー指摘技法
この記事のポイント
- 例外メッセージ設計のレビュー観点を実務レベルで整理
- ロギング方針と例外情報設計の一体化をレビュー指摘可能になる
- 「障害解析できるコードか?」をレビューアーが見抜く視点を学ぶ
そもそも例外メッセージとは何を担うのか
例外メッセージ(what()出力)は、発生した障害の状況・文脈・原因候補を
「開発者以外の運用担当者・障害解析者」に伝える唯一の構造化されていないダンプ情報です。
| 役割 | 具体内容 |
|---|---|
| 発生状況提示 | どの処理で、どんな入力で |
| 原因候補提示 | どの条件違反で発生したか |
| 調査容易化 | 検索しやすいキーワードを埋め込む |
レビューアーは「このメッセージで障害調査できるか?」を常に確認します。
なぜこれをレビューするのか
- メッセージが抽象的だと原因調査不能になる
- ログ上に汎用"Error occurred"だけが並ぶ障害地獄が発生
- 現場エンジニアが再現条件を復元できなくなる
- 特に夜間運用・障害分析現場で致命的被害が出る
レビューアー視点
- 最低限必要なコンテキスト情報が盛り込まれているか
- 静的メッセージと動的値埋込のバランスが妥当か
- ログ出力基準と例外型設計が一貫しているか
- 何度でも検索可能な情報粒度になっているか
- 個人情報・機密情報を漏洩させていないか
開発者視点
what()の静的文+動的補足情報は標準化する- 入力パラメータ値・キー情報は積極出力
- 障害時の再現材料を未来の自分に残す意識で記述
- ログ上の行数制御(出力過剰回避)も考慮
良い実装例
例1:契約違反系(入力値表示)
入力情報を盛り込む
#include <stdexcept>
#include <string>
class InvalidUserIdException : public std::invalid_argument {
public:
explicit InvalidUserIdException(int userId)
: std::invalid_argument("Invalid User ID: " + std::to_string(userId)) {}
};- 単に"invalid argument"ではなく入力値そのものを埋め込む
- ログ検索・障害解析が容易化される
例2:外部系障害(環境情報表示)
接続先情報を埋め込む
#include <stdexcept>
#include <string>
class DatabaseConnectionException : public std::runtime_error {
public:
DatabaseConnectionException(const std::string& dbName, const std::string& host)
: std::runtime_error("DB connection failed - DB: " + dbName + " Host: " + host) {}
};- どのDBのどの接続先で失敗したかが一目瞭然
- インフラ担当が即座に確認できる
レビュー観点
- 入力パラメータ値が出力されているか
- 接続先情報等の環境依存情報が含まれているか
- 機械検索できる粒度の静的文面になっているか
- スタックトレース補助となる情報が盛り込まれているか
- what()の冗長化は適切に抑制されているか
良くない実装例: ケース1(抽象化しすぎ)
抽象メッセージのみ
#include <stdexcept>
#include <string>
class InvalidUserIdException : public std::invalid_argument {
public:
InvalidUserIdException() : std::invalid_argument("Invalid User ID") {}
@Reviewer入力値が含まれていません。どの値で失敗したか分かるようuserIdを含めてください。};改善例
改善例(入力値埋込)
InvalidUserIdException(int userId)
: std::invalid_argument("Invalid User ID: " + std::to_string(userId)) {}良くない実装例: ケース2(ログ用情報未統合)
例外とログが分断されている悪例
void saveUser(int userId) {
if (userId <= 0) {
logError("Invalid ID detected");
throw std::invalid_argument("Invalid User ID");
}
}
@Reviewerログメッセージと例外メッセージが統一されていません。診断性向上のため例外メッセージに値埋込し統一してください。
改善例
改善例(例外メッセージ内に情報統合)
void saveUser(int userId) {
if (userId <= 0) {
throw std::invalid_argument("Invalid User ID: " + std::to_string(userId));
}
}良くない実装例: ケース3(機密情報流出)
機密情報誤埋込
#include <stdexcept>
#include <string>
class PaymentFailureException : public std::runtime_error {
public:
PaymentFailureException(const std::string& cardNumber)
: std::runtime_error("Payment failed for card: " + cardNumber) {}
@Reviewerクレジットカード番号をそのまま埋め込むのは情報漏洩リスクです。マスク化処理を挟んでください。};改善例
改善例(マスキング処理)
std::string maskCard(const std::string& card) {
if (card.size() < 4) return "****";
return "****" + card.substr(card.size() - 4);
}
class PaymentFailureException : public std::runtime_error {
public:
PaymentFailureException(const std::string& cardNumber)
: std::runtime_error("Payment failed for card: " + maskCard(cardNumber)) {}
};PlantUML:情報量の設計責務フロー
ロギング設計例
ログ出力例(例外統合)
try {
userService.saveUser(-5);
} catch (const std::exception& e) {
logger.error("Exception occurred: {}", e.what());
}- ログ側で例外情報はwhat()に統一依存させる方針
- 各層のログフォーマット基準と一貫統合
観点チェックリスト
まとめ
例外メッセージレビューの核心は
「このログ1行から障害再現できるか?」
という診断可能性評価です。
- 動的値埋込は必須
- ログ設計と統合管理
- 抽象エラーメッセージ禁止
- 個人情報保護意識
レビューアーは「未来の障害解析者の立場」でコードを読み、情報不足を具体的に指摘する習慣を持つことが重要です。
