ELMAH가 ASP.NET MVC [HandleError] 특성과 작동하게하는 방법?


564

ASP.NET MVC 응용 프로그램에서 ELMAH를 사용하여 오류를 기록하려고하지만 컨트롤러에서 [HandleError] 특성을 사용할 때 ELMAH는 오류가 발생할 때 오류를 기록하지 않습니다.

ELMAH는 처리되지 않은 오류 만 기록하고 [HandleError] 특성이 오류를 처리하므로 오류를 기록 할 필요가 없기 때문에 추측합니다.

ELMAH가 오류가 있음을 알고 기록 할 수 있도록 속성을 수정하거나 어떻게 수정합니까?

편집 : 모두가 이해하도록하십시오. 제가 묻는 질문이 아닌 속성을 수정할 수 있음을 알고 있습니다. handleerror 속성을 사용할 때 ELMAH가 무시됩니다. 즉, 처리 되었기 때문에 오류가 발생하지 않았습니다. 이미 속성에 의해 ... 내가 묻는 것은 속성이 처리했지만 ELMAH가 오류를보고 기록하도록하는 방법이 있습니다 ... 나는 그것을 검색하기 위해 호출 할 메소드를 보지 못했습니다. 오류....


12
와우, 제러드가이 질문에 대답하기를 바랍니다. 그들은 Stackoverflow에 ELMAH를 사용하고 있습니다.)
Jon Limjap

11
흠, 이상합니다-HandleErrorAttribute를 사용하지 않습니다. Elmah는 web.config의 <modules> 섹션에 설정되어 있습니다. HandleErrorAttribute를 사용하면 이점이 있습니까?
Jarrod Dixon

9
@Jarrod-ELMAH 포크에 대한 "사용자 정의"가 무엇인지 보는 것이 좋습니다.
Scott Hanselman

3
@dswatik web.config에서 redirectMode를 ResponseRewrite로 설정하여 리디렉션을 방지 할 수도 있습니다. blog.turlov.com/2009/01/…
Pavel Chuchuva

6
웹 문서와 [HandleError] 특성 및 Elmah에 대해 언급 한 게시물을 계속 실행했지만 더미 사례를 설정할 때 이것이 해결되는 동작 (예 : Elmah가 "처리 된"오류를 기록하지 않음)을 보지 못했습니다. Elmah.MVC 2.0.x부터이 사용자 정의 HandleErrorAttribute가 더 이상 필요하지 않기 때문입니다. 그것은 nuget 패키지에 포함되어 있습니다.
plyawn

답변:


503

기본 구현이 처리하는 경우에만 ELMAH를 사용하여 예외를 기록하도록 복사 할 필요없이 멤버를 서브 클래스 화 HandleErrorAttribute하고 대체 할 수 있습니다 OnException. 필요한 최소한의 코드는 다음과 같습니다.

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;
    }
}

이 두 번째 버전은 먼저 ELMAH의 오류 신호 를 사용하려고 시도합니다 . 여기에는 로깅, 메일 링, 필터링과 같은 완전히 구성된 파이프 라인이 포함됩니다. 실패하면 오류를 필터링해야하는지 확인합니다. 그렇지 않으면 오류가 단순히 기록됩니다. 이 구현은 메일 알림을 처리하지 않습니다. 예외 신호를 보낼 수있는 경우, 그렇게 구성된 경우 메일이 전송됩니다.

여러 HandleErrorAttribute인스턴스가 적용되는 경우 중복 로깅이 발생하지 않지만 위의 두 가지 예를 시작해야합니다.


1
우수한. 나는 Elmah를 전혀 구현하려고하지 않았습니다. 방금 몇 년 동안 MVC와 잘 작동하는 방식으로 사용한 자체 오류보고를 연결하려고했습니다. 귀하의 코드는 시작점이되었습니다. +1
Steve Wortham

18
HandleErrorAttribute를 서브 클래스화할 필요는 없습니다. IExceptionFilter를 구현하고 HandleErrorAttribute와 함께 등록 할 수 있습니다. 또한 ErrorSignal.Raise (..)가 실패하는 경우 폴 백이 필요한 이유를 알 수 없습니다. 파이프 라인이 잘못 구성된 경우 수정해야합니다. 5 라이너 IExceptionFilter 검사 점 4. 여기 -ivanz.com/2011/05/08/…
Ivan Zlatev

5
적용 가능성, 단점 등과 관련하여 @IvanZlatev의 아래 답변에 대해 의견을 말해주십시오. 사람들은 더 쉽고 짧고 단순하며 답변과 동일한 결과를 얻으므로 올바른 답변으로 표시해야합니다. 이것에 대한 당신의 관점을 가지고이 답변들에 대한 명확성을 얻는 것이 좋을 것입니다.
앤드류

7
이것은 여전히 ​​관련이 있습니까, 아니면 ELMAH.MVC가 이것을 처리합니까?
Romias

2
심지어 나는 그것이 오늘날의 버전에 여전히 관련이 있는지 알고 싶다
리팩토링

299

죄송하지만 허용되는 답변은 과도하다고 생각합니다. 당신이해야 할 일은 이것입니다 :

public class ElmahHandledErrorLoggerFilter : IExceptionFilter
{
    public void OnException (ExceptionContext context)
    {
        // Log only handled exceptions, because all other will be caught by ELMAH anyway.
        if (context.ExceptionHandled)
            ErrorSignal.FromCurrentContext().Raise(context.Exception);
    }
}

Global.asax.cs에 등록하십시오 (순서 중요).

public static void RegisterGlobalFilters (GlobalFilterCollection filters)
{
    filters.Add(new ElmahHandledErrorLoggerFilter());
    filters.Add(new HandleErrorAttribute());
}

3
+1 아주 좋은의를 확장 할 필요가 HandleErrorAttribute, 필요 무시 없습니다 OnExceptionBaseController. 이것은 받아 들인 대답에 대한 가정입니다.
CallMeLaNN

1
@bigb 난 당신이 예를 들어 (예외 메시지 등 물건을 추가 할 자신의 예외 유형에 예외를 포장 할 것이라고 생각 new UnhandledLoggedException(Exception thrown)받는 것을 추가하는 Message그것을 반환하기 전에를.
이반 즈 레이트 브

23
Atif Aziz가 ELMAH를 만들었습니다. 그의 대답을
하겠습니다

48
@jamiebarrow 나는 그것을 알지 못했지만 그의 대답은 ~ 2 세이며 아마도 API를 단순화하여 질문의 사용 사례를 더 짧고 독립적 인 방식으로 지원할 수 있습니다.
Ivan Zlatev

6
@Ivan Zlatev는 실제로 ElmahHandledErrorLoggerFilter()처리되지 않은 오류를 기록하지만 처리 할 수없는 elmah 를 작동시킬 수는 없습니다. 당신이 언급 한대로 올바른 순서로 필터를 등록했습니다.
kuncevic.dev

14

NuGet에는 Atif의 개선 된 솔루션과 MVC 라우팅 내에서 elmah 인터페이스를 처리하는 컨트롤러 (더 이상 axd를 사용할 필요가 없음)를 포함하는 ELMAH.MVC 패키지가 있습니다.
해당 솔루션의 문제 (여기의 모든 문제 ) )는 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에 대한 설정이 다음과 같은지 확인하십시오.

<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"입니다. 이것이 거짓 인 경우 elmah.mvc 내부의 핸들러를 사용하여 실제로는 handleErrorHandler를 사용하여 예외를 처리하는 customError 설정을 무시합니다.

이 설정을 사용하면 ElmahMVCErrorFilter를 통해 오류를 계속 기록하면서 elmah 모듈을 통해 web.config에 customError 구성을 추가하고 고유 한 Error Handler를 작성하여 클래스 및 뷰에서 고유 한 ErrorHandler 태그를 설정할 수 있습니다. 당신이해야 할 유일한 것은 우리가 작성한 elmah 필터 전에 실제로 오류를 처리하는 필터를 추가하지 않는 것입니다. 그리고 나는 언급하지 않았습니다 : elmah에는 중복이 없습니다.


7

