[C#] C # MVC4 WebAPI 앱에 대해 모든 예외를 전체적으로 어떻게 기록합니까?

배경

클라이언트를위한 API 서비스 계층을 개발 중이며 전 세계적으로 모든 오류를 포착하고 기록하도록 요청되었습니다.

따라서 ELMAH를 사용하거나 다음과 같은 것을 추가하면 알 수없는 끝점 (또는 동작)과 같은 것이 쉽게 처리됩니다 Global.asax.

protected void Application_Error()
{
     Exception unhandledException = Server.GetLastError();
     //do more stuff
}

. . 라우팅과 관련이없는 처리되지 않은 오류는 기록되지 않습니다. 예를 들면 다음과 같습니다.

public class ReportController : ApiController
{
    public int test()
    {
        var foo = Convert.ToInt32("a");//Will throw error but isn't logged!!
        return foo;
    }
}

[HandleError]이 필터를 등록 하여 속성을 전체적으로 설정하려고 시도했습니다 .

filters.Add(new HandleErrorAttribute());

그러나 모든 오류를 기록하지는 않습니다.

문제 / 질문

/test위에서 호출하여 생성 된 오류와 같은 오류를 어떻게 가로 채서 기록 할 수 있습니까? 이 대답은 분명해야하지만 지금까지 생각할 수있는 모든 것을 시도했습니다.

이상적으로는 요청하는 사용자의 IP 주소, 날짜, 시간 등과 같은 오류 로깅에 몇 가지 사항을 추가하고 싶습니다. 또한 오류가 발생하면 지원 담당자에게 자동으로 전자 메일을 보내려고합니다. 이 오류가 발생할 때만 이러한 오류를 가로 챌 수있는 경우이 모든 작업을 수행 할 수 있습니다.

해결되었습니다!

대답을 수락 한 Darin Dimitrov 덕분에 나는 이것을 알아 냈습니다. WebAPI는 일반 MVC 컨트롤러와 같은 방식으로 오류를 처리 하지 않습니다 .

다음은 효과가 있습니다.

1) 네임 스페이스에 사용자 정의 필터를 추가하십시오.

public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        if (context.Exception is BusinessException)
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
            {
                Content = new StringContent(context.Exception.Message),
                ReasonPhrase = "Exception"
            });

        }

        //Log Critical errors
        Debug.WriteLine(context.Exception);

        throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
        {
            Content = new StringContent("An error occurred, please try again or contact the administrator."),
            ReasonPhrase = "Critical Exception"
        });
    }
}

2) 이제 WebApiConfig 클래스 에 필터를 전체적으로 등록하십시오 .

public static class WebApiConfig
{
     public static void Register(HttpConfiguration config)
     {
         config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
         config.Filters.Add(new ExceptionHandlingAttribute());
     }
}

또는 등록을 건너 뛰고 [ExceptionHandling]속성 으로 단일 컨트롤러를 장식 할 수 있습니다 .



답변

웹 API가 ASP.NET 응용 프로그램 내에서 호스팅되는 경우 표시 Application_Error한 테스트 작업의 예외를 포함하여 코드에서 처리되지 않은 모든 예외에 대해 이벤트가 호출됩니다. 따라서 Application_Error 이벤트 내에서이 예외를 처리하기 만하면됩니다. 샘플 코드 HttpException에서 Convert.ToInt32("a")코드 의 경우가 아닌 유형의 예외 만 처리하고 있음을 보여주었습니다 . 따라서 모든 예외를 기록하고 처리해야합니다.

protected void Application_Error()
{
    Exception unhandledException = Server.GetLastError();
    HttpException httpException = unhandledException as HttpException;
    if (httpException == null)
    {
        Exception innerException = unhandledException.InnerException;
        httpException = innerException as HttpException;
    }

    if (httpException != null)
    {
        int httpCode = httpException.GetHttpCode();
        switch (httpCode)
        {
            case (int)HttpStatusCode.Unauthorized:
                Response.Redirect("/Http/Error401");
                break;

            // TODO: don't forget that here you have many other status codes to test 
            // and handle in addition to 401.
        }
        else
        {
            // It was not an HttpException. This will be executed for your test action.
            // Here you should log and handle this case. Use the unhandledException instance here
        }
    }
}

