logging - 如何使用 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 是否能够在一个条目下记录“这里有一些控制器日志”和“这里有一些服务日志”?而不是两个条目?
谢谢
解决方案
您可以在您的应用程序上添加中间件。它将捕获传入和传出的顶级请求。
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
也许它会帮助你。
推荐阅读
- python - 如何将此基于列表的代码转换为基于numpy数组的代码?
- python - 如何使用 pandas 将 csv 转换为具有多级嵌套的 json
- reactjs - 如何基于悬停而不是单击制作 Material-UI 菜单
- c# - 如何在 RethinkDB 中获取给定谓词的元素索引
- c - 用其他字符替换重复出现的相邻字符
- scala - Spark Standalone Cluster deployMode =“cluster”:我的驱动程序在哪里?
- git - 如何将我的分支附加到我的主分支
- python - 如何在具有多个索引/数组的变量中仅遍历字典的某些元素?
- matlab - Matlab:在同一图中使用情节和符号学(Matlab 2014)
- debian - 在 Debian 上安装 php-gd ext