首页 > 解决方案 > 如何使用 Serilog 将一个请求中的所有日志消息记录到单个条目

问题描述

想要检查是否可以将一个请求中的所有消息记录为单个条目。

我在看这个https://andrewlock.net/using-serilog-aspnetcore-in-asp-net-core-3-reducing-log-verbosity/但不是我想要的。

例如,我有一个 .Net Core Web 应用程序

public MyController(IService myService, ILogger<MyController> logger)
        {
            _myService = myService ?? throw new ArgumentNullException(nameof(myService));
            _logger = logger;
        }

[HttpGet]
        public async Task<IActionResult> Get([FromQuery( Name = "Id")] List<string> ids)
        {          
            _myService.DoSomething();
            _logger.Log(LogLevel.Information, $"Some controller logs here");
            return new OkObjectResult();
        }

对于 IService:

public MyService(ILogger<MyService> logger)
        {
            _logger = logger;
        }

public DoSomething()
{
_logger.Log(LogLevel.Information, $"Some service logs here");
}

Serilog 是否能够在一个条目下记录“这里有一些控制器日志”和“这里有一些服务日志”?而不是两个条目?

谢谢

标签: logging.net-coreserilog

解决方案


您可以在您的应用程序上添加中间件。它将捕获传入和传出的顶级请求。

public class RequestResponseLoggingMiddleware
{
    private readonly RequestDelegate _next;

    public RequestResponseLoggingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        //First, get the incoming request
        var request = await FormatRequest(context.Request);

        //Copy a pointer to the original response body stream
        var originalBodyStream = context.Response.Body;

        //Create a new memory stream...
        using (var responseBody = new MemoryStream())
        {
            //...and use that for the temporary response body
            context.Response.Body = responseBody;

            //Continue down the Middleware pipeline, eventually returning to this class
            await _next(context);

            //Format the response from the server
            var response = await FormatResponse(context.Response);

            //TODO: Save log to chosen datastore

            //Copy the contents of the new memory stream (which contains the response) to the original stream, which is then returned to the client.
            await responseBody.CopyToAsync(originalBodyStream);
        }
    }

    private async Task<string> FormatRequest(HttpRequest request)
    {
        var body = request.Body;

        //This line allows us to set the reader for the request back at the beginning of its stream.
        request.EnableBuffering();

        //We now need to read the request stream.  First, we create a new byte[] with the same length as the request stream...
        var buffer = new byte[Convert.ToInt32(request.ContentLength)];

        //...Then we copy the entire request stream into the new buffer.
        await request.Body.ReadAsync(buffer, 0, buffer.Length);

        //We convert the byte[] into a string using UTF8 encoding...
        var bodyAsText = Encoding.UTF8.GetString(buffer);

        //..and finally, assign the read body back to the request body, which is allowed because of EnableRewind()
        request.Body = body;

        return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
    }

    private async Task<string> FormatResponse(HttpResponse response)
    {
        //We need to read the response stream from the beginning...
        response.Body.Seek(0, SeekOrigin.Begin);

        //...and copy it into a string
        string text = await new StreamReader(response.Body).ReadToEndAsync();

        //We need to reset the reader for the response so that the client can read it.
        response.Body.Seek(0, SeekOrigin.Begin);

        //Return the string for the response, including the status code (e.g. 200, 404, 401, etc.)
        return $"{response.StatusCode}: {text}";
    }
}

并且不要忘记在您的启动文件中注入中间件。

app.UseMiddleware<RequestResponseLoggingMiddleware>();

您可以像这样配置您的 serilog 文件:

"Serilog": {
"MinimumLevel": "Error",
"WriteTo": [
  {
    "Name": "RollingFile",
    "Args": {
      "pathFormat": "\\log-{Date}.log",
      "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}"
    }
  },
]

},

它将写入所选目的地的文件。

请注意,这里是最低级别的 serilog。 不同的最低级别日志 Serilog

也许它会帮助你。


推荐阅读