웹 API의 예외 처리는 다양한 수준에서 수행 될 수 있습니다. 다음 detailed article은 다양한 가능성을 설명합니다.

  • 글로벌 예외 필터로 등록 될 수있는 사용자 정의 예외 필터 속성

    [AttributeUsage(AttributeTargets.All)]
    public class ExceptionHandlingAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            if (context.Exception is BusinessException)
            {
                throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
                {
                    Content = new StringContent(context.Exception.Message),
                    ReasonPhrase = "Exception"
                });
            }
    
            //Log Critical errors
            Debug.WriteLine(context.Exception);
    
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
            {
                Content = new StringContent("An error occurred, please try again or contact the administrator."),
                ReasonPhrase = "Critical Exception"
            });
        }
    }
  • 맞춤 액션 호출자

    public class MyApiControllerActionInvoker : ApiControllerActionInvoker
    {
        public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
        {
            var result = base.InvokeActionAsync(actionContext, cancellationToken);
    
            if (result.Exception != null && result.Exception.GetBaseException() != null)
            {
                var baseException = result.Exception.GetBaseException();
    
                if (baseException is BusinessException)
                {
                    return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)
                    {
                        Content = new StringContent(baseException.Message),
                        ReasonPhrase = "Error"
    
                    });
                }
                else
                {
                    //Log critical error
                    Debug.WriteLine(baseException);
    
                    return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)
                    {
                        Content = new StringContent(baseException.Message),
                        ReasonPhrase = "Critical Error"
                    });
                }
            }
    
            return result;
        }
    }

답변

이전 답변에 추가로.

어제 ASP.NET Web API 2.1이 공개적으로 발표되었습니다 .
전 세계적으로 예외를 처리 할 수있는 또 다른 기회를 제공합니다.
자세한 내용은 샘플 에 나와 있습니다 .

간단히, 전역 예외 로거 및 / 또는 전역 예외 처리기 (하나만)를 추가합니다.
구성에 추가하십시오.

public static void Register(HttpConfiguration config)
{
  config.MapHttpAttributeRoutes();

  // There can be multiple exception loggers.
  // (By default, no exception loggers are registered.)
  config.Services.Add(typeof(IExceptionLogger), new ElmahExceptionLogger());

  // There must be exactly one exception handler.
  // (There is a default one that may be replaced.)
  config.Services.Replace(typeof(IExceptionHandler), new GenericTextExceptionHandler());
}

그리고 그들의 실현 :

public class ElmahExceptionLogger : ExceptionLogger
{
  public override void Log(ExceptionLoggerContext context)
  {
    ...
  }
}

public class GenericTextExceptionHandler : ExceptionHandler
{
  public override void Handle(ExceptionHandlerContext context)
  {
    context.Result = new InternalServerErrorTextPlainResult(
      "An unhandled exception occurred; check the log for more information.",
      Encoding.UTF8,
      context.Request);
  }
}


답변

왜 다시 던질까요? 이것은 작동하며 서비스 반환 상태를 500 등으로 만듭니다.

public class LogExceptionFilter : ExceptionFilterAttribute
{
    private static readonly ILog log = LogManager.GetLogger(typeof (LogExceptionFilter));

    public override void OnException(HttpActionExecutedContext actionExecutedContext)
    {
        log.Error("Unhandeled Exception", actionExecutedContext.Exception);
        base.OnException(actionExecutedContext);
    }
}


답변

핸들 에러 액션 필터와 같은 것을하는 것에 대해 생각 했습니까?

[HandleError]
public class BaseController : Controller {...}

[HandleError]오류 정보 및 기타 모든 세부 정보를 기록 할 수 있는 사용자 지정 버전을 만들 수도 있습니다.


답변

모든 것을 try / catch로 감싸고 처리되지 않은 예외를 기록한 다음 전달하십시오. 더 나은 기본 제공 방법이 없다면.

다음은 모두 처리 된 (처리 된 또는 처리되지 않은) 예외를 참조합니다.

(편집 : 오 API)


답변