ELMAHをASP.NET MVC[HandleError]属性で動作させるには?




asp.net-mvc logging (6)

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

ELMAHは処理されていないエラーだけを記録し、[HandleError]属性はエラーを処理しているため、ログに記録する必要はないため、推測しています。

ELMAHがエラーがあったことを知り、ログに記録するように、属性を変更するにはどうすればよいのですか。

編集:すべての人が理解できるようにしましょう。私が求めているのではなく、属性を変更できることはわかっています... ELMAHはhandleerror属性を使用するとバイパスされます。処理されたためエラーが表示されません。既に属性によって...私が求めているのは、ELMAHにエラーが表示され、属性がそれを処理してもログに記録する方法です...私は検索し、強制的にログに記録する方法は見当たりませんエラー....


HandleErrorAttributeをサブクラス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番目のバージョンは、ELMAHからのエラーシグナリングを最初に使用しようとします。これには、ロギング、メーリング、フィルタリングなどの完全に構成されたパイプラインが含まれます。 それに失敗すると、エラーをフィルタリングする必要があるかどうかを確認しようとします。 そうでない場合、エラーは単に記録されます。 この実装はメール通知を処理しません。 例外が通知された場合、そのように設定されていればメールが送信されます。

また、複数のHandleErrorAttributeインスタンスが有効な場合、重複したロギングは発生しませんが、上記の2つの例が開始されるように注意する必要があります。


これはまさに私のMVCサイト設定に必要なものです!

Atif Azizが提案したように、複数のHandleErrorAttributeインスタンスを処理するために、 OnExceptionメソッドに少し変更を加えました。

複数のHandleErrorAttributeインスタンスが有効な場合、重複したロギングが発生しないように注意する必要があるかもしれないことに注意してください。

他の誰かが現在のハンドラの前に例外を処理したかどうかを知るために、基本クラスを呼び出す前にcontext.ExceptionHandledチェックするだけです。
それは私のために働くと誰かがそれを必要とする場合にコードを投稿し、誰かが私が何かを見落としたかどうか知っているかどうか尋ねる。

希望はそれが便利です:

public override void OnException(ExceptionContext context)
{
    bool exceptionHandledByPreviousHandler = context.ExceptionHandled;

    base.OnException(context);

    Exception e = context.Exception;
    if (exceptionHandledByPreviousHandler
        || !context.ExceptionHandled  // if unhandled, will be logged anyhow
        || RaiseErrorSignal(e)        // prefer signaling, if possible
        || IsFiltered(context))       // filtered?
        return;

    LogException(e);
}

上記のコードを実行して、すべてのコントローラーにHandleErrorWithElmah属性を注入するカスタムコントローラーファクトリーを導入することで、さらに進んでいくことができます。

詳細については、MVCでログインしているブログシリーズをご覧ください。 最初の記事では、ElmahをMVC用にセットアップして実行する方法について説明します。

記事の最後にダウンロード可能なコードへのリンクがあります。 希望が役立ちます。

http://dotnetdarren.wordpress.com/


現在、NuGetのELMAH.MVCパッケージには、Atifの改良されたソリューションと、MVCルーティング内のelmahインターフェイスを扱うコントローラー(このaxdをもう使用する必要はありません)が含まれています。
その解決策(とここにあるすべてのもの)の問題は、elmahエラーハンドラが実際にエラーを処理しているか、customErrorタグとして設定するか、ErrorHandlerまたは独自のエラーハンドラ
IMHOの最も良い解決策は、他のすべてのフィルタの最後で動作し、すでに処理されたイベントを記録するフィルタを作成することです。 elmahモジュールは、アプリケーションによって処理されない他のエラーのログを処理する必要があります。 これにより、正常性モニターとasp.netに追加できる他のすべてのモジュールを使用して、エラー・イベントを調べることもできます

私はelmah.mvcの中のErrorHandlerでリフレクターを使ってこれを書いています

public class ElmahMVCErrorFilter : IExceptionFilter
{
   private static ErrorFilterConfiguration _config;

   public void OnException(ExceptionContext context)
   {
       if (context.ExceptionHandled) //The unhandled ones will be picked by the elmah module
       {
           var e = context.Exception;
           var context2 = context.HttpContext.ApplicationInstance.Context;
           //TODO: Add additional variables to context.HttpContext.Request.ServerVariables for both handled and unhandled exceptions
           if ((context2 == null) || (!_RaiseErrorSignal(e, context2) && !_IsFiltered(e, context2)))
           {
            _LogException(e, context2);
           }
       }
   }

