简体   繁体   English

Serilog 文本格式包含有效 json output

[英]Serilog text formatting to contain valid json output

I am logging with Serilog and have a (text) file sink with an output template and a console logger.我正在使用 Serilog 进行日志记录,并且有一个带有 output 模板和控制台记录器的(文本)文件接收器。

The output template of the text file logger:文本文件记录器的output模板:

"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff}|{Level:u3}|{SourceContext}|{Message}{NewLine:1}{Exception:1}"

The output template of the console logger:控制台记录器的 output 模板:

"outputTemplate": "{Timestamp:HH:mm:ss.fff} {Level:u3} {SourceContext} {Message:lj}{NewLine:1}{Exception:1}"

Now we try (for the sake of easiness) to log some class data in JSON format.现在我们尝试(为了简单起见)以 JSON 格式记录一些 class 数据。 We want it as JSON beacuse we try to extract the data later from the log files.我们希望它为 JSON,因为我们稍后会尝试从日志文件中提取数据。

The sample logging code is easy enough:示例日志记录代码非常简单:

protected void DoTestLogs()
{

  Logger.LogInformation("HERE IT IS");

  Logger.LogInformation("HERE IT IS {@Foo}", new { Foo = "bar" });

  int x = 1;
  string y = "2";

  Logger.LogInformation("HERE IS ANOTHER ONE {@Other}", new { x, y });

  var data = new My.Data
  {
    Name = "my data",
    IsValid = true,
    Value = 27.82859584
  };

  Logger.LogInformation("HERE IS DATA {@Data}", data);
  
}

The console output looks valid as expected (except the serialzed type of data which I could ignore)控制台 output 看起来像预期的那样有效(除了我可以忽略的序列化数据类型)

08:09:38.880 INF My.Test HERE IT IS
08:09:38.884 INF My.Test HERE IT IS {"Foo": "bar"}
08:09:38.888 INF My.Test HERE IS ANOTHER ONE {"x": 1, "y": "2"}
08:09:38.889 INF My.Test HERE IS DATA {"Name": "my data", "Value": 27.82859584, "IsValid": true, "$type": "Data"}

But the json in the text file log seems to be "randomly" invalid.但是文本文件日志中的 json 似乎是“随机”无效的。 I can't explain why quotes are there or not我无法解释为什么有或没有引号

2023-01-27 08:09:38.880|INF|My.Test|HERE IT IS
2023-01-27 08:09:38.884|INF|My.Test|HERE IT IS { Foo: "bar" }
2023-01-27 08:09:38.888|INF|My.Test|HERE IS ANOTHER ONE { x: 1, y: "2" }
2023-01-27 08:09:38.889|INF|My.Test|HERE IS DATA Data { Name: "my data", Value: 27.82859584, IsValid: True }

Does anyone have a hint how I could trick the file sink to put quotes around the names like Foo, x, y... as the console sink does?有没有人提示我如何像控制台接收器那样欺骗文件接收器在名称周围加上引号,例如 Foo、x、y...?


In case anyone want's to reproduce it and doesn't have a serilog setup at hand: here is the setup.如果有人想要复制它并且手头没有 serilog 设置:这里是设置。

First create a new project from the "ASP.NET Core Web API" template.首先从“ASP.NET Core Web API”模板创建一个新项目。 Then delete the WeatherForecast.cs and the WeatherForecastController.然后删除 WeatherForecast.cs 和 WeatherForecastController。

Add the Serilog nuget to the cs.proj将 Serilog nuget 添加到 cs.proj

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
    <PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
  </ItemGroup>

</Project>

Configure Serilog via the appsettings.json通过 appsettings.json 配置 Serilog

{

  "AllowedHosts": "*",

  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft.Hosting.Lifetime": "Information",
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "Enrich": [ "FromLogContext" ],
    "WriteTo": [
      {
        "Name": "Console",
        "Args": {
          "outputTemplate": "{Timestamp:HH:mm:ss.fff} {Level:u3} {SourceContext} {Message:lj}{NewLine:1}{Exception:1}"
        }
      },
      {
        "Name": "File",
        "Args": {
          "path": "logs/log.log",
          "rollingInterval": "Day",
          "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff}|{Level:u3}|{SourceContext}|{Message}{NewLine:1}{Exception:1}"
        }
      }
    ]
  }
}

Integrate Serilog in the Program.cs在 Program.cs 中集成 Serilog

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Stackoverflow.Question75255058
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseSerilog
                (
                    (context, services, configuration) =>
                        configuration                            
                            .ReadFrom.Configuration(context.Configuration)
                            .ReadFrom.Services(services)
                )
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

No need for changes in the StartUp.cs无需更改 StartUp.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Stackoverflow.Question75255058
{
    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 = "Stackoverflow.Question75255058", Version = "v1" });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Stackoverflow.Question75255058 v1"));
            }

            app.UseRouting();

            app.UseAuthorization();

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

Create an new OutputController and the My.Data class under controllers在控制器下创建一个新的 OutputController 和 My.Data class

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Stackoverflow.Question75255058.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class LogOutputController : ControllerBase
    {

        public readonly ILogger<LogOutputController> Logger;

        public LogOutputController(ILogger<LogOutputController> logger)
        {
            Logger = logger;
        }

        [HttpGet]
        public IActionResult Get()
        {
            Logger.LogInformation("HERE IT IS");

            Logger.LogInformation("HERE IT IS {@Foo}", new { Foo = "bar" });

            int x = 1;
            string y = "2";

            Logger.LogInformation("HERE IS ANOTHER ONE {@Other}", new { x, y });

            var data = new My.Data
            {
                Name = "my data",
                IsValid = true,
                Value = 27.82859584
            };

            Logger.LogInformation("HERE IS DATA {@Data}", data);

            return NoContent();
        }
    }
}

namespace My
{
    public class Data
    {
        public string Name { get; set; }
        public bool IsValid { get; set; }
        public double Value { get; set; }
    }
}

Start the application and execute the controller action provided by Swagger.启动应用程序并执行Swagger提供的controller动作。

The difference is in your outputTemplate - {Message:lj} produces literal strings and JSON objects, while the default {Message} produces quoted strings and the earlier C#-like object format.不同之处在于您的outputTemplate - {Message:lj}生成文字字符串和 JSON 对象,而默认{Message}生成带引号的字符串和早期的 C#-like object 格式。

Using the same output template with each sink should produce the same result.对每个接收器使用相同的 output 模板应该会产生相同的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM