1. ホーム
  2. asp.net-mvc

[解決済み] ELMAHをASP.NET MVCの[HandleError]属性で動作させる方法は?

2022-03-17 17:28:36

質問

ASP.NET MVCアプリケーションでELMAHを使用してエラーを記録しようとしていますが、コントローラで[HandleError]属性を使用すると、ELMAHはエラーが発生してもログを記録しません。

推測するに、ELMAHは処理されなかったエラーのみを記録し、[HandleError]属性はエラーを処理しているので、それを記録する必要がないためだと思われます。

ELMAHがエラーがあったことを知り、それを記録できるようにするには、どのように属性を修正すればよいのでしょうか。

編集してください。 みんなに理解してもらうために、私が属性を変更できることは知っていますが、それは私が質問していることではありません...。ELMAHはhandleerror属性を使うときにバイパスされます。つまり、それは属性によってすでに処理されたので、エラーがあったことを見ることができません... 私が聞いているのは、ELMAHにエラーを見て、属性がそれを処理したにもかかわらず、それを記録させる方法はあるかということです...私はいろいろ探しましたが、エラーを記録するように強制するメソッドは見つかりませんでした...

どうすればいいですか?

をサブクラス化することができます。 HandleErrorAttribute をオーバーライドし、その OnException メンバー(コピーする必要はありません)を使って、ELMAHで例外を記録し、基本実装がそれを処理した場合のみ、例外を記録するようにします。必要な最小限のコードは以下の通りです。

using System.Web.Mvc;
using Elmah;

public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        base.OnException(context);
        if (!context.ExceptionHandled) 
            return;
        var httpContext = context.HttpContext.ApplicationInstance.Context;
        var signal = ErrorSignal.FromContext(httpContext);
        signal.Raise(context.Exception, httpContext);
    }
}

まず基本実装が呼び出され、例外を処理するようにマークする機会が与えられます。そして、その後に例外が通知されます。上記のコードは単純なものです。 HttpContext は、テストなどでは利用できない場合があります。その結果、(少し長くなりますが)より防御的なコードが欲しくなります。

using System.Web;
using System.Web.Mvc;
using Elmah;

public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        base.OnException(context);
        if (!context.ExceptionHandled       // if unhandled, will be logged anyhow
            || TryRaiseErrorSignal(context) // prefer signaling, if possible
            || IsFiltered(context))         // filtered?
            return;

        LogException(context);
    }

    private static bool TryRaiseErrorSignal(ExceptionContext context)
    {
        var httpContext = GetHttpContextImpl(context.HttpContext);
        if (httpContext == null)
            return false;
        var signal = ErrorSignal.FromContext(httpContext);
        if (signal == null)
            return false;
        signal.Raise(context.Exception, httpContext);
        return true;
    }

    private static bool IsFiltered(ExceptionContext context)
    {
        var config = context.HttpContext.GetSection("elmah/errorFilter")
                        as ErrorFilterConfiguration;

        if (config == null)
            return false;

        var testContext = new ErrorFilterModule.AssertionHelperContext(
                              context.Exception, 
                              GetHttpContextImpl(context.HttpContext));
        return config.Assertion.Test(testContext);
    }

    private static void LogException(ExceptionContext context)
    {
        var httpContext = GetHttpContextImpl(context.HttpContext);
        var error = new Error(context.Exception, httpContext);
        ErrorLog.GetDefault(httpContext).Log(error);
    }

    private static HttpContext GetHttpContextImpl(HttpContextBase context)
    {
        return context.ApplicationInstance.Context;
    }
}

この2番目のバージョンでは エラー信号 これは、ロギング、メーリング、フィルタリングなど、完全に設定されたパイプラインを含んでいます。失敗した場合は、そのエラーがフィルタリングされるべきものであるかどうかを確認しようとします。もしそうでなければ、エラーは単にログに記録されます。この実装では、メール通知は行いません。例外がシグナルされるように設定されている場合は、メールが送信されます。

また、複数の HandleErrorAttribute インスタンスが有効な場合、重複したロギングが発生しないようにする必要があります。