.NET - 「すべての例外をキャッチするハンドラー」を実装する最良の方法は何ですか
-
03-07-2019 - |
質問
「もしすべてが失敗したら、それを捕まえる」ための最良の方法は何だろうと考えています。
つまり、アプリケーションでできるだけ多くの例外を処理しています。 しかし、それでもバグがあるはずなので、何かが必要です。 未処理の例外をすべてキャッチして、情報を収集して保存できるようにします データベースに保存したり、Web サービスに送信したりします。
AppDomain.CurrentDomain.UnhandledException イベントはすべてをキャプチャしますか?アプリケーションがマルチスレッドであっても?
サイドノート:Windows Vista では、任意のアプリケーションを許可するネイティブ API 関数が公開されています クラッシュ後に自分自身を回復するには...今は名前が思いつきません…しかし、私はむしろしたくない ユーザーの多くはまだ Windows XP を使用しているため、これを使用してください。
解決
AppDomainのUnhandledException動作を試しましたが、 (これは、未処理の例外が登録される最後の段階です)
はい、イベントハンドラを処理した後、アプリケーションは終了し、厄介な「...プログラムが動作ダイアログを停止しました」表示されます。
:) あなたはそれをまだ避けることができます。
チェックアウト:
class Program
{
void Run()
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Console.WriteLine("Press enter to exit.");
do
{
(new Thread(delegate()
{
throw new ArgumentException("ha-ha");
})).Start();
} while (Console.ReadLine().Trim().ToLowerInvariant() == "x");
Console.WriteLine("last good-bye");
}
int r = 0;
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Interlocked.Increment(ref r);
Console.WriteLine("handled. {0}", r);
Console.WriteLine("Terminating " + e.IsTerminating.ToString());
Thread.CurrentThread.IsBackground = true;
Thread.CurrentThread.Name = "Dead thread";
while (true)
Thread.Sleep(TimeSpan.FromHours(1));
//Process.GetCurrentProcess().Kill();
}
static void Main(string[] args)
{
Console.WriteLine("...");
(new Program()).Run();
}
}
PS。上位レベルでApplication.ThreadException(WinForms)またはDispatcherUnhandledException(WPF)の未処理を処理します。
他のヒント
ASP.NETでは、 Global.asax
ファイルで Application_Error
関数を使用します。
WinFormsでは、 ApplicationEvents
ファイルで MyApplication_UnhandledException
を使用します
これらの関数は両方とも、コードで未処理の例外が発生した場合に呼び出されます。例外をログに記録し、これらの関数からユーザーに素敵なメッセージを提示できます。
Winformアプリケーションでは、AppDomain.CurrentDomain.UnhandledExceptionに加えて、 Application.ThreadException および Application.SetUnhandledExceptionMode (UnhandledExceptionMode.CatchException付き)。この組み合わせはすべてをキャッチするようです。
メインスレッドには、次のオプションがあります。
- コンソールまたはサービスアプリケーション:
AppDomain.CurrentDomain .UnhandledException
- WinFormsアプリケーション:
アプリケーション.ThreadException
- Webアプリケーション:Global.asaxの
Application_Error
他のスレッドの場合:
- セカンダリスレッドには未処理の例外はありません。 SafeThread を使用します
- ワーカースレッド:(タイマー、スレッドプール)セーフティネットがまったくありません!
これらのイベントは例外を処理せず、単にアプリケーションに報告するだけであることに注意してください。それらについて
例外のログ記録は適切ですが、アプリケーションの監視はより適切です;-)
警告:私は SafeThread の記事の著者です。
WinFormsの場合、現在のスレッドの未処理の例外イベントに添付することも忘れないでください(特にマルチスレッドを使用している場合)。
ベストプラクティスに関するリンクこちらおよびこちらおよびここ(おそらく.netの最高の例外処理記事)
ELMAH というクールなものもあり、ASP.NETエラーを記録します。 Webアプリケーションで発生します。 Winform Appソリューションについて質問していることは承知していますが、これはこの種のことをWebアプリで必要とする人にとって有益だと感じました。私は仕事の場でそれを使用し、デバッグに非常に役立ちました(特に本番サーバーで!)
ここにいくつかの機能があります(ページから直接引き出します):
- ほとんどすべての未処理の例外のログ。
- 記録された例外のログ全体をリモートで表示するWebページ。
- ログに記録された例外の詳細をリモートで表示するWebページ。
- 多くの場合、死の元の黄色の画面を確認できます 特定の目的で生成されたASP.NET customErrorsモードであっても例外 オフにしました。
- 各エラーの発生時の電子メール通知。
- ログの最新の15個のエラーのRSSフィード。
- ログのバッキングストレージの実装の数。 インメモリ、Microsoft SQL Serverおよび コミュニティによって寄贈されました。
マルチスレッドアプリでもそのハンドラーのほとんどの例外を監視できますが、.NET(2.0以降)では、1.1互換モードを有効にしない限り、未処理の例外をキャンセルできません。それが起こると、AppDomainは何があってもシャットダウンされます。最善の方法は、この例外を処理し、新しいAppDomainを作成してアプリを再起動できるように、別のAppDomainでアプリを起動することです。
私は次のアプローチを使用しています。これは機能し、コードの量を大幅に削減します(ただし、より良い方法があるかどうか、またはその落とし穴が何であるかはわかりません。電話をかけるときはいつでも:私は、質問者がマイナスを与えることが彼らの行動を明確にするのに十分礼儀正しいだろうと疑問に思います。)
try
{
CallTheCodeThatMightThrowException()
}
catch (Exception ex)
{
System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace ();
Utils.ErrorHandler.Trap ( ref objUser, st, ex );
} //eof catch
そして、これが ErrorHandler コードです。明確にしておきます-:objUser - アプリユーザーをモデル化するオブジェクトです (ドメイン名、部門、地域などの情報を取得できます。ロギング目的 ILog logger - ロギングオブジェクトです。ロギング・アクティビティーを実行するもの StackTrace st - アプリのデバッグ情報を提供する StackTrace オブジェクト
using System;
using log4net; //or another logging platform
namespace GenApp.Utils
{
public class ErrorHandler
{
public static void Trap ( Bo.User objUser, ILog logger, System.Diagnostics.StackTrace st, Exception ex )
{
if (ex is NullReferenceException)
{
//do stuff for this ex type
} //eof if
if (ex is System.InvalidOperationException)
{
//do stuff for this ex type
} //eof if
if (ex is System.IndexOutOfRangeException)
{
//do stuff for this ex type
} //eof if
if (ex is System.Data.SqlClient.SqlException)
{
//do stuff for this ex type
} //eof if
if (ex is System.FormatException)
{
//do stuff for this ex type
} //eof if
if (ex is Exception)
{
//do stuff for this ex type
} //eof catch
} //eof method
}//eof class
} //eof namesp
管理されたGUIアプリでは、デフォルトで、GUIスレッドで発生した例外はApplication.ThreadExceptionに割り当てられたものによって処理されます。
他のスレッドで発生した例外は、AppDomain.CurrentDomain.UnhandledExceptionによって処理されます。
GUIスレッドの例外を非GUIの例外と同じように動作させ、AppDomain.CurrentDomain.UnhandledExceptionによって処理されるようにするには、次のようにします。
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
ThreadExceptionを使用してGUIスレッド例外をキャッチすることの利点は、アプリを続行させるオプションを使用できることです。構成ファイルがデフォルトの動作をオーバーライドしていないことを確認するには、次を呼び出します。
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
あなたは未だに正常に動作しないネイティブdllからの例外に対して脆弱です。ネイティブdllがWin32 SetUnhandledExceptionFilterを使用して独自のハンドラーをインストールする場合、以前のフィルターへのポインターを保存して呼び出すことも想定されています。そうしないと、ハンドラは呼び出されません。