Ogu*_*cin 7 c# serilog asp.net-core
我有一个基于 .net core 3.1 的 Web API。
我使用 SeriLog 库作为记录器。
这是我的 SeriLog 配置。Serilog 已从“appsettings.json”配置。
如果存在,我需要将“请求正文”参数添加到日志的输出中。有什么办法可以配置这个。另外,我分享了我的日志输出。
小智 10
options.EnrichDiagnosticContext您可以通过从ReadBodyFromRequest方法或从 matthewd98FormatRequest的方法实现逻辑来记录正文
,但是您还需要将正文添加到模板消息中,因为 Serilog 中的默认模板消息是
并且它不包含任何占位符
HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms Body
app.UseSerilogRequestLogging(options =>
{
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
// string body = your logic to get body from httpContext.Request.Body
diagnosticContext.Set("Body", body);
};
options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} {Body} responded {StatusCode} in {Elapsed:0.0000}";
});
Run Code Online (Sandbox Code Playgroud)
小智 8
我编写了一个自定义中间件来捕获 HTTP 请求和响应。它与 ASP.NET Core 3.X 兼容,也应该适用于 2.X 和 .NET 5.0,尽管我还没有用这些框架版本测试过它。
这是我的 git repo 的链接:https : //github.com/matthew-daddario/AspNetCoreRequestResponseLogger
相关代码是这样的:
public class RequestResponseLoggerMiddleware
{
private readonly RequestDelegate _next;
private readonly bool _isRequestResponseLoggingEnabled;
public RequestResponseLoggerMiddleware(RequestDelegate next, IConfiguration config)
{
_next = next;
_isRequestResponseLoggingEnabled = config.GetValue<bool>("EnableRequestResponseLogging", false);
}
public async Task InvokeAsync(HttpContext httpContext)
{
// Middleware is enabled only when the EnableRequestResponseLogging config value is set.
if (_isRequestResponseLoggingEnabled)
{
Console.WriteLine($"HTTP request information:\n" +
$"\tMethod: {httpContext.Request.Method}\n" +
$"\tPath: {httpContext.Request.Path}\n" +
$"\tQueryString: {httpContext.Request.QueryString}\n" +
$"\tHeaders: {FormatHeaders(httpContext.Request.Headers)}\n" +
$"\tSchema: {httpContext.Request.Scheme}\n" +
$"\tHost: {httpContext.Request.Host}\n" +
$"\tBody: {await ReadBodyFromRequest(httpContext.Request)}");
// Temporarily replace the HttpResponseStream, which is a write-only stream, with a MemoryStream to capture it's value in-flight.
var originalResponseBody = httpContext.Response.Body;
using var newResponseBody = new MemoryStream();
httpContext.Response.Body = newResponseBody;
// Call the next middleware in the pipeline
await _next(httpContext);
newResponseBody.Seek(0, SeekOrigin.Begin);
var responseBodyText = await new StreamReader(httpContext.Response.Body).ReadToEndAsync();
Console.WriteLine($"HTTP request information:\n" +
$"\tStatusCode: {httpContext.Response.StatusCode}\n" +
$"\tContentType: {httpContext.Response.ContentType}\n" +
$"\tHeaders: {FormatHeaders(httpContext.Response.Headers)}\n" +
$"\tBody: {responseBodyText}");
newResponseBody.Seek(0, SeekOrigin.Begin);
await newResponseBody.CopyToAsync(originalResponseBody);
}
else
{
await _next(httpContext);
}
}
private static string FormatHeaders(IHeaderDictionary headers) => string.Join(", ", headers.Select(kvp => $"{{{kvp.Key}: {string.Join(", ", kvp.Value)}}}"));
private static async Task<string> ReadBodyFromRequest(HttpRequest request)
{
// Ensure the request's body can be read multiple times (for the next middlewares in the pipeline).
request.EnableBuffering();
using var streamReader = new StreamReader(request.Body, leaveOpen: true);
var requestBody = await streamReader.ReadToEndAsync();
// Reset the request's body stream position for next middleware in the pipeline.
request.Body.Position = 0;
return requestBody;
}
}
Run Code Online (Sandbox Code Playgroud)
如果您可以升级到 dotnet core 6,则无需编写自己的请求/响应主体日志记录中间件。微软在aspnet框架中有一个。
这可以设置为与serilog一起使用,尽管我发现serilog文档是错误的,因为启动代码需要Microsoft的UseHttpLogging和Serilogs UseSerilogRequestLogging(Serilog doco说只使用他们的...没有写主体) Serilog文档 Microsoft的新aspnet dotnet 6 日志记录文档
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app
.UseHttpsRedirection()
.UseRouting()
.UseHttpLogging()
.UseSerilogRequestLogging()
.UseSwagger()
.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Some.api"))
.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Run Code Online (Sandbox Code Playgroud)
要完成此操作(根据微软日志记录中的 doco),您可以设置诸如
services.AddHttpLogging(logging =>
{
logging.LoggingFields = HttpLoggingFields.All;
logging.RequestHeaders.Add(HeaderNames.Accept);
logging.RequestHeaders.Add(HeaderNames.ContentType);
logging.RequestHeaders.Add(HeaderNames.ContentDisposition);
logging.RequestHeaders.Add(HeaderNames.ContentEncoding);
logging.RequestHeaders.Add(HeaderNames.ContentLength);
logging.MediaTypeOptions.AddText("application/json");
logging.MediaTypeOptions.AddText("multipart/form-data");
logging.RequestBodyLogLimit = 1024;
logging.ResponseBodyLogLimit = 1024;
});
Run Code Online (Sandbox Code Playgroud)
并且不要忘记用于 Microsoft 日志记录和 Serilog 的 appsettings.json。例如:
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Information",
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
}
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore": "Information",
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
}
},
"WriteTo": [
{
"Name": "File",
"Args": { "path": "c:/data/logs/aspnetcore-log-.txt", "rollingInterval": "Day" }
}
]
},
Run Code Online (Sandbox Code Playgroud)
并且需要调用UseSerilog扩展,如:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console())
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
Run Code Online (Sandbox Code Playgroud)
@Alexander 的上述回答确实做得很好,但它没有解释如何获取身体,这是很难正确完成的。
这是完整的答案
首先,您需要一个新的中间件
public class ResetTheBodyStreamMiddleware
{
private readonly RequestDelegate _next;
public ResetTheBodyStreamMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Still enable buffering before anything reads
context.Request.EnableBuffering();
// Call the next delegate/middleware in the pipeline
await _next(context);
// Reset the request body stream position to the start so we can read it
context.Request.Body.Position = 0;
}
}
Run Code Online (Sandbox Code Playgroud)
然后您需要注册中间件然后注册该UseSerilogRequestLogging方法。
app.UseMiddleware<ResetTheBodyStreamMiddleware>();
app.UseSerilogRequestLogging(options =>
options.EnrichDiagnosticContext = async (diagnosticContext, context) =>
{
// Reset the request body stream position to the start so we can read it
context.Request.Body.Position = 0;
// Leave the body open so the next middleware can read it.
using StreamReader reader = new(
context.Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false);
string body = await reader.ReadToEndAsync();
if (body.Length is 0)
return;
object? obj = JsonSerializer.Deserialize<object>(body);
if (obj is null)
return;
diagnosticContext.Set("Body", obj);
options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} {Body} responded {StatusCode} in {Elapsed:0.0000}";
}
);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5077 次 |
| 最近记录: |