如何从 Serilog 中排除特定的异常类型?

Posted

技术标签:

【中文标题】如何从 Serilog 中排除特定的异常类型?【英文标题】:How to exclude specific exception types from Serilog? 【发布时间】:2019-02-04 16:02:08 【问题描述】:

我正在使用 Serilog 记录有关托管在 IIS 上的 asp.net core 2.1 应用程序的信息。发生异常时,我会通过电子邮件收到通知。问题是,一些例外情况的发生根本不会损害应用程序,我不想在每次发生时都被注意到。

Serilog 中是否有办法排除特定异常类型的记录?

编辑:

这是我在 Program.cs 中配置 Serilog 的方式:

using System;
using System.Data;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Sinks.MSSqlServer;

namespace Some_App

    public class Program
    
        public static void Main(string[] args)
        
            var configuration = new ConfigurationBuilder()
                .AddJsonFile(Environment.CurrentDirectory + "/appsettings.json")
                .Build();

            var columnOptions = new ColumnOptions 
                AdditionalDataColumns = new System.Collections.ObjectModel.Collection<DataColumn>
                
                    new DataColumn DataType = typeof (string), ColumnName = "email", DefaultValue = "myemail@myemail.com", MaxLength = 4000,
                    new DataColumn DataType = typeof (string), ColumnName = "subject", DefaultValue = "Application error", MaxLength = 4000,
                
            ;
            columnOptions.Store.Remove(StandardColumn.Level);
            columnOptions.Store.Remove(StandardColumn.LogEvent);
            columnOptions.Store.Remove(StandardColumn.Message);
            columnOptions.Store.Remove(StandardColumn.MessageTemplate);
            columnOptions.Store.Remove(StandardColumn.Properties);
            columnOptions.Store.Remove(StandardColumn.TimeStamp);
            columnOptions.Exception.ColumnName = "message";
            columnOptions.Id.ColumnName = "id";
            columnOptions.DisableTriggers = true;

            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Information)
                .Enrich.FromLogContext()
                .Filter.ByExcluding(ObjectDisposedException) //What is the right way to tell Serilog to ignore this type of exception?
                .WriteTo.RollingFile("logs\\log-Hour.txt", retainedFileCountLimit: 168)
                .WriteTo.MSSqlServer(
                connectionString: configuration.GetConnectionString("DefaultConnection"),
                tableName: "emailtosend",
                columnOptions: columnOptions,
                restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Error
                )
                .CreateLogger();

            CreateWebHostBuilder(args).Build().Run();
        

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .UseSerilog();
    

编辑 2:

@reza baiat、@GoldenAge 和@Alessandro Di Cicco:当然,您的回答可以让我处理异常,但仅限于在 Serilog 记录器之外发生的异常。 Serilog 是一个日志库,它取代了 Microsoft 的默认库,使我能够省略其余代码中的所有 Try/Catch,因为它将它们全部记录在(在我的情况下)一个日志文件和一个 SQL 表中。我正在尝试做的事情是告诉 Serilog 不要记录,例如,在关键代码中发生的ObjectDisposedException 异常。我猜 Try/Catch 在我的代码中令人困惑,所以我将从我的示例中删除它,因为它不相关。一旦应用程序构建并运行,Serilog 将捕获任何引发的异常。我在想的可能是一个额外的 Serilog 配置行,如下所示: .Filter.ByExcluding(ObjectDisposedException) 或类似的东西。有可能吗?

谢谢

【问题讨论】:

您能否详细描述一下您的 Serilog 配置(例如接收器、asp.net 核心的全局过滤器) 答案不应该被接受。它仍然没有回答您在 Edit2 中澄清的问题 您的.Filter.ByExcluding(ObjectDisposedException) 方法对我有用。事实上,它解决了我一直遇到的一个问题。我有异常处理中间件,它以某种格式记录某些类型的异常,但是当我重新抛出异常时,Serilog 会以标准格式再次记录它们。我需要重新抛出它们以保持应用程序在异常时的正确行为,但是将这些异常记录两次很烦人。所以谢谢。你的问题实际上已经回答了自己......你已经解决了我的问题! 【参考方案1】:

在声明 LoggerConfiguration 时添加过滤器应该可以正常工作:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    ...
    .Filter
      .ByExcluding(logEvent => 
         logEvent.Exception != null && 
         logEvent.Exception.GetType() == typeof(OperationCanceledException))
    ...
    .CreateLogger();

【讨论】:

【参考方案2】:

编辑

让我澄清一下,我主要考虑的是生产代码,我仍然坚持我之前编写的内容,记录器应该记录每个未处理的异常,以便能够捕获一些通常你永远无法预测的边缘情况。如果您不同意,请写下评论,以便我们讨论。对于其他环境,例如 localhost,您可以使用 Grzegorz Smulko answer

回答:

Serilog 中有没有一种方法可以排除特定的异常类型 被记录了吗?

我只会为该特定类型的异常创建一个空捕获,并且在主体内什么也不做,例如

try

    // invoke some function/s

catch (BlahException)

    // do nothing

catch (Exception e)

    Log.Fatal(ex, "Host terminated unexpectedly");
    // do something

finally

    Log.CloseAndFlush();

如果您想忽略更多异常,我会创建一些额外的函数来检查您是否要记录此类错误。

// list of error codes:
// https://docs.microsoft.com/en-gb/windows/desktop/Debug/system-error-codes
public static bool IgnoreError(int errorCode)

    switch (errorCode)
    
        case 16389:
            return true;
        case 16387:
            return true;
        default:
            return false;
    

然后在 catch 块中,您可以将异常代码传递给该方法。例如

try

    throw new ArgumentNullException("value");

catch (Exception e) 

    // Using an AND operation will retrieve the error code from the HRESULT:
    if (IgnoreError(e.HResult & 0xFFFF))
    
        return;
    
    Log.Fatal(e, "message");

我认为每个未处理的异常都应该由记录器记录,因为这是它的主要职责。我看不出忽略记录未处理的异常有什么好处,也不应该有这样的设置,这太可怕了!如果您知道在给定位置可能发生异常,那么您应该创建 try 和 catch 块以捕获尽可能多的预期异常。然后,您可以决定是否要忽略 catch 块中的某些特定异常。

【讨论】:

我想我会这样做。谢谢。【参考方案3】:

下面的代码可以帮助你

catch (Exception ex)

    if (ex.GetType() != typeof(ExceptionTypeToExclude) ) 
         Log.Fatal(ex, "Host terminated unexpectedly");
    
    return;

【讨论】:

以上是关于如何从 Serilog 中排除特定的异常类型?的主要内容,如果未能解决你的问题,请参考以下文章

Serilog 中的异常解构

客户端日志和异常处理

如何不在 Logback 中记录特定类型的异常?

SonarQube:(改为获取特定异常子类型的列表)

使用不同的类型和消息重新引发异常,保留现有信息

每个功能的 serilog 多个实例