質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

4回答

9788閲覧

例外・ログメッセージ管理のプラクティス

ikasoumen

総合スコア110

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

3クリップ

投稿2017/11/14 17:16

アプリケーションで想定するエラーやログのメッセージ管理はどのようなプラクティスがありますか?

例えば、データベースが切断された場合や、入力値が不正である場合など、
エラーの種類により、Exceptionクラスを定義するのか、
それとも、アプリケーション独自のExceptionクラスは1つで、
MessageIDなどで紐づけたCSVファイルのようなデータからメッセージを取り出すなど、
どのようなアプローチが一般的でしょうか。

エラーのカテゴリによって、エラーメッセージの粒度が違っていたり、
うまく体系化するのが難しいと感じています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

C# のタグが付いていますので .NET アプリの話と理解してレスします。(そういうことは、質問の一番最初に書いておいてほしいです)

.NET の例外処置について、自分的にベストプラクティスと思っていることを書きます。

詳しくは後の方に紹介した記事を読んでいただきたいのですが、要約すると以下の通りです。

(1) 予測可能で正しい業務フローに戻すことができる「業務エラー」(例:ユーザーの入力間違い)と、予測できないもしくは予測はできても何の対応もできない「例外」(例:DB サーバーダウン)を区別して対処。

質問者さんが挙げた、

例えば、データベースが切断された場合や、入力値が不正である場合など、

のようなケースでは、前者を「例外」、後者を「業務エラー」として扱うということです。紹介した記事にも書いてありますが、いずれも SqlException としてしか検出できない場合でも、その中身を調べて処理を分けることは可能です。

(2) 「例外」はランタイムに拾わせてアプリケーションを停止させる。

(3) よほどのことがない限り try-catch は書かない。

(4) キャッチせざるを得ない場合でも Exception はキャッチしない。

(5) 間違って補足してしまった例外は throw する。(注:catch ブロックでキャッチした例外を throw するとスタックトレースが途切れるので単に throw と書く)

(6) ユーザーへの通知が必要なら、集約的例外処置を利用する。

詳しくは以下の記事を見てください。

NETの例外処理 Part.1
https://blogs.msdn.microsoft.com/nakama/2008/12/29/net-part-1/

.NETの例外処理 Part.2
https://blogs.msdn.microsoft.com/nakama/2009/01/02/net-part-2/

あと、.NET 4 からは破損状態例外は catch できなくなっているそうですが、「それでも Catch (Exception e) を使用するのはよくない」ということについては以下の記事を見てください。

破損状態例外を処理する
https://msdn.microsoft.com/ja-jp/magazine/dd419661.aspx

【追伸】

下記は 2017/11/18 23:27 のコメント欄で「上記に対するレスは回答欄に書きます」と書いた件です。

上に書いた「業務エラー」と「例外」を区別して対処というところは納得していただけたでしょうか?

そうでないとするとこの先話を続けてもなかなか理解が得られないと思いますが、そこは自分の考えでレスします。

まず、「例外」ですが、それは開発者にのみ提供すればいい情報ですので、.NET Framework が提供する例外情報(例外の種類、メッセージ、スタックトレースなど)がログなどで取得できればよく、管理とか体系化とか余計な個とは考えなくてもいいと思います。

「業務エラー」の方は、ユーザーにユーザー入力の訂正などを促して正しい業務フローに戻すということになるでしょうから、ユーザーに適切に情報を提供する必要があります。そのためには何らかの管理は必要になるのは分かります。

ASP.NET MVC アプリの場合は、ユーザー入力の検証結果をどのようにユーザーに伝えるかがメインとなり、そのための機能にデータアノテーション属性による検証とエラーメッセージの表示があると思います。(それだけではないとは思いますが、範囲を広げると話が発散するので)

そのためには、以下のような配慮をするということだと思います。(質問者さんの言う「管理」とは違うかもしれませんが)

(1) DataType 属性による検証やデフォルトのエラーメッセージに頼らず、Range, RegularExpression, Required, StringLength 属性などを組み合わせて使い、エラーメッセージは自分で各データアノテーション属性の ErrorMessage プロパティに設定する。そのあたりについての詳細は以下の記事を見てください。

