HTTP Logging (ghi nhật ký HTTP) trong .NET và ASP.NET Core
HTTP logging (ghi nhật ký HTTP) là middleware (phần mềm trung gian) ghi nhật ký thông tin về các HTTP requests đến và HTTP responses. HTTP logging cung cấp logs của:
- Thông tin HTTP request
- Các thuộc tính chung
- Headers (tiêu đề)
- Body (nội dung)
- Thông tin HTTP response
HTTP logging có thể:
- Ghi tất cả requests và responses, hoặc chỉ các requests và responses đáp ứng tiêu chí nhất định.
- Chọn các phần nào của request và response được ghi.
- Cho phép ẩn thông tin nhạy cảm trong logs.
HTTP logging có thể làm giảm hiệu năng ứng dụng, đặc biệt khi ghi request và response bodies. Cân nhắc tác động đến hiệu năng khi chọn các trường để ghi. Test tác động đến hiệu năng của các thuộc tính logging đã chọn.
HTTP logging có khả năng ghi thông tin nhận dạng cá nhân (PII). Xem xét rủi ro và tránh ghi thông tin nhạy cảm.
Bật HTTP logging
HTTP logging được bật bằng cách gọi AddHttpLogging và UseHttpLogging:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpLogging(o => { });
var app = builder.Build();
app.UseHttpLogging();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.MapGet("/", () => "Hello World!");
app.Run();Lambda rỗng trong ví dụ trên thêm middleware với cấu hình mặc định. Theo mặc định, HTTP logging ghi các thuộc tính chung như path, status-code và headers cho requests và responses.
Thêm dòng sau vào file appsettings.Development.json ở mức "LogLevel": { để HTTP logs được hiển thị:
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
Với cấu hình mặc định, một request và response được ghi như một cặp messages tương tự ví dụ sau:
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
Request:
Protocol: HTTP/2
Method: GET
Scheme: https
PathBase:
Path: /
Accept: text/html,application/xhtml+xml,...
Host: localhost:52941
User-Agent: Mozilla/5.0 ...HTTP logging options (tùy chọn HTTP logging)
Để cấu hình global options cho HTTP logging middleware, gọi AddHttpLogging trong Program.cs, dùng lambda để cấu hình HttpLoggingOptions:
using Microsoft.AspNetCore.HttpLogging;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpLogging(logging =>
{
logging.LoggingFields = HttpLoggingFields.All;
logging.RequestHeaders.Add("sec-ch-ua");
logging.ResponseHeaders.Add("MyResponseHeader");
logging.MediaTypeOptions.AddText("application/javascript");
logging.RequestBodyLogLimit = 4096;
logging.ResponseBodyLogLimit = 4096;
logging.CombineLogs = true;
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseHttpLogging();
app.Use(async (context, next) =>
{
context.Response.Headers["MyResponseHeader"] =
new string[] { "My Response Header Value" };
await next();
});
app.MapGet("/", () => "Hello World!");
app.Run();Trong ví dụ trên, UseHttpLogging được gọi sau UseStaticFiles, nên HTTP logging không được bật cho static files. Để bật HTTP logging cho static files, gọi UseHttpLogging trước UseStaticFiles.
LoggingFields
HttpLoggingOptions.LoggingFields là enum flag cấu hình các phần cụ thể của request và response để ghi. LoggingFields mặc định là RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders.
RequestHeaders và ResponseHeaders
RequestHeaders và ResponseHeaders là các tập hợp HTTP headers được ghi. Giá trị header chỉ được ghi cho các tên header có trong các bộ sưu tập này. Nếu các dòng thêm header bị xóa, các giá trị sẽ hiển thị là [Redacted].
MediaTypeOptions
MediaTypeOptions cung cấp cấu hình để chọn encoding nào dùng cho media type cụ thể. Cách tiếp cận này cũng có thể dùng để bật logging cho dữ liệu không được ghi mặc định (ví dụ form data, có media type như application/x-www-form-urlencoded hoặc multipart/form-data).
Các phương thức MediaTypeOptions:
AddTextAddBinaryClear
RequestBodyLogLimit và ResponseBodyLogLimit
- RequestBodyLogLimit: Giới hạn kích thước body request được ghi
- ResponseBodyLogLimit: Giới hạn kích thước body response được ghi
CombineLogs
Đặt CombineLogs thành true cấu hình middleware để hợp nhất tất cả logs đã bật cho một request và response vào một log ở cuối. Điều này bao gồm request, request body, response, response body và duration (thời gian).
Cấu hình theo endpoint cụ thể
Để cấu hình theo endpoint trong Minimal API apps, extension method WithHttpLogging có sẵn:
app.MapGet("/response", () => "Hello World! (logging response)")
.WithHttpLogging(HttpLoggingFields.ResponsePropertiesAndHeaders);Attribute [HttpLogging] cũng có sẵn cho controller-based apps và Minimal API apps:
app.MapGet("/duration", [HttpLogging(loggingFields: HttpLoggingFields.Duration)]
() => "Hello World! (logging duration)");IHttpLoggingInterceptor
IHttpLoggingInterceptor là interface cho service có thể được implement để xử lý callbacks per-request và per-response để tùy chỉnh những chi tiết nào được ghi. Cài đặt log theo endpoint cụ thể được áp dụng trước và có thể bị ghi đè trong các callbacks này.
Đăng ký implementation IHttpLoggingInterceptor bằng cách gọi AddHttpLoggingInterceptor trong Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpLogging(logging =>
{
logging.LoggingFields = HttpLoggingFields.Duration;
});
builder.Services.AddHttpLoggingInterceptor<SampleHttpLoggingInterceptor>();Ví dụ implementation IHttpLoggingInterceptor:
using Microsoft.AspNetCore.HttpLogging;
namespace HttpLoggingSample;
internal sealed class SampleHttpLoggingInterceptor : IHttpLoggingInterceptor
{
public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext)
{
if (logContext.HttpContext.Request.Method == "POST")
{
// Don't log anything if the request is a POST.
logContext.LoggingFields = HttpLoggingFields.None;
}
// Don't enrich if we're not going to log any part of the request.
if (!logContext.IsAnyEnabled(HttpLoggingFields.Request))
{
return default;
}
if (logContext.TryDisable(HttpLoggingFields.RequestPath))
{
RedactPath(logContext);
}
if (logContext.TryDisable(HttpLoggingFields.RequestHeaders))
{
RedactRequestHeaders(logContext);
}
EnrichRequest(logContext);
return default;
}
public ValueTask OnResponseAsync(HttpLoggingInterceptorContext logContext)
{
if (!logContext.IsAnyEnabled(HttpLoggingFields.Response))
{
return default;
}
if (logContext.TryDisable(HttpLoggingFields.ResponseHeaders))
{
RedactResponseHeaders(logContext);
}
EnrichResponse(logContext);
return default;
}
private void RedactPath(HttpLoggingInterceptorContext logContext)
{
logContext.AddParameter(nameof(logContext.HttpContext.Request.Path), "RedactedPath");
}
private void RedactRequestHeaders(HttpLoggingInterceptorContext logContext)
{
foreach (var header in logContext.HttpContext.Request.Headers)
{
logContext.AddParameter(header.Key, "RedactedHeader");
}
}
private void EnrichRequest(HttpLoggingInterceptorContext logContext)
{
logContext.AddParameter("RequestEnrichment", "Stuff");
}
private void RedactResponseHeaders(HttpLoggingInterceptorContext logContext)
{
foreach (var header in logContext.HttpContext.Response.Headers)
{
logContext.AddParameter(header.Key, "RedactedHeader");
}
}
private void EnrichResponse(HttpLoggingInterceptorContext logContext)
{
logContext.AddParameter("ResponseEnrichment", "Stuff");
}
}Thứ tự ưu tiên cấu hình logging
Danh sách sau cho thấy thứ tự ưu tiên cho cấu hình logging:
- Cấu hình global từ HttpLoggingOptions, được đặt bằng cách gọi AddHttpLogging.
- Cấu hình theo endpoint cụ thể từ attribute
[HttpLogging]hoặc extension method WithHttpLogging ghi đè cấu hình global. IHttpLoggingInterceptorđược gọi với kết quả và có thể tiếp tục sửa đổi cấu hình theo từng request.
Ẩn dữ liệu nhạy cảm (Redacting sensitive data)
HTTP logging với redaction (ẩn dữ liệu nhạy cảm) có thể được bật bằng cách gọi AddHttpLoggingRedaction:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpLogging(logging =>
{
logging.LoggingFields = HttpLoggingFields.Duration;
});
builder.Services.AddRedaction();
builder.Services.AddHttpLoggingRedaction(op => { });Tùy chọn logging redaction
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpLogging(o => { });
builder.Services.AddRedaction();
builder.Services.AddHttpLoggingRedaction(op =>
{
op.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None;
op.RequestPathLoggingMode = IncomingPathLoggingMode.Formatted;
op.RequestHeadersDataClasses.Add(HeaderNames.Accept, MyTaxonomyClassifications.Public);
op.ResponseHeadersDataClasses.Add(HeaderNames.ContentType, MyTaxonomyClassifications.Private);
op.RouteParameterDataClasses = new Dictionary<string, DataClassification>
{
{ "one", MyTaxonomyClassifications.Personal },
};
op.ExcludePathStartsWith.Add("/home");
op.IncludeUnmatchedRoutes = true;
});
var app = builder.Build();
app.UseHttpLogging();
app.MapGet("/", () => "Logged!");
app.MapGet("/home", () => "Not logged!");
app.Run();RequestPathLoggingMode
RequestPathLoggingMode xác định cách ghi request path:
Formatted: Ghi request path không có parameters.Structured: Ghi request path với parameters.
RequestPathParameterRedactionMode
RequestPathParameterRedactionMode chỉ định cách ẩn route parameters:
Strict: Route parameters được coi là nhạy cảm, cần annotation rõ ràng với data classification, và bị ẩn theo mặc định.Loose: Tất cả parameters được coi là không nhạy cảm và được bao gồm nguyên vẹn theo mặc định.None: Route parameters không bị ẩn bất kể có data classification annotations.
ExcludePathStartsWith
ExcludePathStartsWith chỉ định các paths phải được loại trừ hoàn toàn khỏi logging.
IncludeUnmatchedRoutes
IncludeUnmatchedRoutes cho phép báo cáo các routes không khớp. Nếu đặt thành true, ghi toàn bộ path của routes không được nhận dạng bởi Routing thay vì ghi giá trị Unknown.