Dela via


Enkel loggning

Tips/Råd

Du kan ladda ned den här artikelns exempel från GitHub.

Enkel loggning med Entity Framework Core (EF Core) kan användas för att enkelt hämta loggar när du utvecklar och felsöker program. Den här typen av loggning kräver minimal konfiguration och inga ytterligare NuGet-paket.

Tips/Råd

EF Core integreras också med Microsoft.Extensions.Logging, som kräver mer konfiguration, men som ofta är lämpligare för loggning i produktionsprogram.

Konfiguration

EF Core-loggar kan nås från alla typer av program med hjälp av LogTo när du konfigurerar en DbContext-instans. Den här konfigurationen görs ofta i en åsidosättning av DbContext.OnConfiguring. Till exempel:

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

Alternativt kan LogTo anropas som en del av AddDbContext eller när du skapar en DbContextOptions-instans som ska skickas till DbContext-konstruktorn.

Tips/Råd

OnConfiguring anropas fortfarande när AddDbContext används eller en DbContextOptions-instans skickas till DbContext-konstruktorn. Detta gör det till den perfekta platsen för att tillämpa kontextkonfiguration oavsett hur DbContext konstrueras.

Dirigera loggarna

Logga in på konsolen

LogTo kräver en Action<T> delegerad som tar emot en sträng. EF Core anropar det här ombudet med en sträng för varje loggmeddelande som genereras. Det är sedan upp till ombudet att göra något med det angivna meddelandet.

Metoden Console.WriteLine används ofta för det här ombudet, som du ser ovan. Detta resulterar i att varje loggmeddelande skrivs till konsolen.

Logga till felsökningsfönstret

Debug.WriteLine kan användas för att skicka utdata till felsökningsfönstret i Visual Studio eller andra IDE:er. Lambda-syntax måste användas i det här fallet eftersom Debug klassen kompileras från versionsversioner. Till exempel:

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

Logga in på en fil

Att skriva till en fil kräver att du skapar en StreamWriter eller liknande för filen. Metoden WriteLine kan sedan användas som i de andra exemplen ovan. Kom ihåg att se till att filen stängs korrekt genom att ta bort skrivaren när kontexten tas bort. Till exempel:

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

Tips/Råd

Överväg att använda Microsoft.Extensions.Logging för loggning till filer i produktionsprogram.

Få detaljerade meddelanden

Känsliga data

Som standard innehåller EF Core inte värdena för några data i undantagsmeddelanden. Detta beror på att sådana data kan vara konfidentiella och kan avslöjas i produktionsanvändning om ett undantag inte hanteras.

Men att känna till datavärden, särskilt för nycklar, kan vara till stor hjälp vid felsökning. Detta kan aktiveras i EF Core genom att anropa EnableSensitiveDataLogging(). Till exempel:

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

Detaljerade frågeundantag

Av prestandaskäl omsluter EF Core inte varje anrop för att läsa ett värde från databasprovidern i ett try-catch-block. Detta resulterar dock ibland i undantag som är svåra att diagnostisera, särskilt när databasen returnerar en NULL när den inte tillåts av modellen.

Att slå på EnableDetailedErrors gör att EF introducerar dessa try-catch-block och därigenom ger mer detaljerade fel. Till exempel:

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

Filtrering

Loggnivåer

Varje EF Core-loggmeddelande tilldelas en nivå som definieras av uppräkningen LogLevel. Som standardinställning innehåller EF Core enkel loggning varje meddelande på Debug-nivå eller högre. LogTo kan ha en högre minimumnivå för att filtrera bort vissa meddelanden. Om du till exempel skickar Information resulterar det i en minimal uppsättning loggar som är begränsade till databasåtkomst och vissa hushållningsmeddelanden.

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

Specifika meddelanden

Varje loggmeddelande tilldelas en EventId. Dessa ID:er kan nås från CoreEventId klassen eller RelationalEventId klassen för relationsspecifika meddelanden. En databasprovider kan också ha providerspecifika ID:er i en liknande klass. Till exempel SqlServerEventId för SQL Server-providern.

LogTo kan konfigureras för att endast logga de meddelanden som är associerade med ett eller flera händelse-ID:t. Om du till exempel bara vill logga meddelanden för den kontext som initieras eller tas bort:

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

Meddelandekategorier

Varje loggmeddelande tilldelas till en namngiven hierarkisk loggningskategori. Kategorierna är:

Kategori Meddelanden
Microsoft.EntityFrameworkCore Alla EF Core-meddelanden
Microsoft.EntityFrameworkCore.Database Alla databasinteraktioner
Microsoft.EntityFrameworkCore.Database.Connection Användning av en databasanslutning
Microsoft.EntityFrameworkCore.Database.Command Användning av ett databaskommando
Microsoft.EntityFrameworkCore.Database.Transaction Användning av en databastransaktion
Microsoft.EntityFrameworkCore.Update Spara objekt, utan databasinteraktioner
Microsoft.EntityFrameworkCore.Model Alla modell- och metadatainteraktioner
Microsoft.EntityFrameworkCore.Model.Validation Modellverifiering
Microsoft.EntityFrameworkCore.Query Frågor, exklusive databasinteraktioner
Microsoft.EntityFrameworkCore.Infrastructure Allmänna händelser, till exempel skapande av kontext
Microsoft.EntityFrameworkCore.Scaffolding Omvänd databasteknik
Microsoft.EntityFrameworkCore.Migrations Migreringar
Microsoft.EntityFrameworkCore.ChangeTracking Interaktioner med ändringsspårning

