Serilog 在通用记录器中缺少异常

Serilog missing exception in generic logger

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

这是控制器:

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 完成?

编辑

首先,请更改

_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 recommends two-stage initialization in order to have a temporary logger before starting the host. The code below shows how to skip stage #1 with a tiny change and still get a logger before starting the host.

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

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

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

第 1 阶段看起来像这样并且需要 Nuget Serilog.Extensions.Hosting

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

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

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

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

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

To address this, Serilog supports two-stage initialization. An initial "bootstrap" logger is configured immediately when the program starts, and this is replaced by the fully-configured logger once the host has loaded.

请注意,在注释掉文档中的代码行后,UseSerilog 部分现在等于问题中的配置。

我正在使用你问题中的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>());
    }
}