从 .NET 6 Minimal API 调用的类库方法进行 Serilog 日志记录

Fri*_*z45 4 asp.net-web-api serilog asp.net-core .net-6.0

我设置了一个 .Net 6 Core Minimal API,以使用 SeriLog 登录到 MS SQL Server。在我的类库中,仅当我修改类库中的构造函数时,我才能够使用 SeriLog 进行日志记录。我试图避免修改类库类或方法的构造函数。

根据我使用控制台应用程序的经验,如果我在主 Program.cs 中设置 SeriLog,那么我可以在类库中的任何类中使用日志记录,而无需将记录器传递给构造函数。因此,我可以在类库中的任何位置使用 Log.Information("my message") 并且它可以工作。我试图在 .Net 6 最小 API 项目中使用我的 Program.cs 实现相同的目标。

我觉得通过查看有关该主题的其他问题应该可以做到。特别是这个,其中的答案指出:

您无需在类库中执行任何操作。只有主应用程序具有组合根(应用程序生命周期中可以设置对象图的最早点)。

因此,根据上面的内容,在我的 API Program.CS 中,我有以下代码(我已在注释中指出了哪些有效,哪些无效):

    //Configure SeriLog
    builder.Logging.ClearProviders();
    var appSettings = new ConfigurationBuilder()
       .SetBasePath(Directory.GetCurrentDirectory())
       .AddJsonFile("appsettings.json")
       .Build();
    
    var logDB = 
    builder.Configuration.GetSection("ConnectionStrings:Default").Value;
    var sinkOpts = new MSSqlServerSinkOptions { TableName = "Logs" };
    var columnOptions = new ColumnOptions();
    
    var logger = new LoggerConfiguration()
       .MinimumLevel.Override("Microsoft", 
    Serilog.Events.LogEventLevel.Information)
      .WriteTo.MSSqlServer(
          connectionString: logDB,        
          sinkOptions: sinkOpts,
          columnOptions: columnOptions,
          appConfiguration: appSettings
        ).CreateLogger();
    
    builder.Logging.AddSerilog(logger);

    //Depency injection for Class Library method 
    //LoggerTestClass is a class in my Class Library project
    builder.Services.AddScoped<ILoggerTestClass, LoggerTestClass>();

    var app = builder.Build();
    app.ConfigureTestAPI();
Run Code Online (Sandbox Code Playgroud)

方法“ConfigureTestAPI()”位于扩展类中,如下所示:

    public static class API_Test
    {

    public static void ConfigureTestAPI(this WebApplication app) 
    //Extension method for app
    {               
        app.MapGet("/test/", GetTest);
        
    }

    private static async Task<IResult> GetTest(int id, 
    ILogger<LoggerTestClass> logger, ILoggerTestClass testClass)
    {
        try
        {
            try
            {
                //This works
                logger.LogInformation("Starting test now");  

                //This does NOT work
                Log.Information("Using Log. directly"); 

                
                testClass.Test();  //Call to class library method

                logger.LogInformation("Test finished");  //Also works
                return Results.Ok("OK");
            }
            catch (Exception ex)
            {
                return Results.Problem(ex.Message);
            }
        }
        catch (Exception ex)
        {
            return Results.Problem(ex.Message);
        }
     }
    }
Run Code Online (Sandbox Code Playgroud)

最后这是我的类库中带有测试方法的类:

        namespace TestingLib.Testing;
    public class LoggerTestClass : ILoggerTestClass
    {
    
        private Microsoft.Extensions.Logging.ILogger _logger;
    
        public LoggerTestClass(ILogger<LoggerTestClass> logger)
        {
            _logger = logger;
        }
    
        public void Test()
        {
            try
            {
    
               //Does not work
               Log.Information("Test logging from class library using Log.");
    
               //Does not work
               Log.Logger.Information("In Test Class in DLL. Trying loging with [Log.Logger.Information]");
    
               //This works
               _logger.LogInformation("In Test Class in DLL. Trying loging with [_logger.LogInformation]");
            }
            catch (Exception ex)
            {
                Log.Error("An error in class library");
            }
        }
    
    }

Run Code Online (Sandbox Code Playgroud)

Fri*_*z45 5

我发现问题出在我的 API Program.cs 文件中缺少一行代码。我需要添加:“Log.Logger = logger;” 设置 SeriLog 记录器后。

工作解决方案的代码如下。

在我的 .NET6 API Program.cs 文件中,我有以下内容:

var builder = WebApplication.CreateBuilder(args);

//Configure SeriLog
builder.Logging.ClearProviders();
var appSettings = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .Build();

var logDB = builder.Configuration.GetSection("ConnectionStrings:Default").Value;
var sinkOpts = new MSSqlServerSinkOptions { TableName = "Logs" };
var columnOptions = new ColumnOptions
{
    AdditionalColumns = new Collection<SqlColumn>
    {
        new SqlColumn("UserID", SqlDbType.Int),
        new SqlColumn("RunTag", SqlDbType.NVarChar),
        new SqlColumn("CustomType", SqlDbType.NVarChar)
    }
};

var logger = new LoggerConfiguration()
    .MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Information)
    .WriteTo.MSSqlServer(
        connectionString: logDB,
        sinkOptions: sinkOpts,
        columnOptions: columnOptions,
        appConfiguration: appSettings
    ).CreateLogger();

builder.Logging.AddSerilog(logger);

//NOTE that this is not needed
//builder.Services.AddScoped<TestingLib.Testing.ILoggerTestClass, TestingLib.Testing.LoggerTestClass>();
Run Code Online (Sandbox Code Playgroud)

然后在这个文件的下方我有这个:

Log.Logger = logger; //This was the line I was missing!

var app = builder.Build();
Run Code Online (Sandbox Code Playgroud)

我调用类库的 API Get 方法现在看起来像这样(如果与问题中的原始方法相比,您将看到不再需要传递 ILogger):

private static async Task<IResult> GetTest(int id)
    {
        try
        {
            try
            {
                string runTag = "In API GetTest(id)";
                string custom = "Calling from API method";                
                Log.Information("{Message}-{RunTag}-{CustomType}", "Message logging in API method", runTag, custom);
                
                //Call to Class Library Method - no need to pass ILogger
                LoggerTestClass testClass = new LoggerTestClass();
                testClass.Test();
               
                return Results.Ok("OK");
            }
            catch (Exception ex)
            {
                return Results.Problem(ex.Message);
            }
        }
        catch (Exception ex)
        {
            return Results.Problem(ex.Message);
        }
    }
Run Code Online (Sandbox Code Playgroud)

最后,这是我的类库中的完整测试类。类库包含对 SeriLog 的引用:

using Serilog;

namespace TestingLib.Testing;
public class LoggerTestClass //: ILoggerTestClass
{

    public LoggerTestClass()
    {
        //No need to pass in ILogger to class
    }

    public void Test()
    {
        try
        {
            string runTag = "In Class Library Method)";
            string custom = "Calling class library method Test()";
            Log.Information("{Message}-{RunTag}-{CustomType}", "Message logging in Class Library", runTag, custom);            
        }
        catch (Exception ex)
        {
            Log.Error("An error in class library");
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

我发现这非常适合我的项目。类库中的日志记录直接进入我的 SQL 数据库,其中包含自定义列等。