如何为C#MVC4 WebAPI应用程序全局logging所有exception?

背景

我正在开发一个客户端的API服务层,我已经被要求捕捉并logging所有错误的全球。

所以,虽然类似未知端点(或动作)的东西很容易通过使用ELMAH或向Global.asax添加如下内容来处理:

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

。 。 。与路由无关的未处理错误不会被logging。 例如:

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

我也尝试通过注册这个filter来全局设置[HandleError]属性:

 filters.Add(new HandleErrorAttribute()); 

但是,这也不logging所有的错误。

问题/疑问

如何拦截上面调用/test生成的错误,以便我可以logging它们? 看起来这个答案应该是显而易见的,但是我已经尝试了所有我能想到的东西。

理想情况下,我想添加一些东西到错误logging,如请求用户的IP地址,date,时间等等。 当遇到错误时,我也希望能够自动发送电子邮件给支持人员。 所有这一切我都可以做,只要我能拦截这些错误发生时!

解决!

感谢达林·季米特洛夫(Darin Dimitrov),我接受了他的回答,我明白了这一点。 WebAPI不像处理常规MVC控制器一样处理错误。

这是什么工作:

1)添加一个自定义filter到你的名字空间:

 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类中全局注册filter:

 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]属性来修饰一个控制器。

如果您的Web API驻留在ASP.NET应用程序中,则将针对代码中的所有未处理的exception(包括您在testing操作中显示的exception)调用Application_Error事件。 所以你所要做的就是在Application_Error事件中处理这个exception。 在示例代码中,您只显示了处理HttpExceptiontypes的exception,而Convert.ToInt32("a")代码显然不是这种情况。 所以请确保您logging并处理所有exception:

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

Web API中的exception处理可以在各个层次上完成。 这里有一个detailed article解释不同的可能性:

  • 自定义exceptionfilter属性,可以注册为全局exceptionfilter

     [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被正式发布 。
它为全球处理例外提供了另一个机会。
详细信息在示例中给出。

简而言之,您将添加全局exceptionlogging器和/或全局exception处理程序(仅一个)。
您将它们添加到configuration中:

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

为什么rethrow等? 这个工作,它会使服务返回状态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); } } 

将所有东西包装在try / catch中并logging未处理的exception,然后传递它。 除非有更好的内置方法来做到这一点。

这是一个引用Catch All(已处理或未处理)exception

(编辑:哦API)

你有没有想过做一些类似于句柄错误操作的filter

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

您还可以创build[HandleError]的自定义版本,您可以使用该自定义版本编写错误信息和所有其他logging