ASP.NET Core 中的 HTTP 記錄

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱本文的 .NET 8 版本

HTTP 記錄是一個中介軟體,可記錄輸入 HTTP 要求和 HTTP 回應的相關資訊。 HTTP 記錄提供下列項目的記錄:

  • HTTP 要求資訊
  • 通用屬性
  • 標頭
  • 本文
  • HTTP 回應資訊

HTTP 記錄可以:

  • 記錄所有要求和回應,或只記錄符合特定條件的要求和回應。
  • 選取要求和回應的記錄部分。
  • 可讓您從記錄中編輯敏感性資訊。

HTTP 記錄可能會降低應用程式的效能,特別是在記錄要求和回應主體時。 選取要記錄的欄位時,請考量效能影響。 測試所選記錄屬性的效能影響。

警告

HTTP 記錄可能會記錄個人識別資訊 (PII)。 請考慮風險並避免記錄敏感性資訊。

啟用 HTTP 記錄

HTTP 記錄是藉由呼叫 AddHttpLoggingUseHttpLogging 來啟用,如下列範例所示:

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();

在上述範例中,呼叫 AddHttpLogging 的空白 Lambda 會新增具有預設組態的中介軟體。 根據預設,HTTP 記錄會記錄一般屬性,例如要求和回應的路徑、狀態碼和標頭。

將下列這一行新增至 "LogLevel": { 層級的 appsettings.Development.json 檔案,以便顯示 HTTP 記錄:

 "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"

使用預設組態時,要求和回應會記錄為類似下列範例的訊息組:

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
      Request:
      Protocol: HTTP/2
      Method: GET
      Scheme: https
      PathBase:
      Path: /
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
      Host: localhost:52941
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.61
      Accept-Encoding: gzip, deflate, br
      Accept-Language: en-US,en;q=0.9
      Upgrade-Insecure-Requests: [Redacted]
      sec-ch-ua: [Redacted]
      sec-ch-ua-mobile: [Redacted]
      sec-ch-ua-platform: [Redacted]
      sec-fetch-site: [Redacted]
      sec-fetch-mode: [Redacted]
      sec-fetch-user: [Redacted]
      sec-fetch-dest: [Redacted]
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
      Response:
      StatusCode: 200
      Content-Type: text/plain; charset=utf-8
      Date: Tue, 24 Oct 2023 02:03:53 GMT
      Server: Kestrel

HTTP 記錄選項

若要設定 HTTP 記錄中介軟體的全域選項,請在 Program.cs 中呼叫 AddHttpLogging,並使用 Lambda 來設定 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();

注意

在上述範例和下列範例中,會在 UseStaticFiles 之後呼叫 UseHttpLogging,因此不會針對靜態檔案啟用 HTTP 記錄。 若要啟用靜態檔案 HTTP 記錄,請在 UseStaticFiles 之前呼叫 UseHttpLogging

LoggingFields

HttpLoggingOptions.LoggingFields 是列舉旗標,可設定所要記錄要求和回應的特定部分。 HttpLoggingOptions.LoggingFields 預設為 RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders

RequestHeadersResponseHeaders

RequestHeadersResponseHeaders 是記錄的 HTTP 標頭集合。 只會記錄這些集合中標頭名稱的標頭值。 下列程式碼會將 sec-ch-ua 新增至 RequestHeaders,因此會記錄 sec-ch-ua 標頭的值。 還會將 MyResponseHeader 新增至 ResponseHeaders,因此會記錄 MyResponseHeader 標頭的值。 如果移除這些行,這些標頭的值會是 [Redacted]

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();

MediaTypeOptions

MediaTypeOptions 會提供設定,以選取要用於特定媒體類型的編碼方式。

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();

此方法也可以用來啟用預設未記錄之資料的記錄功能 (例如表單資料,其可能具有 application/x-www-form-urlencodedmultipart/form-data 等媒體類型)。

MediaTypeOptions 方法

RequestBodyLogLimitResponseBodyLogLimit

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();

CombineLogs

CombineLogs 設定為 true,可將中介軟體設定為將要求的所有啟用記錄和回應最後合併到的一個記錄檔。 這包括要求、要求本文、回應、回應本文和持續時間。

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();

端點專屬組態

對於基本 API 應用程式中的端點專屬組態,可以使用 WithHttpLogging 擴充方法。 下列範例示範如何設定一個端點的 HTTP 記錄:

app.MapGet("/response", () => "Hello World! (logging response)")
    .WithHttpLogging(HttpLoggingFields.ResponsePropertiesAndHeaders);

對於使用控制器的應用程式,可以對其中的端點專屬組態使用 [HttpLogging] 屬性。 該屬性也可以在基本 API 應用程式中使用,如下列範例所示:

app.MapGet("/duration", [HttpLogging(loggingFields: HttpLoggingFields.Duration)]
    () => "Hello World! (logging duration)");

IHttpLoggingInterceptor

IHttpLoggingInterceptor 是可實作以處理個別要求和個別回應回呼的服務介面,可用來自訂要記錄哪些詳細資料。 任何端點專屬的記錄設定都會先套用,然後可以在這些回呼中加以覆寫。 實作可以:

  • 檢查要求或回應。
  • 啟用或停用任何 HttpLoggingFields
  • 調整要記錄多少要求或回應本文。
  • 在記錄中新增自訂欄位。

Program.cs 中呼叫 AddHttpLoggingInterceptor<T> 以註冊 IHttpLoggingInterceptor 實作。 如果註冊多個 IHttpLoggingInterceptor 執行個體,則會依註冊的順序執行。

下列範例示範如何註冊 IHttpLoggingInterceptor 實作:

var builder = WebApplication.CreateBuilder(args);

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

下列範例中的 IHttpLoggingInterceptor 實作可以:

  • 檢查要求方法,並停用 POST 要求的記錄。
  • 針對非 POST 要求:
    • 編輯要求路徑、要求標頭和回應標頭。
    • 將自訂欄位和欄位值新增至要求和回應記錄。
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)
    {
        // Don't enrich if we're not going to log any part of the response
        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");
    }
}

