Having worked through a course on Pluralsight (.NET Logging Done Right: An Opinionated Approach Using Serilog by Erik Dahl) I began implementing a similar solution in my own ASP.Net Core 3.1 MVC project. As an initial proof of concept I downloaded his complete sample code from the course and integrated his logger class library into my project to see if it worked.
Unfortunately, everything seems to work apart from one critical element. In the Configure method of my project's Startup.cs file rather than app.UseExceptionHandler("/Home/Error");
I now have app.UseCustomExceptionHandler("MyAppName", "Core MVC", "/Home/Error");
- in theory this is meant to hit some custom middleware, passing in some additional data for error logging, then behave like the normal exception handler and hit the error handling path. In practice, it doesn't hit the error handling path and users are shown the browser's error page.
The comments on the middleware code say this code is:
// based on Microsoft's standard exception middleware found here:
// https://github.com/aspnet/Diagnostics/tree/dev/src/
// Microsoft.AspNetCore.Diagnostics/ExceptionHandler
This link no longer works. I found the new link but it's in a GitHub archive and it didn't tell me anything useful.
I am aware that there were changes to the way routing works between .Net Core 2.0 and 3.1, but I'm not sure whether these would cause the issue I am experiencing. I do not think the issue is in the code below that gets called from Startup.cs.
public static class CustomExceptionMiddlewareExtensions
{
public static IApplicationBuilder UseCustomExceptionHandler(
this IApplicationBuilder builder, string product, string layer,
string errorHandlingPath)
{
return builder.UseMiddleware<CustomExceptionHandlerMiddleware>
(product, layer, Options.Create(new ExceptionHandlerOptions
{
ExceptionHandlingPath = new PathString(errorHandlingPath)
}));
}
}
I believe the issue is likely to be in the Invoke method in the actual CustomExceptionMiddleware.cs below:
public sealed class CustomExceptionHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ExceptionHandlerOptions _options;
private readonly Func<object, Task> _clearCacheHeadersDelegate;
private string _product, _layer;
public CustomExceptionHandlerMiddleware(string product, string layer,
RequestDelegate next,
ILoggerFactory loggerFactory,
IOptions<ExceptionHandlerOptions> options,
DiagnosticSource diagSource)
{
_product = product;
_layer = layer;
_next = next;
_options = options.Value;
_clearCacheHeadersDelegate = ClearCacheHeaders;
if (_options.ExceptionHandler == null)
{
_options.ExceptionHandler = _next;
}
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
WebHelper.LogWebError(_product, _layer, ex, context);
PathString originalPath = context.Request.Path;
if (_options.ExceptionHandlingPath.HasValue)
{
context.Request.Path = _options.ExceptionHandlingPath;
}
context.Response.Clear();
var exceptionHandlerFeature = new ExceptionHandlerFeature()
{
Error = ex,
Path = originalPath.Value,
};
context.Features.Set<IExceptionHandlerFeature>(exceptionHandlerFeature);
context.Features.Set<IExceptionHandlerPathFeature>(exceptionHandlerFeature);
context.Response.StatusCode = 500;
context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response);
await _options.ExceptionHandler(context);
return;
}
}
private Task ClearCacheHeaders(object state)
{
var response = (HttpResponse)state;
response.Headers[HeaderNames.CacheControl] = "no-cache";
response.Headers[HeaderNames.Pragma] = "no-cache";
response.Headers[HeaderNames.Expires] = "-1";
response.Headers.Remove(HeaderNames.ETag);
return Task.CompletedTask;
}
}
Any suggestions would be really appreciated, I've been down so many rabbit holes trying to get this working over the last few days to no avail, and quite aside from my own project, I'd love to be able to leave a comment on the Pluralsight course for anyone else trying to do this to save them having to go through the same struggles as I have.
I actually recommend an easier approach than what I had originally showed in the .NET Logging Done Right course (that course was built around .NET Framework for the most part and the ASP.NET Core module was added after original publication). A better course for doing ASP.NET Core logging is the newer Effective Logging in ASP.NET Core. But rather than simply send you off to another course to watch, allow me to answer your question.
I think you should use the UseExceptionHandler(string path)
middleware. You are free to log the exception in the error code (controller or razor page code). You can see this concretely in this code repo:
https://github.com/dahlsailrunner/aspnetcore-effective-logging
Specifically look at these files in the BookClub.UI project:
Configure
method)That will keep your custom code to a minimum (always a good thing).
HTH