I a using elmah.io for logging errors in my asp.net core application:
app.UseElmahIo("API_KEY", new Guid("LOG_ID"));
It's just a middleware and it looks like:
public async Task Invoke(HttpContext context)
{
try
{
await _next.Invoke(context);
await MessageShipper.ShipAsync(_apiKey, _logId, "Unsuccessful status code in response", context, _settings);
}
catch (Exception exception)
{
await exception.ShipAsync(_apiKey, _logId, context, _settings);
throw;
}
}
Also, I have my custom ExceptionFilter to handle errors and return specific message to client:
public class ApiExceptionFilter : ExceptionFilterAttribute
{
private ApiError _apiError;
public override void OnException(ExceptionContext context)
{
if (context.Exception is ApiException)
{
HandleKnownError(context);
}
else if (context.Exception is UnauthorizedAccessException)
{
HandleUnauthorizedException(context);
}
else
{
UnhandeledException(context);
}
context.Result = new JsonResult(_apiError);
base.OnException(context);
}
private void UnhandeledException(ExceptionContext context)
{
var msg = context.Exception.GetBaseException().Message;
string stack = context.Exception.StackTrace;
_apiError = new ApiError(msg) {Detail = stack};
context.HttpContext.Response.StatusCode = 500;
}
private void HandleKnownError(ExceptionContext context)
{
var ex = context.Exception as ApiException;
context.Exception = null;
_apiError = new ApiError(ex.Message);
context.HttpContext.Response.StatusCode = ex.StatusCode;
}
}
In this case, if I throw exception somewhere, in elmah.io I just see message Unsuccessful status code in response. I believe it's because my ApiExceptionFilter handle exception first and elmah middleware doesn't have exception, just bad status. How to solve it?
PS. Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.Configure<Settings>(Configuration);
ConfigureCors(services);
services.AddMvc(o =>
{
o.Filters.Add(new ApiResponseWrapper());
o.Filters.Add(typeof(ApiExceptionFilter));
})
... a lot of my services
}
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
IServiceProvider serviceProvider)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
var logPath = Path.Combine(env.WebRootPath, "Logs/App-log-{Date}.txt");
loggerFactory.AddFile(logPath);
app.UseElmahTo("API",new Guid("LOG_ID"));
app.UseCors("CorsPolicy");
ConfigureAuth(app, env);
app.UseMvc();
}
You are right in the fact that your exception filter swallows the exception before the elmah.io middleware is executed. You'd normally add the elmah.io middleware as the last in the chain, to make sure that other pieces of middleware doesn't behave like this. But in your case, the filter is executed before elmah.io is ever notified about the error.
You could fix this by:
Implementing your error filter as middleware. You current solution probably breaks some of the built in pieces of middleware in ASP.NET Core anyway.
Manually log unhandled exceptions to elmah.io, by extending the
UnhandledExceptionmethod: