シンプルなログ

注意

この機能は EF Core 5.0 で導入されました。

ヒント

この記事のサンプルは GitHub からダウンロードできます。

Entity Framework Core (EF Core) のシンプルなログを使用して、アプリケーションの開発とデバッグを行うときにログを簡単に取得することができます。 この形式のログ記録では最小限の構成しか必要とせず、追加の NuGet パッケージも不要です。

ヒント

EF Core は、Microsoft.Extensions.Logging とも統合します。これは必要な構成が多くなりますが、多くの場合、運用環境のアプリケーションでのログ記録に適しています。

構成

DbContext インスタンスの構成時に LogTo を使用すると、任意の種類のアプリケーションから EF Core ログにアクセスできます。 この構成は、通常、DbContext.OnConfiguring のオーバーライドで行われます。 次に例を示します。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(Console.WriteLine);

または、 LogTo コンストラクターに渡すインスタンスの AddDbContext 一部または作成時 DbContextOptionsDbContext 呼び出すことができます。

ヒント

AddDbContext が使用される場合も、または DbContextOptions インスタンスが DbContext コンストラクターに渡される場合も、OnConfiguring が呼び出されます。 したがって、それは DbContext の構築方法に関係なく、コンテキスト構成を適用するのに理想的な場所となります。

ログの出力指定

コンソールへのログの記録

LogTo には、文字列を Action<T> 受け入れるデリゲートが必要です。 EF Core は、生成されたログ メッセージごとに文字列を使用して、このデリゲートを呼び出します。 次に、デリゲートによって、指定されたメッセージを使用して何らかが実行されます。

この Console.WriteLine メソッドは、上に示すように、このデリゲートによく使用されます。 この結果、各ログ メッセージがコンソールに書き込まれます。

デバッグ ウィンドウへのログ記録

Debug.WriteLineを使用して、Visual Studioまたはその他の IDE の [デバッグ] ウィンドウに出力を送信できます。 Debug クラスはリリース ビルドからコンパイルされるため、この場合はラムダ構文を使用する必要があります。 次に例を示します。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(message => Debug.WriteLine(message));

ファイルへのログ記録

ファイルに書き込むには、そのファイル用 StreamWriter に作成する必要があります。 このメソッドは WriteLine 、上記の他の例と同様に使用できます。 コンテキストが破棄されたときにライターを破棄することによって、ファイルが正常に閉じられるようにしてください。 次に例を示します。

private readonly StreamWriter _logStream = new StreamWriter("mylog.txt", append: true);

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(_logStream.WriteLine);

public override void Dispose()
{
    base.Dispose();
    _logStream.Dispose();
}

public override async ValueTask DisposeAsync()
{
    await base.DisposeAsync();
    await _logStream.DisposeAsync();
}

ヒント

運用環境のアプリケーションでのファイルへのログ記録には、Microsoft.Extensions.Logging を使用することを検討してください。

詳細なメッセージを取得する

機微なデータ

既定では、EF Core には例外メッセージ内のデータの値は含まれません。 これは、そのようなデータは機密情報である可能性があるためで、例外が処理されない場合に運用環境で流出するおそれがあるためです。

ただし、特にキーについて、データ値を把握することは、デバッグ時に非常に役立ちます。 これは、EF Core で呼び出 EnableSensitiveDataLogging()すことによって有効にすることができます。 次に例を示します。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine)
        .EnableSensitiveDataLogging();

詳細なクエリの例外

パフォーマンス上の理由により、EF Core ではデータベース プロバイダーから値を読み取るために各呼び出しを try-catch ブロックでラップすることはしません。 ただし、これによって診断が困難な例外が発生することがあり、特にモデルで許可されない場合にデータベースが NULL を返す場合がそうです。

オン EnableDetailedErrors にすると、EF でこれらの try-catch ブロックが導入され、より詳細なエラーが発生します。 次に例を示します。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine)
        .EnableDetailedErrors();

フィルター処理

ログ レベル

すべての EF Core ログ メッセージは、列挙型によって定義されたレベルに LogLevel 割り当てられます。 既定では EF Core のシンプルなログには、Debug レベル以上のすべてのメッセージが含まれます。 より高い最小レベルを LogTo に渡すと、一部のメッセージをフィルターで除外できます。 たとえば、Information を渡すと、データベース アクセスと一部のハウスキーピング メッセージに限定されたログの最小セットが得られます。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);

特定のメッセージ

