ASP.NET Core での HTTP ログ

Note

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

警告

このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、「.NET および .NET Core サポート ポリシー」を参照してください。 現在のリリースについては、この記事の .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 を呼び出す箇所の空のラムダ式により、既定の構成でミドルウェアが追加されます。 既定では、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.csAddHttpLogging を呼び出し、ラムダ式を使って 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

RequestHeadersResponseHeaders は、ログされる HTTP ヘッダーのセットです。 これらのコレクションにヘッダー名が含まれているヘッダー値のみがログされます。 次のコードでは sec-ch-uaRequestHeaders に追加しているため、sec-ch-ua ヘッダーの値はログされます。 また、MyResponseHeaderResponseHeaders に追加しているため、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 メソッド

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

CombineLogstrue に設定すると、要求と応答に対して有効なすべてのログを、最後に 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.csAddHttpLoggingInterceptor<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

ログ構成の優先順位

次のリストは、ログ構成の優先順位を示しています。

  1. AddHttpLogging を呼び出して設定する、HttpLoggingOptions からのグローバル構成。
  2. [HttpLogging] 属性または WithHttpLogging 拡張メソッドからのエンドポイント固有の構成によって、グローバル構成がオーバーライドされます。
  3. 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.csAddHttpLogging を呼び出します。

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 メソッド

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