Serilog 在达到最大文件大小之前创建文件

Serilog creates files before reaching max file size

我使用 .NET core 2.1 创建了一个 API,并使用 Serilog 记录所有方法类型在 API 级别上的每个响应和请求:Get、Post .. . 尽管响应代码;下面是我添加到中间件的代码 class:

var request = context.Request;
    var stopWatch = Stopwatch.StartNew();
    var requestTime = DateTime.UtcNow;
    var requestBodyContent = await ReadRequestBody(request);
    var originalBodyStream = context.Response.Body;
    generalError = Configuration["generalError"];

        using (var memStream = new MemoryStream())
        {
            HttpResponse response = context.Response;
            response.Body = memStream;
            await next(context);
            stopWatch.Stop();

            string responseBodyContent = null;
            responseBodyContent = ReadResponseBody(memStream);
            memStream.CopyTo(originalBodyStream);

            string infoToLog = "\r\n" +
                               "------------------------ \r\n" +
                               "Elapsed Milliseconds: " + stopWatch.ElapsedMilliseconds + "\r\n" +
                               "Status Code: " + context.Response.StatusCode + "\r\n" +
                               "Method: " + request.Method + "\r\n" +
                               "Path: " + request.Path + "\r\n" +
                               "QueryString: " + request.QueryString.ToString() + "\r\n" +
                               "Request Body: " + requestBodyContent + "\r\n" +
                               "Response Body: " + responseBodyContent + "\r\n" +
                               "------------------------" + "\r\n\r\n";

            Log.Logger = new LoggerConfiguration()
                .WriteTo.Async(a => a.File("Logs/myapp.log",
                          rollOnFileSizeLimit: true,                                  
                          fileSizeLimitBytes: Convert.ToInt32(Configuration["MaxLogFileSize"])))
                            //shared:true,
                .CreateLogger();

            if (context.Response.StatusCode == 200)
            {
                Log.Information(infoToLog);                        
            }
            else {
                Log.Error(infoToLog);                        
            }

            Log.CloseAndFlush();

            context.Response.Body = originalBodyStream;
        }

因此,如您所见,我实施了一种滚动日志记录方法,该方法仅在达到最大文件大小问题后才创建新文件。 主要问题是,在发布之后,当 API 从移动应用程序调用时,我们意识到同时创建了许多日志文件而没有达到最大文件大小,更具体地说,当应该使用多种方法时同时登录。无论如何我们可以解决这个问题吗?

您正在中间件中的每个 Web 请求上实例化一个新的记录器,我认为您也在使用 Log.CloseAndFlush(); 关闭日志文件,如果我记得文档的话我认为它应该是一个单例。

据我所知,您只需要在应用程序期间创建记录器 bootstrap。我就是这样做的(program.cs with .NET core 2.2):

    public class Program
    {
        private static readonly string _applicationName = System.Reflection.Assembly.GetEntryAssembly().GetName().Name;
        public static IConfiguration Configuration = new ConfigurationBuilder()
                  .SetBasePath(Directory.GetCurrentDirectory())
                  .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                  .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", reloadOnChange: true, optional: true)
                  .AddEnvironmentVariables()
                  .Build();

    public static int Main(string[] args)
    {
        // serilog
        Log.Logger = new LoggerConfiguration()
                        .ReadFrom.Configuration(Configuration)
                        .Enrich.FromLogContext()
                        .CreateLogger();

        try
        {
            if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")))
                throw new Exception("Hosting environment variable not set");

            Log.Information($"Bootstrapping application: {_applicationName}");
            CreateWebHostBuilder(args).Build().Run();
            Log.Information($"Gracefully closing application: {_applicationName}");
            return 0; // returning a zero, indicates a successful shutdown.
        }
        catch (Exception e)
        {
            Log.Warning($"Host {_applicationName} - terminated unexpectedly.");
            Log.Fatal(e.Message);
            return 1; // returning a none-zero, indicates failure.
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseConfiguration(Configuration)
            .UseSerilog();
}

因此,当您确实想要记录某些内容时,只需导入 using Serilog; 并调用 Log.Information("Your message.")

更多信息可以在底部找到:https://serilog.net/ or at https://github.com/serilog/serilog

编辑

这是我的配置,将控制台指定为接收器(您可以添加更多类似文件、elasticsearch、seq...其他)并且我还覆盖了 Microsoft 的默认记录器以仅显示日志级别警告或更高级别:

  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },  
    "WriteTo": [
      {
        "Name": "Console",
        "Args": {
          "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} {SourceContext}>{NewLine}{Exception}"
        }
      }
    ]
  },

更新

嗯,你把你的日志放在教程说 //TODO: Save log to chosen datastore 的地方,这只是 serilog 术语 Log.Information("Your message"); 的意思。我在我的例子中用 // serilog 标记了 serilog 部分 - 所以如果你想配置记录器也记录到文件,就像你的一样添加你的配置部分:

// serilog
Log.Logger = new LoggerConfiguration()
                 .ReadFrom.Configuration(Configuration)
                 .Enrich.FromLogContext()
                 .WriteTo.Async(a => a.File("Logs/myapp.log",
                           rollOnFileSizeLimit: true,                                  
                           fileSizeLimitBytes: Convert.ToInt32(Configuration["MaxLogFileSize"])))
                 .CreateLogger();

您还可以通过配置 appsettings.json 添加该部分 - 如果您不需要此部分 .ReadFrom.Configuration(Configuration).Enrich.FromLogContext() 只需将其删除。

因此您的中间件代码将如下所示:

    var request = context.Request;
    var stopWatch = Stopwatch.StartNew();
    var requestTime = DateTime.UtcNow;
    var requestBodyContent = await ReadRequestBody(request);
    var originalBodyStream = context.Response.Body;
    generalError = Configuration["generalError"];

        using (var memStream = new MemoryStream())
        {
            HttpResponse response = context.Response;
            response.Body = memStream;
            await next(context);
            stopWatch.Stop();

            string responseBodyContent = null;
            responseBodyContent = ReadResponseBody(memStream);
            memStream.CopyTo(originalBodyStream);

            string infoToLog = "\r\n" +
                               "------------------------ \r\n" +
                               "Elapsed Milliseconds: " + stopWatch.ElapsedMilliseconds + "\r\n" +
                               "Status Code: " + context.Response.StatusCode + "\r\n" +
                               "Method: " + request.Method + "\r\n" +
                               "Path: " + request.Path + "\r\n" +
                               "QueryString: " + request.QueryString.ToString() + "\r\n" +
                               "Request Body: " + requestBodyContent + "\r\n" +
                               "Response Body: " + responseBodyContent + "\r\n" +
                               "------------------------" + "\r\n\r\n";

            if (context.Response.StatusCode == 200)
            {
                Log.Information(infoToLog);                        
            }
            else 
            {
                Log.Error(infoToLog);                        
            }    

            context.Response.Body = originalBodyStream;
        }

另请注意,我在 program.cs 中对此方法 public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 调用了 .UseSerilog(); 以覆盖 Microsfts 默认记录器(我更喜欢所有日志的 serilog)。