ASP.NET Core での HTTP ログ
Note
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
警告
このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
重要
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。
現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
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
を呼び出す箇所の空のラムダ式により、既定の構成でミドルウェアが追加されます。 既定では、HTTP ログにより、要求と応答についてのパス、状態コード、ヘッダーといった一般的なプロパティがログされます。
HTTP のログが表示されるように、appsettings.Development.json
ファイルの "LogLevel": {
レベルに次の行を追加します。
"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 を呼び出し、ラムダ式を使って 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();
Note
上記のサンプルと以降のサンプルでは、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
に設定すると、要求と応答に対して有効なすべてのログを、最後に 1 つのログに統合するようにミドルウェアを構成できます。 これには、要求、要求本文、応答、応答本文、期間が含まれます。
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();
エンドポイント固有の構成
Minimal API アプリのエンドポイント固有の構成では、WithHttpLogging 拡張メソッドを使用できます。 次の例は、1 つのエンドポイントの HTTP ログを構成する方法を示しています。
app.MapGet("/response", () => "Hello World! (logging response)")
.WithHttpLogging(HttpLoggingFields.ResponsePropertiesAndHeaders);
コントローラーを使用するアプリのエンドポイント固有の構成では、[HttpLogging]
属性を使用できます。 この属性は、次の例に示すように、Minimal 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");
}
}
このインターセプターにより、HttpLoggingFields.All
をログするように HTTP ログが構成されていても、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
ログ構成の優先順位
次のリストは、ログ構成の優先順位を示しています。
- AddHttpLogging を呼び出して設定する、HttpLoggingOptions からのグローバル構成。
[HttpLogging]
属性または WithHttpLogging 拡張メソッドからのエンドポイント固有の構成によって、グローバル構成がオーバーライドされます。IHttpLoggingInterceptor
は結果と共に呼び出され、要求ごとに構成をさらに変更できます。
HTTP ログは、受信した HTTP 要求と HTTP 応答に関する情報をログに記録するミドルウェアです。 HTTP ログにより、次のログが提供されます。
- HTTP 要求情報
- 共通プロパティ
- ヘッダー
- 本文
- HTTP 応答情報
HTTP ログは、複数のシナリオで次のことを行うのに役立ちます。
- 受信した要求と応答に関する情報を記録する。
- 要求と応答のどの部分をログに記録するかをフィルター処理する。
- どのヘッダーをログに記録するかをフィルター処理する。
HTTP ログを使用すると、アプリのパフォーマンスが低下するおそれがあります。特に要求と応答の本文をログに記録する場合に、そう言えます。 ログに記録するフィールドを選択する際には、パフォーマンスへの影響を考慮してください。 選択したログ プロパティがパフォーマンスに及ぼす影響をテストします。
警告
HTTP ログによって、個人を特定できる情報 (PII) がログに記録されるおそれがあります。 リスクを考慮し、機密情報をログに記録しないようにします。
HTTP ログを有効にする
HTTP ログは UseHttpLogging で有効になります。これによって、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 ログにより、要求と応答について、パス、状態コード、ヘッダーなどの一般的なプロパティがログに記録されます。 HTTP のログが表示されるように、appsettings.Development.json
ファイルの "LogLevel": {
レベルに次の行を追加します。
"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();
ASP.NET Core