すべてのログ メッセージに .EventId これらの ID には、リレーショナル固有のメッセージの CoreEventId クラスまたは RelationalEventId クラスからアクセスできます。 データベース プロバイダーには、同様のクラスのプロバイダー固有の ID が含まれる場合もあります。 たとえば、 SqlServerEventId SQL Server プロバイダーの場合です。

LogTo は、1 つまたは複数のイベント ID に関連付けられたメッセージのみをログに記録するように構成できます。 たとえば、初期化または破棄されたコンテキストのメッセージのみをログに記録するには、次のようにします。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine, new[] { CoreEventId.ContextDisposed, CoreEventId.ContextInitialized });

メッセージの種類

すべてのログ メッセージは、名前付きの階層ロガー カテゴリに割り当てられます。 カテゴリは次のとおりです。

カテゴリ [メッセージ]
Microsoft.EntityFrameworkCore すべての EF Core メッセージ
Microsoft.EntityFrameworkCore.Database すべてのデータベース操作
Microsoft.EntityFrameworkCore.Database.Connection データベース接続の使用
Microsoft.EntityFrameworkCore.Database.Command データベース コマンドの使用
Microsoft.EntityFrameworkCore.Database.Transaction データベース トランザクションの使用
Microsoft.EntityFrameworkCore.Update エンティティの保存 (データベース操作を除く)
Microsoft.EntityFrameworkCore.Model すべてのモデルとメタデータの相互作用
Microsoft.EntityFrameworkCore.Model.Validation モデルの検証
Microsoft.EntityFrameworkCore.Query クエリ (データベース操作を除く)
Microsoft.EntityFrameworkCore.Infrastructure 一般的なイベント (コンテキストの作成など)
Microsoft.EntityFrameworkCore.Scaffolding データベースのリバース エンジニアリング
Microsoft.EntityFrameworkCore.Migrations 移行
Microsoft.EntityFrameworkCore.ChangeTracking 変更の追跡操作

LogTo は、1 つまたは複数のカテゴリからのメッセージのみをログに記録するように構成できます。 たとえば、データベース操作のみをログに記録するには、次のようにします。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Name });

このクラスには、カテゴリを DbLoggerCategory 検索するための階層型 API が用意されており、文字列をハードコーディングする必要がなくなります。

カテゴリは階層的であるため、Database カテゴリを使用するこの例には、サブカテゴリ Database.ConnectionDatabase.CommandDatabase.Transaction のすべてのメッセージが含まれます。

カスタム フィルター

LogTo では、上記のフィルター処理オプションで十分ではない場合にカスタム フィルターを使用できます。 たとえば、Information レベル以上のメッセージと、接続を開くメッセージと閉じるメッセージをログに記録するには、次のようにします。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(
            Console.WriteLine,
            (eventId, logLevel) => logLevel >= LogLevel.Information
                                   || eventId == RelationalEventId.ConnectionOpened
                                   || eventId == RelationalEventId.ConnectionClosed);

ヒント

カスタム フィルターを使用するか、ここで示す他のオプションを使用したフィルター処理は、LogTo デリゲートでのフィルター処理よりも効率的です。 これは、あるメッセージをログに記録すべきではないとフィルターで判断された場合、そのログ メッセージは作成すらされないためです。

特定のメッセージの構成

EF Core ConfigureWarnings API を使用すると、特定のイベントが発生したときに発生する動作をアプリケーションで変更できます。 これは次の場合に使用できます。

  • イベントがログに記録されるログ レベルを変更する
  • イベントのログ記録を完全にスキップする
  • イベントが発生したときに例外をスローする

イベントのログ レベルの変更

前の例ではカスタム フィルターを使用して、LogLevel.Information のすべてのメッセージと、LogLevel.Debug に対して定義された 2 つのイベントをログに記録しました。 2 つの Debug イベントのログ レベルを Information に変更することで、同じことを実現できます。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .ConfigureWarnings(
            b => b.Log(
                (RelationalEventId.ConnectionOpened, LogLevel.Information),
                (RelationalEventId.ConnectionClosed, LogLevel.Information)))
        .LogTo(Console.WriteLine, LogLevel.Information);

イベントのログ記録を抑制する

同様に、個々のイベントをログに記録しないようにすることもできます。 これは、確認して理解した警告を無視する場合に特に便利です。 次に例を示します。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .ConfigureWarnings(b => b.Ignore(CoreEventId.DetachedLazyLoadingWarning))
        .LogTo(Console.WriteLine);

イベントに対してスローする