LogTo kan konfigureras för att endast logga meddelanden från en eller flera kategorier. Om du till exempel bara vill logga databasinteraktioner:

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

Observera att DbLoggerCategory klassen tillhandahåller ett hierarkiskt API för att hitta en kategori och undviker behovet av hårdkodade strängar.

Eftersom kategorier är hierarkiska innehåller det här exemplet med Database kategorin alla meddelanden för underkategorierna Database.Connection, Database.Commandoch Database.Transaction.

Anpassade filter

LogTo tillåter att ett anpassat filter används för fall där inget av filtreringsalternativen ovan räcker. Om du till exempel vill logga ett meddelande på nivå Information eller högre, samt meddelanden för att öppna och stänga en anslutning:

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

Tips/Råd

Att använda anpassade filter eller något av de andra alternativ som visas här är mer effektivt än att filtrera i delegeraren LogTo. Det beror på att om filtret bestämmer att meddelandet inte ska loggas skapas inte loggmeddelandet ens.

Konfiguration för specifika meddelanden

MED EF Core ConfigureWarnings API kan program ändra vad som händer när en specifik händelse påträffas. Detta kan användas för att:

  • Ändra loggnivån där händelsen loggas
  • Hoppa över att logga händelsen helt och hållet
  • Utlöser ett undantag när händelsen inträffar

Ändra loggnivån för en händelse

I föregående exempel användes ett anpassat filter för att logga varje meddelande vid LogLevel.Information samt två händelser som definierats för LogLevel.Debug. Samma sak kan uppnås genom att ändra loggnivån för de två Debug händelserna till 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);

Förhindra loggning av en händelse

På liknande sätt kan en enskild händelse förhindras från loggning. Detta är särskilt användbart för att ignorera en varning som har granskats och förståtts. Till exempel:

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

Anordna ett evenemang

Slutligen kan EF Core konfigureras för att generera för en viss händelse. Detta är särskilt användbart för att ändra en varning till ett fel. (Detta var faktiskt det ursprungliga syftet med ConfigureWarnings metoden, därav namnet.) Till exempel:

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

Meddelandeinnehåll och formatering

Standardinnehållet från LogTo formateras över flera rader. Den första raden innehåller meddelandemetadata:

  • Prefixet LogLevel med fyra tecken
  • En lokal tidsstämpel, formaterad för den aktuella kulturen
  • I EventId formuläret som kan kopieras/klistras in för att få tag på medlemmen från CoreEventId eller någon av de andra EventId klasserna, plus det ursprungliga ID-värdet
  • Händelsekategorin enligt beskrivningen ovan.

Till exempel:

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.

Det här innehållet kan anpassas genom att skicka värden från DbContextLoggerOptions, som du ser i följande avsnitt.

Tips/Råd

Överväg att använda Microsoft.Extensions.Logging för mer kontroll över loggformatering.

Använda UTC-tid

Som standard är tidsstämplar utformade för lokal förbrukning vid felsökning. Använd DbContextLoggerOptions.DefaultWithUtcTime för att använda kulturagnostiska UTC-tidsstämplar i stället, men behåll allt annat på samma sätt. Till exempel:

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

Det här exemplet resulterar i följande loggformatering:

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.

Loggning med en rad

Ibland är det bra att få exakt en rad per loggmeddelande. Detta kan aktiveras av DbContextLoggerOptions.SingleLine. Till exempel:

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

Det här exemplet resulterar i följande loggformatering:

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.

Andra innehållsalternativ

Andra flaggor i DbContextLoggerOptions kan användas för att minska mängden metadata som ingår i loggen. Detta kan vara användbart i kombination med loggning av enstaka rader. Till exempel:

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

Det här exemplet resulterar i följande loggformatering:

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.

Övergång från EF6

Enkel EF Core-loggning skiljer sig från Database.Log i EF6 på två viktiga sätt:

  • Loggmeddelanden är inte begränsade till endast databasinteraktioner
  • Loggningen måste konfigureras vid tidpunkten för kontextinitiering

För den första skillnaden kan filtreringen som beskrivs ovan användas för att begränsa vilka meddelanden som loggas.

Den andra skillnaden är en avsiktlig ändring för att förbättra prestanda genom att inte generera loggmeddelanden när de inte behövs. Det går dock fortfarande att få ett liknande beteende som EF6 genom att skapa en Log egenskap på din DbContext och sedan bara använda den när den har angetts. Till exempel:

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

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