How to suppress 'An unhandled exception has occurred while executing the request.' log message with Serilog

Viewed 1342

When implementing my own Exception handler using app.UseExceptionHandler with an exception handler lambda / delegate (Documentation) I do not want Microsofts Exception handler middleware to log this error:

An unhandled exception has occurred while executing the request.

This exception message clutters my log file because I take care of logging the exception with an appropriate LogLevel by myself.

This is how I register my own ErrorHandler:

app.UseExceptionHandler(errorApp =>
                errorApp.Run(ErrorHandler));

As described in this issue filtering this exact message and nothing else is not possible in a simple way.

How do I filter this exact log message and nothing else from the middleware?

I am using:

  • ASP.NET Core 3.1
  • Serilog 3.4.0
2 Answers

As described in the issue mentioned above, we can use a Filter expression for this. Unfortunately the issue only describes a solution for a deprecated package and is filtering all messages from the Middleware, not only the mentioned message.

  1. Install the Serilog.Expressions package
  2. Update the matching appsettings.json with this configuration:
  "Serilog": {
    "Using": [ "Serilog.Expressions" ],
    "Filter": [
      {
        "Name": "ByExcluding",
        "Args": {
          "expression": "@l='Error' and SourceContext='Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware' and @mt='An unhandled exception has occurred while executing the request.'"
        }
      }
    ]
  }
  1. Make sure you have matching code that loads the configuration from appsettings.json as documented, e.g.:
Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(configuration)
                .CreateLogger();

This way you can keep all other messages from the ExceptionHandlerMiddleware and just disable the redundant error log message.

Side note: In case the response already has started, our custom exception handler will not be executed - but a warning will be logged and the exception will be rethrown and later logged by another application layer - at least in the application I tested. You can test this behavior by adding this code somewhere before the exception is thrown (context is the current HttpContext):

 await context.Response.WriteAsync("fish");

I have replaced app.UseExceptionHandler with a custom middleware in my program, it works like a charm.

Startup.cs:

app.UseMiddleware<HttpExceptionMiddleware>();

HttpExceptionMiddleware.cs

using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;

internal class HttpExceptionMiddleware
{
    private readonly RequestDelegate next;

    public HttpExceptionMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await this.next.Invoke(context);
        }
        catch (HttpException e)
        {
            var response = context.Response;
            if (response.HasStarted)
            {
                throw;
            }
            response.StatusCode = (int) e.StatusCode;
            response.ContentType = "application/json; charset=utf-8";
            response.Headers[HeaderNames.CacheControl] = "no-cache";
            response.Headers[HeaderNames.Pragma] = "no-cache";
            response.Headers[HeaderNames.Expires] = "-1";
            response.Headers.Remove(HeaderNames.ETag);

            var bodyObj = new {
                Message = e.Message,
                Status = e.StatusCode.ToString()
            };
            var body = JsonSerializer.Serialize(bodyObj);
            await context.Response.WriteAsync(body);
        }
    }
}

HTTPException.cs

using System;
using System.Net;

public class HttpException : Exception
{
    public HttpStatusCode StatusCode { get; }

    public HttpException(HttpStatusCode statusCode)
    {
        this.StatusCode = statusCode;
    }

    public HttpException(int httpStatusCode)
        : this((HttpStatusCode) httpStatusCode)
    {
    }

    public HttpException(HttpStatusCode statusCode, string message)
        : base(message)
    {
        this.StatusCode = statusCode;
    }

    public HttpException(int httpStatusCode, string message)
        : this((HttpStatusCode) httpStatusCode, message)
    {
    }

    public HttpException(HttpStatusCode statusCode, string message, Exception inner)
        : base(message, inner)
    {
    }

    public HttpException(int httpStatusCode, string message, Exception inner)
        : this((HttpStatusCode) httpStatusCode, message, inner)
    {
    }
}
Related