DataType 属性による検証
http://surferonwww.info/BlogEngine/post/2016/03/08/validation-by-datatypeattribute-and-default-error-message.aspx

(2) コードにエラーメッセージをハードコーディングしたくないならリソースファイルを使用する。リソースファイルを使用すればもちろん国際化はできます。詳しくは以下の記事を見てください。

データアノテーション検証の多言語対応
http://surferonwww.info/BlogEngine/post/2014/09/11/multi-languages-message-for-data-annotation.aspx

(3) オマケ情報ですが、ADO.NET Entity Data Model を使って作成したモデルの場合は以下のようにデータアノテーション属性を付与できます。

EDM にデータアノテーション属性を付与
http://surferonwww.info/BlogEngine/post/2017/05/21/how-to-add-dataannotation-attributes-to-edm-generated-by-visual-studio.aspx

投稿2017/11/14 23:56

編集2017/11/18 14:33
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikasoumen

2017/11/15 00:23

ご回答ありがとうございます。参考にいたします。 破損状態例外というのは、初めて聞きました。 しかし、恐れながら、例外の処置方法は本質問の趣旨でなく、 例外・業務エラーをどこで体系化・管理するかというのが質問の趣旨となります。
ikasoumen

2017/11/15 00:25

フレームワークはASP.NET MVC5を利用しています。
退会済みユーザー

退会済みユーザー

2017/11/15 01:18

> 例外・業務エラーをどこで体系化・管理するか すみませんが意味が理解できません。 何のためにどのような目的で「体系化・管理」するのか、MVC5 アプリの話に限って、やりたいことの全体のシナリオ・ストーリーを具体的に書いていただくと自分も理解できそうですが・・・
退会済みユーザー

退会済みユーザー

2017/11/15 01:27

あと、もう一つ、 > 例外の処置方法は本質問の趣旨でなく、 とのことですが、それ(例外の処置方法)が決まってから、発生する例外・業務エラーをどう管理するかという話になると思います。なので、順番が違うような気がします。その点誤解があれば書いてください。
ikasoumen

2017/11/18 04:22

すいません。質問を再度整理しました。 メッセージ一覧で管理する項目はどのようにするべきか。 メッセージは、例外・業務エラーなどのカテゴリでどう体系化するべきか。 メッセージを外出しする意図としては、以下のようなことが上げられます。  ・コードの実装とメッセージのメンテナンス作業を分離したい。  ・一覧化し、資料として活用したい。  ・国際化対応など、表示メッセージの切り替え処理に対応したい。  ・どんな機能・どんな状況で発生するメッセージか把握したい。
退会済みユーザー

退会済みユーザー

2017/11/18 14:27

上記に対するレスは回答欄に書きます。
ikasoumen

2017/11/22 13:53

ご丁寧にご回答ありがとうございました。 「業務エラー」と「例外」については理解いたしました。 ビジネスロジックは、ASP.NETでサポートしている検証機能はフルに活用すべきだと思っております。 メッセージの管理については、リソースファイルがよさそうですね。 ただ、列を追加するなどカスタマイズはできなそうですね。 ともかく今度試してみようと思います。
guest

0

例外の方だけ。
msdnの例外の推奨事項Eric Lippert氏のVexing exceptionsのページ

例外に以下のラベル付けを行っています。
・Fatal
・Boneheaded
・vexing and exogenous

投稿2017/11/14 21:47

編集2017/11/15 00:30
umyu

総合スコア5846

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

アプリケーション独自のExceptionクラスは1つで、
MessageIDなどで紐づけたCSVファイルのようなデータからメッセージを取り出す

個人的にはこちらを採用することが多いです。
特定のエラーについて情報を増やすためにサブクラスを作成することはあります。

投稿2017/11/14 20:49

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikasoumen

2017/11/15 00:08

複数のエラーが発生する処理などでは、 列挙機構を持つエラークラスに、それぞれの詳細なエラーをまとめておくなど必要そうですね。
退会済みユーザー

退会済みユーザー

2017/11/15 00:15 編集

複数のエラーが発生し得る場合も、一度に発生するエラーは1つではないかと思います。 "入力値が不正である場合"等も質問に記載されているので、バリデーション等を意図されているのでしょうか。
ikasoumen

2017/11/15 00:34

バリデーションも意図しています。 バリデーションに関しては、ASP.NET MVC5でサポートされている、 モデルのプロパティに属性を付与することで実現しています。
guest

0

例外処理はオブジェクト指向の仕組みです。
C言語になくて、C++やC#やJavaにあるというだけでなく、
じっさい、例外時に例外オブジェクトを生成するでしょう。

エラーの種類により、Exceptionクラスを定義するのか
それとも、アプリケーション独自のExceptionクラスは1つ

だから、オブジェクト指向らしい例外処理ということでは、
例外の種類によって、例外クラスを分けていきます。
基本的には、普通のクラスを分けていくことと同じです。

ただ、小規模ならひとつで済む場合もあるかと思います。
というより、例外処理に手を掛けられないという方が実態かも。


エラーのカテゴリによって、エラーメッセージの粒度が違っていたり、
うまく体系化するのが難しいと感じています。

たしかに、例外処理は難しいです。本に載ってないことが多いし、
そもそもエラーは個別的だから体系化が難しい。

じっさい、言語処理系のエラーメッセージですら、
分かりにくいことが多いですよね。

じゃあ、具体的にどうすればいいのかというと、
本体の処理ができた後でエラー処理をくっつけていくのではなく、
最初からエラー処理や例外設計がしやすい設計にします。

たとえば、レイヤーアーキテクチャで階層を分けるのは、
理由は色々あるでしょうが、エラー処理のしやすさも、そのひとつでしょう。

というのも、UIやDBは独自のエラーをモリモリ出してくるから、
それらをドメインモデルと混ぜてしまうと、
エラー処理でドメインが埋まってしまう。だから分けると。


データベースが切断された場合や、入力値が不正である場合

何を例外にするかは、私の見方では大別すると、
エラー処理には、「例外なく例外を使う」考え方(統一派)と、
DBは例外で、入力値の不正は例外にしない、
という「例外は例外的状況にのみ使う」という考え方(純粋派)があります。

統一派は入力値の不正も例外にしますが、
純粋派はそれは「準正常系」だといって、例外にしません。
どうするかというと、IF文で分けたり、戻り値でエラーを表現したりします。

例外処理における統一派と純粋派は、それぞれ一長一短だと思います。
言語仕様を見ると、Javaは統一派で、C#は純粋派に近い立場でしょう。

統一派は、IF文のエラー処理で本体の処理が埋もれる、
リターン値のエラーコードは見過ごされる、と主張します。

純粋派は、とくに大規模では例外処理が複雑になり過ぎる、
だから実態としては例外でできることが少ない、と主張します。

「すべてがオブジェクト」みたいに「すべてのエラーが例外」と、
例外に統一化した方がOOの理想郷ではあるが(私も考え方としては好きです)、
現実には純粋主義的な例外の方が使いやすい場面もあるのかなと思います。

投稿2017/11/14 21:46

LLman

総合スコア5592

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikasoumen

2017/11/15 00:07

私もクラスで書きたいですが、 そうするとトラブルシューティングとかのネタにするメッセージの一覧がなくて困ったり、 肥大化するとどんなエラーを選べばよいか、分類できないから新たに定義するべきかなど考え事が増えてきそうですよね。 エラークラスからメッセージ集めて自動出力とかできればいいのですが。 処理の失敗を戻り値で伝えるか、例外で伝えるか、 私の経験上、戻り値で伝えようとした場合、どうしても情報が足りなくて例外が必要になる場合があり、 初めから例外に統一した方がいいかなと思っています。 レイヤーの設計では、検証(ビジネスロジック)・登録・更新(DB層)で分け、 登録・更新を行う場合は、渡された値は検証済みのため正しいという前提を元に処理するようにしています。 UIから渡された値を検証するには、上記でいいですが、テーブル間の整合性のチェックなどは、 処理の中でゴリゴリチェックを書きがちになってしまいますね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問