모든 컨트롤러에 HandleErrorWithElmah 속성을 주입하는 사용자 정의 컨트롤러 팩토리를 도입하여 위의 코드를 가져와 한 단계 더 나아갈 수 있습니다.

자세한 내용은 MVC 로그인에 대한 내 블로그 시리즈를 확인하십시오. 첫 번째 기사에서는 Elmah를 MVC에 설치하고 실행하는 방법에 대해 설명합니다.

기사 끝에 다운로드 가능한 코드에 대한 링크가 있습니다. 희망이 도움이됩니다.

http://dotnetdarren.wordpress.com/


6
기본 컨트롤러 클래스에 붙이는 것이 훨씬 쉬울 것 같습니다.
Nathan Taylor

2
로깅 및 예외 처리에 대한 위의 Darren 시리즈는 읽을 가치가 있습니다! 매우 철저합니다!
Ryan Anderson

6

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> 

간단합니다!


이것이 가장 간단한 해결책입니다. 커스텀 핸들러와 물건을 쓰거나 등록 할 필요가 없습니다. 나를 위해 잘 작동
ThiagoAlves

3
JSON / HTML이 아닌 응답에 대해서는 무시됩니다.
Craig Stuntz

6
또한 이것은 뷰에서 서비스 레벨 기능을 수행합니다. 여기에 속하지 않습니다.
Trevor de Koekkoek

6

완전히 대안적인 솔루션은 MVC를 사용하지 HandleErrorAttribute않고 Elmah가 설계 한 ASP.Net 오류 처리에 의존하는 것입니다.

HandleErrorAttributeApp_Start \ FilterConfig (또는 Global.asax)에서 기본 전역을 제거한 다음 Web.config에서 오류 페이지를 설정해야합니다.

<customErrors mode="RemoteOnly" defaultRedirect="~/error/" />

이는 MVC 라우팅 URL 일 수 있으므로 ErrorController.Index오류가 발생 하면 위의 작업으로 리디렉션됩니다 .


이것은 가장 간단한 솔루션이며 기본 리디렉션은 MVC 작업이 될 수 있습니다. :
Jeremy Cook

3
JSON과 같은 다른 유형의 요청으로 리디렉션됩니다.
zvolkov

5

저에게는 이메일 로깅을 작동시키는 것이 매우 중요했습니다. 얼마 후 Atif 예제 에서이 코드가 2 줄 더 필요하다는 것을 알았습니다.

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

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

나는 이것이 누군가를 도울 수 있기를 바랍니다 :)


2

이것이 바로 내 MVC 사이트 구성에 필요한 것입니다!

Atif Aziz가 제안한 것처럼 OnException여러 HandleErrorAttribute인스턴스 를 처리 하기 위해 메소드에 약간의 수정을 추가했습니다 .

여러 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);
}

base.OnException () .... 그리고 (exceptionHandledByPreviousHandler ||! context.ExceptionHandled || ...) 호출 주위에 "if"문이없는 것 같습니다. 서로를 취소하고 항상 사실입니다. 뭔가 빠졌습니까?
joelvh

먼저 현재 전에 호출 된 다른 처리기가 예외를 관리했는지 확인하고 결과를 변수에 예외 처리기 dByPreviousHandler로 저장합니다. 그런 다음 현재 처리기에서 예외 자체를 관리 할 수있는 기회를 제공합니다 : base.OnException (context).
ilmatte

먼저 현재 전에 호출 된 다른 처리기가 예외를 관리했는지 확인하고 결과를 변수에 예외 처리기 dByPreviousHandler로 저장합니다. 그런 다음 현재 처리기에서 예외 자체를 관리 할 수있는 기회를 제공합니다 : base.OnException (context). 예외가 이전에 관리되지 않은 경우 1-현재 핸들러에 의해 관리되고 다음과 같습니다. exceptionHandledByPreviousHandler = false 및! context.ExceptionHandled = false 2-현재 핸들러에 의해 관리되지 않음 : exceptionHandledByPreviousHandler = false 및! context. ExceptionHandled true입니다. 사례 1 만 기록됩니다.
ilmatte
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.