在带有数据库的 ASP.NET Core 5.0 应用程序中使用 Serilog 实现日志记录

Posted

技术标签:

【中文标题】在带有数据库的 ASP.NET Core 5.0 应用程序中使用 Serilog 实现日志记录【英文标题】:Implement Logging Using Serilog In ASP.NET Core 5.0 Application With Database 【发布时间】:2021-09-30 23:02:38 【问题描述】:

在我的 asp.net core 5.0 应用程序中,每当我尝试使用 serilog 执行日志记录并将日志保存到我的数据库时。但是,当我运行 api 时,它告诉我:

System.TypeInitializationException HResult=0x80131534 消息=The “PaymentService.API.Program”的类型初始化程序引发异常。 来源=PaymentService.API StackTrace:在 API\Program.cs:line 中的 PaymentService.API.Program.get_Configuration() 21 在 PaymentService.API.Program.Main(String[] args) 中 API\Program.cs:第 34 行

此异常最初是在此调用堆栈中引发的: [外部代码]

内部异常 1:FormatException:无法解析 JSON 文件。

内部异常 2:JsonReaderException:预期深度为零 JSON 有效负载的结尾。有一个开放的 JSON 对象或数组 那应该关闭。行号:7 | BytePositionInLine:1。

第 21 行是:

public static IConfiguration Configuration  get;  = new ConfigurationBuilder()
                            .SetBasePath(Directory.GetCurrentDirectory())
                            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                            .AddJsonFile($"appsettings.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production".json", optional: true)
                            .Build();

第 34 行是:

string connectionString = Configuration.GetConnectionString("Default");

我是新手,但我必须对“ASPNETCORE_ENVIRONMENT”进行任何配置吗?

之后,我尝试将自定义列添加到名为CorrelationId 的数据库中,并将CorrelationId 发送到其特定列。我正在关注这个tutorial 这样做,但我被困在他们想要为日志捕获用户的步骤上。我想做同样的事情,但使用CorrelationId 记录日志。

Program.cs:

public class Program
    

        public static IConfiguration Configuration  get;  = new ConfigurationBuilder()
                            .SetBasePath(Directory.GetCurrentDirectory())
                            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                            .AddJsonFile($"appsettings.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production".json", optional: true)
                            .Build();

        public static void Main(string[] args)
        
            

            string connectionString = Configuration.GetConnectionString("Default");

            var columnOptions = new ColumnOptions
            
                AdditionalColumns = new Collection<SqlColumn>
                
                    new SqlColumn("CorrelationId", SqlDbType.NVarChar)
                
            ; // through this columnsOptions we can dynamically add custom columns which we want to add in the db

            Log.Logger = new LoggerConfiguration()
                .Enrich.FromLogContext()
                .WriteTo.MSSqlServer(connectionString, sinkOptions: new MSSqlServerSinkOptions  TableName = "PaymentLogs" 
                , null, null, LogEventLevel.Information, null, columnOptions: columnOptions, null, null)
                .CreateLogger();

            CreateHostBuilder(args).Build().Run();
        

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                
                    webBuilder.UseStartup<Startup>().UseSerilog();
                );
    

Startup.cs:

    public class Startup
    
        public Startup(IConfiguration configuration)
        
            Configuration = configuration;
        

        public IConfiguration Configuration  get; 

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        

            services.AddControllers();
            services.AddSwaggerGen(c =>
            
                c.SwaggerDoc("v1", new OpenApiInfo  Title = "PaymentService.API", Version = "v1" );
            );

            services.AddHttpContextAccessor();
        

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        

            app.UseMiddleware<LogHeaderMiddleware>();

            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "PaymentService.API v1"));
            

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

  /*          app.Use(async (httpContext, next) => 
            
                var correlationId = httpContext.Session. // need to find a way to map correlationId and send it to the logs
            )*/

            app.UseEndpoints(endpoints =>
            
                endpoints.MapControllers();
            );
        
    

appsettings.json


  "ConnectionStrings": 
    "Default": "Data Source=.\\SQLExpress;Database=ElasticSearchService;Trusted_Connection=True;"
  ,
  "Serilog": 
    "MinimumLevel": "Information",
  "AllowedHosts": "*"

PaymentController

[Route("api/[controller]")]
    [ApiController]
    public class PaymentController : ControllerBase
    

        private readonly ILogger<PaymentServicesController> _logger;

        public PaymentServicesController(ILogger<PaymentServicesController> logger)
        
            _logger = logger;
        

        // GET: api/<PaymentServices>
        [HttpGet]
        [Route("payment")]
        public void MakePayment()
        

            _logger.LogInformation("PAYMENT METHOD INVOLKED!!!");


        
       
    

这里header 将保存我需要的correlationId,以便我可以将其发送到数据库。

public class LogHeaderMiddleware
    
        private readonly RequestDelegate _next;

        public LogHeaderMiddleware(RequestDelegate next)
        
            _next = next;
        

        public async Task InvokeAsync(HttpContext context)
        
            var header = context.Request.Headers["CorrelationId"];

            if (header.Count > 0)
            
                var logger = context.RequestServices.GetRequiredService<ILogger<LogHeaderMiddleware>>();

                using (logger.BeginScope("@CorrelationId", header[0]))
                
                    await _next(context);
                
            
            else
            
                await _next(context);
            
        
    

【问题讨论】:

您不应该使用静态配置对象。您应该在 HostBuilder 上使用 ConfigureHostConfiguration 和 ConfigureAppConfiguration 的配置构建 API。然后,您可以访问配置对象。 UseSerilog 还具有与 HostBuilderContext/Configuration 一起使用的重载。是的,您的 json 格式不正确,但您还需要清理 Program.cs,以便它遵循 HostBuilder API 的标准使用模式,其中有 100 个示例用于整个网络(Serilog 也有一些,请检查出 Serilog.Extensions.Hosting 或 Serilog.Settings.Configuration) @pinkfloydx33 感谢您的提醒。我不知道不使用静态配置对象,因为我是新手,并且正在按照教程在 asp.net core 5.0 中使用 serilog 实现日志记录。所以我需要在HostBuilder 上使用带有ConfigureHostConfigurationConfigureAppConfiguration 的serilog 搜索日志记录示例?我应该将.UseSerilog() 放在.ConfigureWebHostDefaults() 上方吗? 关注 Serilog.AspNetCore 存储库中的示例github.com/serilog/serilog-aspnetcore(查看示例文件夹,而不仅仅是自述文件)。另请注意,如果您添加他们的 UseSerilogRequestLogging 那么您就不需要中间件(请参阅该部分的自述文件) 【参考方案1】:

您的 JSON 文件中缺少 Serilog 对象的右大括号,导致 JSON 格式错误。因此出现异常:内部异常 1:FormatException:无法解析 JSON 文件。

【讨论】:

以上是关于在带有数据库的 ASP.NET Core 5.0 应用程序中使用 Serilog 实现日志记录的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Asp.Net Core Mvc 5.0 中将 sql 数据库与 ado.net 连接?

通过 ASP.NET Core 3.1/5.0 MVC 中的脚本使用选定的 ''value='' 填充数据库

如何按照存储库模式在 Asp.Net Core 5.0 项目上实现 .Net Core Identity?

Asp.Net Core 5.0 Deploy by Plesk Panel 问题

ASP.NET CORE 5.0 Identity 显示当前登录用户

将 ASP.NET Core 部署到 AWS Elastic Beanstalk,必须包含带有“.runtimeconfig.json”后缀错误的文件