ASP.NET Core 中的 HTTP 記錄
注意
這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本。
HTTP 記錄是一個中介軟體,可記錄輸入 HTTP 要求和 HTTP 回應的相關資訊。 HTTP 記錄提供下列項目的記錄:
- HTTP 要求資訊
- 通用屬性
- 標頭
- 本文
- HTTP 回應資訊
HTTP 記錄可以:
- 記錄所有要求和回應,或只記錄符合特定條件的要求和回應。
- 選取要求和回應的記錄部分。
- 可讓您從記錄中編輯敏感性資訊。
HTTP 記錄可能會降低應用程式的效能,特別是在記錄要求和回應主體時。 選取要記錄的欄位時,請考量效能影響。 測試所選記錄屬性的效能影響。
警告
HTTP 記錄可能會記錄個人識別資訊 (PII)。 請考慮風險並避免記錄敏感性資訊。
啟用 HTTP 記錄
HTTP 記錄是藉由呼叫 AddHttpLogging 和 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();
在上述範例中,呼叫 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。
RequestHeaders
和 ResponseHeaders
RequestHeaders 和 ResponseHeaders 是記錄的 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-urlencoded
或 multipart/form-data
等媒體類型)。
MediaTypeOptions
方法
RequestBodyLogLimit
和 ResponseBodyLogLimit
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
記錄組態優先順序
下列清單顯示記錄組態的優先順序:
- HttpLoggingOptions 中的全域組態,設定方式是呼叫 AddHttpLogging。
[HttpLogging]
屬性或 WithHttpLogging 擴充方法中的端點專屬組態會覆寫全域組態。- 可使用結果來呼叫
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.RequestHeaders
和 HttpLoggingOptions.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-urlencoded
或 multipart/form-data
。
MediaTypeOptions
方法
RequestBodyLogLimit
和 ResponseBodyLogLimit
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();