Error Code Design in Java System

1. よいエラーメッセージとは

優れたエラーメッセージは主に 3 つの部分で構成されます。

コンテキスト:何がエラーの原因となったか。エラー発生時にコードは何を行おうとしていたか。
エラー自体:具体的に何が失敗の原因となったか。具体的な理由と当時のデータは何か。
対応策:このエラーを解決するための方法は何か。ソリューションとも理解できます。
まだ少し抽象的に聞こえるかもしれません。コードの例を示しましょう。たまたま jdoctor というプロジェクトがあり、作成者は Labs[1] の出身です。サンプルコードは以下の通りです。

ここでの Problem は Error と同じ意味で理解してください。コアには主に以下のフィールドが含まれます。

context:アプリケーション名、コンポーネント、ステータスコードなど、現在のコンテキストを文字列で記述します。たとえば、アプリケーション名 + コンポーネント名 + 具体的なエラーのステータスコードなどです。もちろん、JSON 文字列でも構いません。例:{ "app": "uic", "component": "login", "code": "111"}

description:エラーの説明を Long(Short)の形式で記述します。Long と Short の両方の説明を含みます。
because/reason:データを用いてエラーの原因を詳細に説明します。もちろん、対応するデータも含める必要があります。

documentedAt:エラーに対応する HTTP リンクで、エラーについてより詳しく説明しています。
solutions:考えられる解決策です。たとえば、メールアドレスのスペルが正しいか、SMS のパスコードが正しく入力されているかを確認するよう促すなどです。

これらの具体的なフィールドがあれば、理解がずっと容易になります。

2. エラーコードの設計

さまざまなエラー処理にエラーコードを使用することを推奨します。エラーコードには多くの利点があります。一意性、検索や統計がより便利などです。そのため、エラーコードの設計について議論する必要があります。インターネット上にも多くのエラーコード設計の仕様があります。もちろん、この記事でも独自のアプローチを示します。参考としてご利用ください。ご自身で判断していただいて構いません。コメントも歓迎します。

エラーコードは通常 3 つの部分で構成されます。

システム/アプリケーション略称:システムまたはアプリケーションの名前です。RST、OSS などです。Jira に熟悉している方であれば、ほぼ同じ仕様です。Java プログラマーなら HHH や SPR が何を表すかご存じでしょう。

コンポーネント略称またはコード:システム内のコンポーネントの名前またはコードです。LOGIN、AUDIT、001 などです。エラーの特定が便利かつ迅速になります。

ステータスコード:3 桁のステータスコードです。200、404、500 など、主に HTTP Status Code から借用しています。ほとんどの開発者は HTTP ステータスコードを理解しているため、新たに設計する必要がありません。

上記の仕様を踏まえて、典型的なエラーコードがどのようなものか見てみましょう。

OSS-001-404:OSS のあるコンポーネントでリソースが見つからなかったことが分かります。
RST-002-500:あるコンポーネントの内部エラーです。
UIC-LOGIN-404:メンバーログイン時に指定されたアカウントが見つからないケースです。
アプリケーション名の略称、コンポーネント名またはコード、ステータス値を使用し、ダッシュで接続します。アンダースコアは読みやすく、表示時にスペースとして解釈されることもあります。同時に、標準的な HTTP Status Code のサポートにより、ドキュメントを参照しなくてもおおよその意味を推測できます。エラーコードの設計は複雑にしすぎないでください。すべての情報を詰め込もうとすると、情報は非常に包括的になりますが、開発者の理解と使用のコストも増加します。これはトレードオフかもしれません。もちろん、現在の 3 ボタン(報酬、いいね、転送)の構造が最も合理的だと言っているわけではありません。調整も可能です。心理学的な研究をされた方で、この 3 分割方式が人間の認知習慣に最も合っているかどうか教えていただけませんか。4 や 5 のように 3 つ以上の要素がある場合、人々が覚えて使用できる確率は大きく下がるのでしょうか。

前述のエラーのコンテキストを覚えていますか。ここでのエラーコードは、実はコンテキストを開始する機能を持っています。たとえば UIC-LOGIN-404 では、どこでエラーが発生したか。エラーコードが特定に役立ちます。当時コードは何をしようとしていたか。エラーコードが説明しています。エラーコードはエラーのコンテキストを完全には表現できませんが、それが持つ情報は当時のコンテキストを理解するのに十分です。したがって、ここでのエラーコードはコンテキストの役割を果たしています。少なくとも、ProblemBuilder.newBuilder(TestProblemId.ERROR1, StandardSeverity.ERROR, "Hawaiian pizza") のようにコンテキストとして「Hawaiian pizza」と書くよりは、エラーコードの方が説得力があり、標準化されています。

