Christoph Sonntag
Christoph Sonntag

Reputation: 4629

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

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:

Upvotes: 10

Views: 4396

Answers (3)

Janeks Malinovskis
Janeks Malinovskis

Reputation: 560

I think I found much simpler solution:

"Serilog": {
  "MinimumLevel": {
    ...
    "Override": {
      ...
      "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "Fatal"
    }
  }

Trick is that ExceptionHandlerMiddleware logs with lover level=Error, but level=Fatal is above.

Upvotes: 0

Kiruahxh
Kiruahxh

Reputation: 2015

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

Upvotes: 1

Christoph Sonntag
Christoph Sonntag

Reputation: 4629

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

Upvotes: 4

Related Questions