最後に、特定のイベントに対してスローするように EF Core を構成できます。 これは、警告をエラーに変更する場合に特に便利です。 (実際、これはメソッドの元の ConfigureWarnings 目的であり、名前でした)。例えば:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .ConfigureWarnings(b => b.Throw(RelationalEventId.MultipleCollectionIncludeWarning))
        .LogTo(Console.WriteLine);

メッセージの内容と書式設定

LogTo の既定のコンテンツは複数行にわたって書式設定されます。 最初の行にはメッセージ メタデータが含まれています。

  • LogLevel 4 文字のプレフィックスとして
  • 現在のカルチャ用に書式設定されたローカル タイムスタンプ
  • EventIdコピー/貼り付けして他EventIdのクラスからCoreEventIdメンバーを取得できるフォーム内に、生の ID 値を加えたもの
  • 前述のようなイベント カテゴリ。

次に例を示します。

info: 10/6/2020 10:52:45.581 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "Blogs" (
          "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,
          "Name" INTEGER NOT NULL
      );
dbug: 10/6/2020 10:52:45.582 RelationalEventId.TransactionCommitting[20210] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committing transaction.
dbug: 10/6/2020 10:52:45.585 RelationalEventId.TransactionCommitted[20202] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committed transaction.

このコンテンツは、次のセクションに示すように、値を DbContextLoggerOptions渡すことによってカスタマイズできます。

ヒント

ログの書式設定をさらに制御するには、Microsoft.Extensions.Logging の使用を検討してください。

UTC 時刻の使用

既定では、タイムスタンプはデバッグ中にローカルで使用するように設計されています。 DbContextLoggerOptions.DefaultWithUtcTime代わりにカルチャに依存しない UTC タイムスタンプを使用しますが、それ以外はすべて同じにします。 次に例を示します。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(
        Console.WriteLine,
        LogLevel.Debug,
        DbContextLoggerOptions.DefaultWithUtcTime);

この例では、次のようなログ書式になります。

info: 2020-10-06T17:55:39.0333701Z RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "Blogs" (
          "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,
          "Name" INTEGER NOT NULL
      );
dbug: 2020-10-06T17:55:39.0333892Z RelationalEventId.TransactionCommitting[20210] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committing transaction.
dbug: 2020-10-06T17:55:39.0351684Z RelationalEventId.TransactionCommitted[20202] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committed transaction.

単一行のログ記録

ログ メッセージごとに 1 行を取得するようにすると便利な場合があります。 これを有効にするには、次の操作を行 DbContextLoggerOptions.SingleLineいます。 次に例を示します。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(
        Console.WriteLine,
        LogLevel.Debug,
        DbContextLoggerOptions.DefaultWithLocalTime | DbContextLoggerOptions.SingleLine);

この例では、次のようなログ書式になります。

info: 10/6/2020 10:52:45.723 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) -> Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']CREATE TABLE "Blogs" (    "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,    "Name" INTEGER NOT NULL);
dbug: 10/6/2020 10:52:45.723 RelationalEventId.TransactionCommitting[20210] (Microsoft.EntityFrameworkCore.Database.Transaction) -> Committing transaction.
dbug: 10/6/2020 10:52:45.725 RelationalEventId.TransactionCommitted[20202] (Microsoft.EntityFrameworkCore.Database.Transaction) -> Committed transaction.

その他のコンテンツ オプション

その他のフラグを DbContextLoggerOptions 使用して、ログに含まれるメタデータの量を減らすことができます。 これは単一行のログ記録と組み合わせて使用すると便利です。 次に例を示します。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(
        Console.WriteLine,
        LogLevel.Debug,
        DbContextLoggerOptions.UtcTime | DbContextLoggerOptions.SingleLine);

この例では、次のようなログ書式になります。

2020-10-06T17:52:45.7320362Z -> Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']CREATE TABLE "Blogs" (    "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,    "Name" INTEGER NOT NULL);
2020-10-06T17:52:45.7320531Z -> Committing transaction.
2020-10-06T17:52:45.7339441Z -> Committed transaction.

EF6 からの移行

EF Core の単純なログ記録は、EF6 とは 2 つの重要な点で異なります Database.Log

  • ログ メッセージはデータベース操作に限定されません
  • ログ記録はコンテキストの初期化時に構成する必要があります

1 つ目の違いについては、上記のフィルター処理を使用して、ログに記録されるメッセージを制限できます。

2 つ目の違いは、必要ないときにログ メッセージを生成しないという、パフォーマンスを向上させる意図的な変更です。 ただし、DbContextLog プロパティを作成し、設定されている場合にのみこれを使用することで、EF6 と同様の動作を引き続き使用できます。 次に例を示します。

public Action<string> Log { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(s => Log?.Invoke(s));