3. エラーメッセージの記述フォーマット

エラーコードを設計した後、エラーコード + 短いメッセージだけで出力するわけにはいきません。さもなければ ORA-00942: table or view does not exist のような事態になり、「どのテーブルやビューなのか教えてくれ」と不満に思うでしょう。そこで、すべてのエラーのコンテキスト、説明、原因、ドキュメントリンク、ソリューションを含めることができるメッセージフォーマットを設計する必要があります。これにより開発者にとってより親切になります。ここで Message 仕様を策定しました。もちろん、皆さんがご自身の意見を表明していただいて構いません。以下の通りです。

long description(short desc): because/reason --- document link -- solutions
説明:

エラーの長い説明を直接記述し、短い説明を括弧で囲みます。この記述方法は契約書などで非常に一般的です。たとえば、阿里巴巴雲計算有限公司(Alibaba Cloud)のように、労働契約にサインする際、会社の肩書は基本的に「正式名称(略称)」の形式です。多くの開発者はエラーログに「login failure」と書きますが、ログインシステムには複数のログイン方式があるため、「Failed to log in with email and password(Login Failed)」「Failed to log in with phone and passcode(Login Failed)」「Failed to log in with oauth2(Login Failed)」の方がはるかに明確です。

エラーの具体的な原因:コロンの後に詳細な原因を記述します。たとえば email user@example.com not found、gender field is not allowed in package.json のように、具体的なデータ情報を含める必要があります。入力値を含む場合もあります。労働契約に例えると、タイトルの後に具体的な職種と給与が続きます。契約はフォーマット化されていますが、各人の具体的な職種と給与は異なり、これらのパラメータは外部から取得されます。ここでセキュリティに詳しい方から、データの脱感作はどうするのかという質問が出ます。これは別の問題です。ほとんどの開発者はマスキングの方法を知っているはずなので、ここでは省略します。労働紛争のエラーが発生した際、具体的な原因の中のデータ(職種や給与など)があれば、労働仲裁局がすばやく「エラー」を特定し解決できます。

document link:次に 3 つのダッシュ --- で区切って、対応するエラーリンクを入力します。3 本のダッシュは多くのシーンで区切り文字として使用されています。mdx、yaml などです。馴染みがあるでしょう。リンクがなければ無視してください。

solutions:自然な文章で明確に説明すれば十分です。これも 3 つのアンダースコアの後に配置します。
具体的なメッセージフォーマットの例を見てみましょう。

上記の APP-100-400 エラーコードに対応する説明は、jdoctor に必要な情報をほぼ網羅しています。エラーの説明は非常に完全であるべきで、一定のフォーマットを持つことで、後続のログ分析も容易になります。

4. エラーコード + メッセージの組み立てと保存

エラーコードとメッセージの仕様が決まったら、次にこの情報をどう保存すべきでしょうか。Java の場合、対応する ErrorEnum を作成し、さらにいくつかの POJO を作る必要があるでしょうか。ここでは個人的にプロパティファイルを使用してエラーコードとメッセージ情報を保存することを推奨します。ファイル名は直接 ErrorMessages.properties とし、もちろん特定のパッケージ配下に配置します。ファイルの例は以下の通りです。

なぜプロパティファイルでエラーコードとメッセージ情報を保存するかというと、主に以下の理由があります。

国際化サポート:Java の開発者ならご存知の通り、エラーメッセージを中国語に調整したい場合は、ErrorMessages-zh_CN.properties を作成すればよいだけです。原文ではエラーメッセージのローカライズは推奨されていませんが、中国のほとんどのプログラマーは英語で十分に表現できないことを考慮すると、中国語でも構いません。余談ですが、中国のプログラマーが英語で記事を明確に読み、考えや意見を表現できるようになれば、コンピューター技術のレベルはさらに引き上げられるかもしれません。

さまざまな言語がプロパティファイルの解析をサポートしています。Java だけでなく他の言語でも使用でき、プロパティファイル自体は複雑ではないため、Node.js や Rust などの言語でも使用できます。Java の enum や POJO では基本的に不可能です。

プロパティファイルのフォーマットは豊富です。コメント、改行、マルチラインエスケープなども問題ありません。

最後に最も重要なのは、IDE のサポートが非常に充実していることです。Java 開発者が使用する IntelliJ IDEA では、プロパティファイルのサポートが極めて充実しています。以下の通りです。

