Nguon: Microsoft Learn · .NET 8.0

HTTP Logging (ghi nhật ký HTTP) trong .NET và ASP.NET Core

Nguồn: HTTP logging in .NET and 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:

HTTP logging có thể:

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 AddHttpLoggingUseHttpLogging:

csharp
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ị:

json
"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:

output
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:

csharp
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.

RequestHeadersResponseHeaders

RequestHeadersResponseHeaders 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:

RequestBodyLogLimitResponseBodyLogLimit

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:

csharp
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:

csharp
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:

csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.Duration;
});
builder.Services.AddHttpLoggingInterceptor<SampleHttpLoggingInterceptor>();

Ví dụ implementation IHttpLoggingInterceptor:

csharp
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:

  1. Cấu hình global từ HttpLoggingOptions, được đặt bằng cách gọi AddHttpLogging.
  2. Cấu hình theo endpoint cụ thể từ attribute [HttpLogging] hoặc extension method WithHttpLogging ghi đè cấu hình global.
  3. 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:

csharp
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

csharp
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:

RequestPathParameterRedactionMode

RequestPathParameterRedactionMode chỉ định cách ẩn route parameters:

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.