   private static bool _IsFiltered(System.Exception e, System.Web.HttpContext context)
   {
       if (_config == null)
       {
           _config = (context.GetSection("elmah/errorFilter") as ErrorFilterConfiguration) ?? new ErrorFilterConfiguration();
       }
       var context2 = new ErrorFilterModule.AssertionHelperContext((System.Exception)e, context);
       return _config.Assertion.Test(context2);
   }

   private static void _LogException(System.Exception e, System.Web.HttpContext context)
   {
       ErrorLog.GetDefault((System.Web.HttpContext)context).Log(new Elmah.Error((System.Exception)e, (System.Web.HttpContext)context));
   }


   private static bool _RaiseErrorSignal(System.Exception e, System.Web.HttpContext context)
   {
       var signal = ErrorSignal.FromContext((System.Web.HttpContext)context);
       if (signal == null)
       {
           return false;
       }
       signal.Raise((System.Exception)e, (System.Web.HttpContext)context);
       return true;
   }
}

さて、フィルタ設定で、次のようなことをしたいと思っています:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //These filters should go at the end of the pipeline, add all error handlers before
        filters.Add(new ElmahMVCErrorFilter());
    }

例外を実際に処理するグローバルフィルタを追加する場合は、この最後のフィルタの前に移動する必要があります。そうしないと、処理されない例外がElmahMVCErrorFilterによって無視されるケースが発生することを思い出させるためにコメントを残しました。それは処理されておらず、Elmahモジュールによってログに記録されるべきですが、次のフィルタは例外を処理済みとしてマークし、モジュールはそれを無視し、例外をelmahに入力しません。

今度は、webconfigのelmahのappsettingsが次のようになっていることを確認してください:

<add key="elmah.mvc.disableHandler" value="false" /> <!-- This handles elmah controller pages, if disabled elmah pages will not work -->
<add key="elmah.mvc.disableHandleErrorFilter" value="true" /> <!-- This uses the default filter for elmah, set to disabled to use our own -->
<add key="elmah.mvc.requiresAuthentication" value="false" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.allowedRoles" value="*" /> <!-- Manages authentication for elmah pages -->
<add key="elmah.mvc.route" value="errortracking" /> <!-- Base route for elmah pages -->

ここで重要なのは "elmah.mvc.disableHandleErrorFilter"です。これがfalseの場合、elmah.mvc内のハンドラを使用します。このハンドラは、あなたのcustomError設定を無視するデフォルトのHandleErrorHandlerを使用して実際に例外を処理します

この設定では、クラスとビューで独自のErrorHandlerタグを設定できますが、ElmahMVCErrorFilterでこれらのエラーをログに記録し、独自のエラーハンドラを作成しても、elmahモジュールを通じてweb.configにcustomError設定を追加します。 あなたが行う必要があるのは、私たちが書いたelmahフィルタの前に実際にエラーを処理するフィルタを追加しないことだけです。 そして、私は言及することを忘れていた:elmahに重複はありません。


私にとって、電子メールのロギングを有効にすることは非常に重要でした。 しばらくすると、私はこれがコードの2行だけをAtifの例でもっと必要とすることを発見します。

public class HandleErrorWithElmahAttribute : HandleErrorAttribute
{
    static ElmahMVCMailModule error_mail_log = new ElmahMVCMailModule();

    public override void OnException(ExceptionContext context)
    {
        error_mail_log.Init(HttpContext.Current.ApplicationInstance);
        [...]
    }
    [...]
}

私はこれが誰かを助けることを願って:)


私はASP.NET MVCの新機能です。 私は同じ問題に直面しました。次は私のErorr.vbhtmlで実行可能です(Elmahログを使用してエラーをログするだけでよい場合は動作します)

@ModelType System.Web.Mvc.HandleErrorInfo

    @Code
        ViewData("Title") = "Error"
        Dim item As HandleErrorInfo = CType(Model, HandleErrorInfo)
        //To log error with Elmah
        Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(New Elmah.Error(Model.Exception, HttpContext.Current))
    End Code

<h2>
    Sorry, an error occurred while processing your request.<br />

    @item.ActionName<br />
    @item.ControllerName<br />
    @item.Exception.Message
</h2> 

それは単純です!





elmah