【问题标题】:Serilog missing exception in generic logger通用记录器中的 Serilog 缺少异常
【发布时间】:2021-02-18 13:01:48
【问题描述】:

在控制器方法中,通用记录器似乎没有定义的丰富器。

这里是控制器:

public class TestController : Controller
{
  ILogger _logger;

  public TestController(ILogger<TestController> logger)
  {
    _logger = logger;
  }

  public IActionResult action()
  {
    try
    {
      throw new NullReferenceException();
    }
    catch(Exception e)
    {
      Serilog.Log.Error(ex, "action KO");
      _logger.LogError("action KO", ex); 
    }
  }
}

appsettings.json:

{
  "Serilog": {
    "MinimumLevel": {
      "Default": "Debug",
    },
    "WriteTo": [
      {
        "Name": "Console"
      },
      {
        "Name": "File",
        "Args": {
          "path": "Log/api.log",
          "outputTemplate": "{Timestamp} [{Level:u3}] ({SourceContext}) {Message}{NewLine}{Exception}",
          "rollingInterval": "Day",
          "retainedFileCountLimit": 7
        }
      }
    ],
    "Enrich": [
      "FromLogContext",
      "WithExceptionDetails"
    ]
  }
}

主机建设:

        IHost host = Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
                webBuilder.UseUrls($"http://*:12345");
            })
            .UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration
                .ReadFrom.Configuration(hostingContext.Configuration)
                )
            .Build()
            ;

在文件/控制台中输出:

02/18/2021 12:24:57 +01:00 [ERR] () action KO
System.NullReferenceException: Object reference not set to an instance of an object.
   at App.TestController`1.action()
02/18/2021 12:24:57 +01:00 [ERR] (App.TestController) action KO

所以当我尝试使用通用记录器时,会忽略异常。当静态记录器写入它时。

我是否缺少诸如控制器记录器提供程序之类的东西,还是应该由 UseSerilog 完成?

编辑

  • 尝试UseSerilogwriteToProviders: true => 无效

  • 尝试AddSerilog 作为日志生成器 => 无效

      services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(
          new LoggerConfiguration().ReadFrom.Configuration(Configuration).CreateLogger(), true));
    
  • 试过 AddSerilogServices => 没有效果

      public static IServiceCollection AddSerilogServices(
          this IServiceCollection services,
          LoggerConfiguration configuration)
      {
          Log.Logger = configuration.CreateLogger();
          AppDomain.CurrentDomain.ProcessExit += (s, e) => Log.CloseAndFlush();
          return services.AddSingleton(Log.Logger);
      }
    

【问题讨论】:

    标签: asp.net-core-webapi serilog


    【解决方案1】:

    首先,请更改

    _logger.LogError("action KO", ex); 
    

    _logger.LogError(ex, "action KO");
    

    测试下面的 try/catch

    try
    {
        throw new NullReferenceException();
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "action KO");
    }
    

    ... 将其写入日志文件:

    02/27/2021 22:55:59 +01:00 [ERR] (MyMicroservice.Controllers.WeatherForecastController) action KO
    System.NullReferenceException: Object reference not set to an instance of an object.
       at MyMicroservice.Controllers.WeatherForecastController.Get() in C:\Prosjekter\MyMicroservice\WebApp\Controllers\WeatherForecastController.cs:line 51
    

    在使用文档检查您的配置并进行一些测试后,您添加到问题中的部分对我来说似乎没问题。

    我在测试和阅读文档期间添加了一些有趣的发现,最后有一个 Program.cs 你可能想看看。


    TL;DR:Serilog 建议两阶段初始化,以便 启动主机之前的临时记录器。下面的代码显示 如何通过微小的更改跳过第 1 阶段并在启动主机之前仍然获得记录器。

    Serilog.AspNetCore 文档https://github.com/serilog/serilog-aspnetcore#inline-initialization

    Program#Main 的开头,您将有一个Serilog.Core.Pipeline.SilentLogger

    如果您遵循建议,您将在第 1 阶段之后收到Serilog.Extensions.Hosting.ReloadableLogger

    Stage #1 看起来像这样,需要 Nuget Serilog.Extensions.Hosting

            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                .Enrich.FromLogContext()
                .WriteTo.Console()
                .CreateBootstrapLogger();
    

    为了尽量为我们节省一些代码行和额外的依赖,注释掉阶段#1,让我们尝试下面的方法,看看我们是否可以在启动虚拟主机之前获得一个初始化的记录器。

    var webHost = CreateHostBuilder(args).Build();
    

    在这一行之后,我们确实有一个Serilog.Core.Logger 的实例,这与我们在使用CreateHostBuilder(args).Build().Run() 时的最终结果相同。因此,我最终得到了下面的Program.cs,我完全省略了第 1 阶段,但保留了第 2 阶段。

    这不应该有任何副作用,医生说:

    为了解决这个问题,Serilog 支持两阶段初始化。一个初始 “bootstrap”记录器在程序启动时立即配置, 一旦主机有 已加载。

    请注意,在注释掉 doc 中的代码行后,UseSerilog 部分现在等于问题中的 config。

    我正在使用您的问题中的appsettings.json

    Startup.cs 中没有 Serilog 配置。

    using System;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using Serilog;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace MyMicroservice
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                // Serilog.Core.Pipeline.SilentLogger at this stage
    
                var webHost = CreateHostBuilder(args).Build();
    
                // Serilog.Core.Logger at this stage
    
                // proof-of-concept: This will log to file before starting the host
                var logger = webHost.Services.GetService<ILogger<Program>>();
                logger.LogWarning("Hello from Program.cs");
    
                try
                {
                    Log.Information("Starting up");
                    webHost.Run();
                }
                catch (Exception ex)
                {
                    Log.Fatal(ex, "Application start-up failed");
                }
                finally
                {
                    Log.CloseAndFlush();
                }
            }
    
            public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .UseSerilog((context, services, configuration) => configuration
                        .ReadFrom.Configuration(context.Configuration)
                        /*.ReadFrom.Services(services) not required for this case */
                        /*.Enrich.FromLogContext()  already configured in appsettings.json */
                        /*.WriteTo.Console() already configured in appsettings.json */
                     )
                    .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
        }
    }
    

    【讨论】:

    • 太棒了! _logger.LogError("action KO", ex); 正在使用 Log4net,但这不是记录的好方法。所以我想现在参数(在消息之后)没有被记录。
    猜你喜欢
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-20
    • 2020-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多