エラーコードの自動補完

クイックプレビュー:マウスを合わせる、CMD キーを押しながらマウスを合わせる、Alt+Space も使用可能です。もちろん、クリックで直接移動できます。

リファクタリングと検索のサポート:エラーコードは文字列ですが、プロパティのキーでもあるため、エラーコードの名前を変更すれば、すべての参照箇所がリネームされます。使用箇所の検索もサポートされており、エラーコードがどこで参照されているか確認でき、非常に便利です。もちろん、システム内で使用されていないエラーコードはグレーアウト表示されます。

折りたたみ時の自動表示機能:コードが折りたたまれた状態のとき、IDEA が直接メッセージを取得して表示します。コードレビュー時に非常に便利で、コードの理解も容易になります。

メッセージの値を直接修正

要するに、IntelliJ IDEA はプロパティファイルをサポートし尽くしており、開発者エクスペリエンスの問題を考慮しない理由はありません。あちこち飛び回ってエラーコードを探すような、プログラマーの開発エクスペリエンスを損なうことはすべきではありません。もちろん、WebStorm など他の JetBrains の IDE もプロパティファイルの編集サポートを備えています。

5. コード実装

かなり便利な機能に見えますが、このようなエラー管理用の開発キットを導入する必要があるでしょうか。いいえ、わずか 10 行のコードで実現できます。以下の通りです。

このように、任意の場所でエラーメッセージを出力したい場合、log.info(AppErrorMessages.message("APP-100-400","xxx")); とするだけです。ログをラップしたい場合、たとえば log.info("APP-100-400","xxx"); のようにしても問題ありません。サンプルコードは以下の通りです。

これでログにエラーコードを直接組み込むことができ、非常に便利です。上記のコードは記述済みです。プロジェクトアドレスは記事の末尾を参照してください。

最終的なログ出力は以下の通りです。


補足:ここでは slf4j の MessageFormatter を使用しています。主に後続の Slf4j との統合を容易にするためです。slf4j の MessageFormatter はフォールトトレランスとパフォーマンスの面で Java の MessageFormat よりも優れています。

6. FAQ

1. なぜ 3 桁の HTTP ステータスコードをエラーステータスコードとして選択するのか
ほとんどの開発者は HTTP ステータスコードに熟悉しているため、これらのコードを見ればおおよその意味を理解できます。もちろん、アプリケーション開発者には厳密な要件があります。404 をデータベース接続エラーのような内部エラーとして解釈してはいけません。通常の考え方に反することはしないでください。HTTP ステータスコードの分類は以下の通りです。HTTP Status Codes Cheat Sheet[2] も参照できます。

ただし、エラーステータスコードは HTTP ステータスコードに限定されません。SMTP、POP3 などのステータスコードも参照でき、007 や 777 などのコードも選択可能です。合理的に説明できれば問題ありません。

日常生活では、特別な意味を持つ数字や数字の語呂合わせを使用します。以下にいくつかの注意点を示します。

UIC-LOGIN-666:非常にスムーズで完璧なログイン。ただし、チームに欧米の外国人がいる場合、悪意のあるログインと解釈され、ログイン失敗とみなされる可能性があります。

APP-LOGIN-062:チームに杭州の地元出身者がいる場合、62 の番号は使用しないでください。
APP-001-013:エラーコードをエンドユーザーに透過的に送信する場合、13 の番号は使用しないでください。不快感を与える可能性があります。

520、886、999、95 などの特別な意味や語呂合わせを持つ数字は、適切に使用すれば理解しやすく、親しみやすいものになります。たとえば、登録成功時に UIC-REG-200 を透過的に送信する場合、UIC-REG-520 に調整するとより温かみがあるかもしれません。一般的に、これらの数字を使用する際はシナリオに注意する必要があります。もちろん、HTTP や SMTP などで設計されたステータスコードを参照する方が安全です。

2. プロパティファイルでエラーコードとメッセージを保存するのは、本当に enum や POJO よりも優れているのか
Java と IntelliJ IDEA のサポートに関して言えば、現在の連携は比較的優れています。i18n、メンテナンスコストなどです。これらの ErrorMessages.properties を中央リポジトリに提出して、エラーコードを集中管理することもできます。Java の Enum + POJO の場合、i18n と集中管理の両方がより煩雑になり、コード量も多くなります。前述の jdoctor の問題ビルダーを見れば分かる通りです。もちろん、言語によっては異なる場合もあります。たとえば Rust では、enum の機能が豊富なため、