使用此攔截器時,即使 HTTP 記錄設定為記錄 HttpLoggingFields.All,POST 要求也不會產生任何記錄。 GET 要求會產生類似下列範例的記錄:

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
      Request:
      Path: RedactedPath
      Accept: RedactedHeader
      Host: RedactedHeader
      User-Agent: RedactedHeader
      Accept-Encoding: RedactedHeader
      Accept-Language: RedactedHeader
      Upgrade-Insecure-Requests: RedactedHeader
      sec-ch-ua: RedactedHeader
      sec-ch-ua-mobile: RedactedHeader
      sec-ch-ua-platform: RedactedHeader
      sec-fetch-site: RedactedHeader
      sec-fetch-mode: RedactedHeader
      sec-fetch-user: RedactedHeader
      sec-fetch-dest: RedactedHeader
      RequestEnrichment: Stuff
      Protocol: HTTP/2
      Method: GET
      Scheme: https
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
      Response:
      Content-Type: RedactedHeader
      MyResponseHeader: RedactedHeader
      ResponseEnrichment: Stuff
      StatusCode: 200
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[4]
      ResponseBody: Hello World!
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[8]
      Duration: 2.2778ms

記錄組態優先順序

下列清單顯示記錄組態的優先順序:

  1. HttpLoggingOptions 中的全域組態,設定方式是呼叫 AddHttpLogging
  2. [HttpLogging] 屬性或 WithHttpLogging 擴充方法中的端點專屬組態會覆寫全域組態。
  3. 可使用結果來呼叫 IHttpLoggingInterceptor,而且可以進一步修改每個要求的組態。

HTTP 記錄是一個中介軟體,可記錄輸入 HTTP 要求和 HTTP 回應的相關資訊。 HTTP 記錄提供下列項目的記錄:

  • HTTP 要求資訊
  • 通用屬性
  • 標頭
  • 本文
  • HTTP 回應資訊

下列幾種情況非常適合使用 HTTP 記錄:

  • 記錄傳入要求和回應的相關資訊。
  • 篩選要求和回應的記錄部分。
  • 篩選要記錄的標頭。

HTTP 記錄可以降低應用程式的效能,特別是在記錄要求和回應主體時。 選取要記錄的欄位時,請考量效能影響。 測試所選記錄屬性的效能影響。

警告

HTTP 記錄可能會記錄個人識別資訊 (PII)。 請考慮風險並避免記錄敏感性資訊。

啟用 HTTP 記錄

使用 UseHttpLogging 啟用 HTTP 記錄,可新增 HTTP 記錄中介軟體。

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.UseHttpLogging();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();

app.MapGet("/", () => "Hello World!");

app.Run();

根據預設,HTTP 記錄會記錄一般屬性,例如要求和回應的路徑、狀態碼和標頭。 將下列這一行新增至 "LogLevel": { 層級的 appsettings.Development.json 檔案,以便顯示 HTTP 記錄:

 "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"

輸出會記錄為位於 LogLevel.Information 的單一訊息。

範例要求輸出

HTTP 記錄選項

若要設定 HTTP 記錄中介軟體,請在 Program.cs 中呼叫 AddHttpLogging

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;

});

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();

注意

在上述範例和下列範例中,會在 UseStaticFiles 之後呼叫 UseHttpLogging,因此不會針對靜態檔案啟用 HTTP 記錄。 若要啟用靜態檔案 HTTP 記錄,請在 UseStaticFiles 之前呼叫 UseHttpLogging

LoggingFields

HttpLoggingOptions.LoggingFields 是列舉旗標,可設定所要記錄要求和回應的特定部分。 HttpLoggingOptions.LoggingFields 預設為 RequestPropertiesAndHeaders | ResponsePropertiesAndHeaders

RequestHeaders

Headers 是一組允許記錄的 HTTP 要求標頭。 只會記錄此集合中標頭名稱的標頭值。 下列程式碼會記錄要求標頭 "sec-ch-ua"。 如果已移除 logging.RequestHeaders.Add("sec-ch-ua");,則會重新處理要求標頭 "sec-ch-ua" 的值。 下列醒目提示的程式碼會呼叫 HttpLoggingOptions.RequestHeadersHttpLoggingOptions.ResponseHeaders

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;

});

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();

MediaTypeOptions

MediaTypeOptions 會提供設定,以選取要用於特定媒體類型的編碼方式。

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;

});

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();

此方法也可以用來啟用預設未記錄之資料的記錄功能 (例如表單資料,其可能具有 application/x-www-form-urlencodedmultipart/form-data 等媒體類型)。

MediaTypeOptions 方法

RequestBodyLogLimitResponseBodyLogLimit

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;

});

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();