この記事のポイント

  • 例外メッセージ設計のレビュー観点を実務レベルで整理
  • ロギング方針と例外情報設計の一体化をレビュー指摘可能になる
  • 「障害解析できるコードか?」をレビューアーが見抜く視点を学ぶ

そもそも例外メッセージとは何を担うのか

例外メッセージ(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:情報量の設計責務フロー

UML Diagram

ロギング設計例

ログ出力例(例外統合)
try {
    userService.saveUser(-5);
} catch (const std::exception& e) {
    logger.error("Exception occurred: {}", e.what());
}
  • ログ側で例外情報はwhat()に統一依存させる方針
  • 各層のログフォーマット基準と一貫統合

観点チェックリスト

まとめ

例外メッセージレビューの核心は
「このログ1行から障害再現できるか?」
という診断可能性評価です。

  • 動的値埋込は必須
  • ログ設計と統合管理
  • 抽象エラーメッセージ禁止
  • 個人情報保護意識

レビューアーは「未来の障害解析者の立場」でコードを読み、情報不足を具体的に指摘する習慣を持つことが重要です。