如何使用 Serilog 抑制 'An unhandled exception has occurred while executing the request.' 日志消息
How to suppress 'An unhandled exception has occurred while executing the request.' log message with Serilog
使用 app.UseExceptionHandler
和异常处理程序 lambda / 委托实现我自己的异常处理程序时(Documentation) I do not want Microsofts Exception handler middleware 记录此错误:
An unhandled exception has occurred while executing the request.
此异常消息使我的日志文件混乱,因为我自己负责使用适当的 LogLevel 记录异常。
这就是我注册自己的 ErrorHandler 的方式:
app.UseExceptionHandler(errorApp =>
errorApp.Run(ErrorHandler));
如this issue中所述过滤这个确切的消息并且没有别的是不可能以简单的方式实现的。
我如何从中间件中过滤掉这个确切的日志消息而不是其他任何东西?
我正在使用:
- ASP.NET 核心 3.1
- Serilog 3.4.0
如上述问题所述,我们可以为此使用Filter expression。不幸的是,这个问题只描述了一个已弃用包的解决方案,并且正在过滤来自中间件的所有消息,而不仅仅是提到的消息。
- 安装 Serilog.Expressions 包
- 使用此配置更新匹配
appsettings.json
:
"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.'"
}
}
]
}
- 确保您有从
appsettings.json
as documented 加载配置的匹配代码,例如:
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
这样您就可以保留来自 ExceptionHandlerMiddleware
的所有其他消息,并且只禁用冗余错误日志消息。
旁注:如果响应已经开始,我们的自定义异常处理程序将不会执行 - 但会记录警告,异常将被重新抛出并稍后记录由另一个应用程序层 - 至少在我测试的应用程序中。您可以通过在抛出异常之前的某处添加此代码来测试此行为(context
是当前的 HttpContext):
await context.Response.WriteAsync("fish");
我已经在我的程序中用一个自定义中间件替换了 app.UseExceptionHandler,它非常有用。
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)
{
}
}
使用 app.UseExceptionHandler
和异常处理程序 lambda / 委托实现我自己的异常处理程序时(Documentation) I do not want Microsofts Exception handler middleware 记录此错误:
An unhandled exception has occurred while executing the request.
此异常消息使我的日志文件混乱,因为我自己负责使用适当的 LogLevel 记录异常。
这就是我注册自己的 ErrorHandler 的方式:
app.UseExceptionHandler(errorApp =>
errorApp.Run(ErrorHandler));
如this issue中所述过滤这个确切的消息并且没有别的是不可能以简单的方式实现的。
我如何从中间件中过滤掉这个确切的日志消息而不是其他任何东西?
我正在使用:
- ASP.NET 核心 3.1
- Serilog 3.4.0
如上述问题所述,我们可以为此使用Filter expression。不幸的是,这个问题只描述了一个已弃用包的解决方案,并且正在过滤来自中间件的所有消息,而不仅仅是提到的消息。
- 安装 Serilog.Expressions 包
- 使用此配置更新匹配
appsettings.json
:
"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.'"
}
}
]
}
- 确保您有从
appsettings.json
as documented 加载配置的匹配代码,例如:
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
这样您就可以保留来自 ExceptionHandlerMiddleware
的所有其他消息,并且只禁用冗余错误日志消息。
旁注:如果响应已经开始,我们的自定义异常处理程序将不会执行 - 但会记录警告,异常将被重新抛出并稍后记录由另一个应用程序层 - 至少在我测试的应用程序中。您可以通过在抛出异常之前的某处添加此代码来测试此行为(context
是当前的 HttpContext):
await context.Response.WriteAsync("fish");
我已经在我的程序中用一个自定义中间件替换了 app.UseExceptionHandler,它非常有用。
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)
{
}
}