3. なぜエラーコードにエラーレベルを含めないのか
多くのエラーコード設計ではエラーレベルが追加されます。RS-001-404-9 のように、最後の 1 桁がエラーの重大度レベルを示します。これ自体は問題ありませんが、現実的な要素も考慮する必要があります。

エラーのレベルは動的に調整されます:時間と空間の変化に伴い、以前は非常に深刻だったエラーが現在ではそれほど深刻ではなくなることがあります。リソースが見つからない場合、以前は非常に深刻だったかもしれませんが、現在ではバックアップソリューションが追加され、バックアップサーバーから再検索できるため、メインサービスでこのエラーが発生しても、それほど深刻ではないかもしれません。

異なるチームはエラーレベルに対して異なる認識を持っています:たとえば、OSS-404 は OSS チームのデータサーバーで見つからない場合、メタ情報はあってもデータサーバーに対応するデータが見つからないため、これは非常に深刻なエラーです。一方、ビジネスチーム、たとえば Serverless Jamstack を担当するチームでは、html、css、image などのファイルが 1 つ欠けていても大きな問題ではないかもしれません。しばらく待って再試行し、失敗したら再度アップロードすればよいのです。伝えたいのは、同じエラーでも異なるチームでは重要性が異なるということです。
エラーコードに重大度を固定してしまうと、後から調整できなくなります。エラーレベルを調整すると別のエラーコードになり、統計や理解に問題が生じます。個人的には、エラーコードに重大度レベルを含めず、周辺ドキュメントや説明で補足することを推奨します。もちろん、log.info や log.error でエラーレベルを判断することもできます。

4. 共有ライブラリを提供できるか

IntelliJ IDEA は動的なプロパティファイル名をサポートしていないため、動的なプロパティファイル名を使用すると、コード補完や検索などの機能が使用できません。そのため、@PropertyKey(resourceBundle = BUNDLE_FQN) の静的なプロパティファイル名を使用する必要があります。これは単なる Java クラスなので、この Java クラスをコピーする必要があります。一度限りの作業です。もちろん、コードをカスタマイズしたい場合はより便利です。Log4j 2.x やカスタムロギングフレームワークとの統合も容易です。ロギングはプロジェクトの最も基本的な要件です。プロジェクトを作成する際に、エラーコードに対応するコードをプロジェクトテンプレートに追加しておけば、プロジェクト作成後に自動的にロギングとエラーコードの機能が含まれます。

5. その他の注意点

原文および Reddit での関連する議論も整理・説明されています。

内部と外部の区別:たとえば、内部開発者のエラーにはサーバーの具体的な情報を含めることができます。ただし、プラットフォームの FaaS 開発者などの最終消費者には、このような情報を出力すべきではなく、セキュリティリスクがあります。

エラーで機密データを公開しないよう注意:エラーログに出力するデータは必ずマスク処理を行う必要があります。もちろん、エラーの特定に支障が出ない範囲で行います。これは具体的なシナリオに依存します。

エラーメッセージを API の契約として使用しない:API のシナリオでは、エラーへの対応方法は 2 つあります。エラーコードに基づく対応(REST API など)と、メッセージに基づく対応(GraphQL など)です。これは選択の問題です。

エラーコードの一貫性:エラーメッセージは REST API、インターフェイスなど、さまざまな消費者に出力されます。エラーメッセージは異なる場合があります(国際化、マスキングなど)が、同じエラーコードを使用することが望ましいです。つまり、フロントエンド + バックエンドで同じエラーコードを共有することで、エラーの特定と統計が容易になります。

7. まとめ

エラーコードを使用し、プロパティファイルベースでエラーメッセージを保存するこの設計は、実際には総合的な選択です。IDE がプロパティファイルを十分にサポートできず、エラーコードを見てもエラーメッセージを直接特定できず、対応するメッセージを探すために移動する必要がある場合は、Enum + POJO が良い選択肢かもしれません。また、エラーコードの設計も HTTP ステータスコード方式に大きく基づいています。これは主に、誰もが HTTP に非常に熟悉しており、基本的におおよその意味を推測できるという事実に基づいています。逆に、独自に割り当てられたコードにはこの利点がなく、エラーコードセンターで確認する必要があり、事実上開発者の時間を浪費することになります。

Related Articles

Explore More Special Offers

  1. Short Message Service(SMS) & Mail Service

    50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00

phone お問い合わせ
Hi, I'm Alibaba Cloud AI Assistant!
I can help with questions and solutions.