Delen via


Eenvoudige logboekregistratie

Aanbeveling

U kunt het voorbeeld van dit artikel downloaden vanuit GitHub.

Eenvoudige logboekregistratie van Entity Framework Core (EF Core) kan worden gebruikt om eenvoudig logboeken te verkrijgen tijdens het ontwikkelen en opsporen van fouten in toepassingen. Deze vorm van logboekregistratie vereist minimale configuratie en geen extra NuGet-pakketten.

Aanbeveling

EF Core kan ook worden geïntegreerd met Microsoft.Extensions.Logging, waarvoor meer configuratie is vereist, maar is vaak geschikter voor het aanmelden in productietoepassingen.

Configuratie

EF Core-logboeken kunnen worden geopend vanuit elk type toepassing door gebruik te maken van LogTo wanneer u een DbContext-exemplaar configureert. Deze configuratie wordt vaak gedaan in een overschrijving van DbContext.OnConfiguring. Voorbeeld:

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

Afwisselend kan LogTo worden aangeroepen als onderdeel van AddDbContext of bij het maken van een DbContextOptions exemplaar dat moet worden doorgegeven aan de DbContext constructor.

Aanbeveling

OnConfiguring wordt nog steeds aangeroepen wanneer AddDbContext wordt gebruikt of als een DbContextOptions-exemplaar wordt doorgegeven aan de DbContext-constructor. Dit maakt het de ideale plek om contextconfiguratie toe te passen, ongeacht hoe dbContext wordt samengesteld.

De logboeken omsturen

Loggen naar de console

LogTo vereist een Action<T> gemachtigde die een tekenreeks accepteert. EF Core roept deze gemachtigde aan met een tekenreeks voor elk gegenereerd logboekbericht. Het is vervolgens aan de gedelegeerde om iets te doen met het opgegeven bericht.

De Console.WriteLine methode wordt vaak gebruikt voor deze gemachtigde, zoals hierboven wordt weergegeven. Dit resulteert in elk logboekbericht dat naar de console wordt geschreven.

Loggen in het debugvenster

Debug.WriteLine kan worden gebruikt om uitvoer te verzenden naar het venster Foutopsporing in Visual Studio of andere IDE's. Lambda-syntaxis moet in dit geval worden gebruikt omdat de Debug klasse is gecompileerd uit release-builds. Voorbeeld:

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

Logboekregistratie naar een bestand

Om naar een bestand te schrijven, moet u een StreamWriter of iets vergelijkbaars voor het bestand maken. De WriteLine methode kan vervolgens worden gebruikt zoals in de andere bovenstaande voorbeelden. Vergeet niet om ervoor te zorgen dat het bestand schoon wordt gesloten door de schrijver te verwijderen wanneer de context wordt verwijderd. Voorbeeld:

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

Aanbeveling

Overweeg het gebruik van Microsoft.Extensions.Logging voor logboekregistratie naar bestanden in productietoepassingen.

Gedetailleerde berichten ontvangen

Gevoelige gegevens

EF Core bevat standaard niet de waarden van gegevens in uitzonderingsberichten. Dit komt doordat dergelijke gegevens mogelijk vertrouwelijk zijn en kunnen worden weergegeven in productiegebruik als er geen uitzondering wordt verwerkt.

Het is echter handig als u gegevenswaarden kent, met name voor sleutels, wanneer u fouten opspoort. Dit kan worden ingeschakeld in EF Core door aan te roepen EnableSensitiveDataLogging(). Voorbeeld:

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

Gedetailleerde query-uitzonderingen

Vanwege prestatieredenen omvat EF Core niet elke aanroep om een waarde van de databaseprovider te lezen in een try-catch-blok. Dit leidt echter soms tot uitzonderingen die moeilijk te diagnosticeren zijn, met name wanneer de database een NULL retourneert wanneer dit niet is toegestaan door het model.

Door EnableDetailedErrors in te schakelen, zal EF deze try-catch-blokken introduceren, waardoor er gedetailleerdere foutmeldingen kunnen worden gegeven. Voorbeeld:

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

Filteren

Logboekniveaus

Elk EF Core-logboekbericht wordt toegewezen aan een niveau dat is gedefinieerd door de LogLevel enum. Ef Core eenvoudige logboekregistratie bevat standaard elk bericht op Debug niveau of hoger. LogTo kan een hoger minimumniveau worden doorgegeven om bepaalde berichten uit te filteren. Als u bijvoorbeeld Information doorgeeft, resulteert dit in een minimale set logboeken die beperkt zijn tot databasetoegang en enkele onderhoudsberichten.

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

Specifieke berichten

Elk logboekbericht krijgt een EventId. Deze id's zijn toegankelijk vanuit de CoreEventId klasse of de RelationalEventId klasse voor relationele specifieke berichten. Een databaseprovider kan ook providerspecifieke id's hebben in een vergelijkbare klasse. Bijvoorbeeld SqlServerEventId voor de SQL Server-provider.

LogTo kan worden geconfigureerd om alleen de berichten te registreren die zijn gekoppeld aan een of meer gebeurtenis-id's. Als u bijvoorbeeld alleen berichten wilt registreren voor de context die wordt geïnitialiseerd of verwijderd:

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

Berichtcategorieën

Elk logboekbericht wordt toegewezen aan een benoemde hiërarchische logboekregistratiecategorie. De categorieën zijn:

Categorie Berichten
Microsoft.EntityFrameworkCore Alle EF Core-berichten
Microsoft.EntityFrameworkCore.Database Alle database-interacties
Microsoft.EntityFrameworkCore.Database.Connection Gebruik van een databaseverbinding
Microsoft.EntityFrameworkCore.Database.Command Gebruik van een databaseopdracht
Microsoft.EntityFrameworkCore.Database.Transaction Gebruik van een databasetransactie
Microsoft.EntityFrameworkCore.Update Entiteiten opslaan, met uitzondering van databaseinteracties
Microsoft.EntityFrameworkCore.Model Alle interacties tussen modellen en metagegevens
Microsoft.EntityFrameworkCore.Model.Validation Modelvalidatie
Microsoft.EntityFrameworkCore.Query Query's, met uitzondering van databaseinteracties
Microsoft.EntityFrameworkCore.Infrastructure Algemene gebeurtenissen, zoals het maken van contexten
Microsoft.EntityFrameworkCore.Scaffolding Omgekeerde engineering van databases
Microsoft.EntityFrameworkCore.Migrations Migraties
Microsoft.EntityFrameworkCore.ChangeTracking Interacties voor het bijhouden van wijzigingen

LogTo kan worden geconfigureerd om alleen de berichten van een of meer categorieën te registreren. Als u bijvoorbeeld alleen databaseinteracties wilt registreren:

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

U ziet dat de DbLoggerCategory klasse een hiërarchische API biedt voor het vinden van een categorie en de noodzaak om tekenreeksen met vaste code te voorkomen.

Omdat categorieën hiërarchisch zijn, bevat dit voorbeeld met behulp van de Database categorie alle berichten voor de subcategorieën Database.Connection, Database.Commanden Database.Transaction.

Aangepaste filters

LogTo hiermee kan een aangepast filter worden gebruikt voor gevallen waarin geen van de bovenstaande filteropties voldoende is. Als u bijvoorbeeld een bericht op niveau Information of hoger wilt registreren, evenals berichten voor het openen en sluiten van een verbinding:

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

Aanbeveling

Filteren met aangepaste filters of het gebruik van een van de andere opties die hier worden weergegeven, is efficiënter dan filteren in de LogTo gemachtigde. Dit komt doordat als het filter bepaalt dat het bericht niet moet worden vastgelegd, het logboekbericht niet eens wordt gemaakt.

Configuratie voor specifieke berichten

Met de EF Core-API ConfigureWarnings kunnen toepassingen wijzigen wat er gebeurt wanneer er een specifieke gebeurtenis wordt aangetroffen. Dit kan worden gebruikt voor het volgende:

  • Het logboekniveau wijzigen waarop de gebeurtenis wordt vastgelegd
  • Logboekregistratie van de gebeurtenis overslaan
  • Een uitzondering genereren wanneer de gebeurtenis plaatsvindt

Het logboekniveau voor een gebeurtenis wijzigen

In het vorige voorbeeld is een aangepast filter gebruikt om elk bericht te registreren bij LogLevel.Information en twee gebeurtenissen die zijn gedefinieerd voor LogLevel.Debug. Hetzelfde kan worden bereikt door het logboekniveau van de twee Debug gebeurtenissen te wijzigen in 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);

Logboekregistratie van een gebeurtenis onderdrukken

Op een vergelijkbare manier kan een afzonderlijke gebeurtenis worden onderdrukt door logboekregistratie. Dit is met name handig voor het negeren van een waarschuwing die is beoordeeld en begrepen. Voorbeeld:

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

Gooien voor een gebeurtenis

Ten slotte kan EF Core worden geconfigureerd om voor een bepaalde gebeurtenis een uitzondering te gooien. Dit is met name handig voor het wijzigen van een waarschuwing in een fout. (Dit was inderdaad het oorspronkelijke doel van ConfigureWarnings de methode, vandaar de naam.) Bijvoorbeeld:

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

Inhoud en opmaak van berichten

De standaardinhoud van LogTo is over meerdere regels verdeeld. De eerste regel bevat berichtmetagegevens:

  • Het LogLevel voorvoegsel met vier tekens
  • Een lokale tijdstempel, opgemaakt voor de huidige cultuur
  • De EventId-representatie die gekopieerd/geplakt kan worden om het lid op te halen uit CoreEventId of een van de andere EventId-klasses, plus de onbewerkte ID-waarde
  • De gebeurteniscategorie, zoals hierboven beschreven.

Voorbeeld:

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.

Deze inhoud kan worden aangepast door waarden uit DbContextLoggerOptionste geven, zoals wordt weergegeven in de volgende secties.

Aanbeveling

Overweeg het gebruik van Microsoft.Extensions.Logging voor meer controle over logboekopmaak.

UTC-tijd gebruiken

Tijdstempels zijn standaard ontworpen voor lokaal verbruik tijdens foutopsporing. Gebruik DbContextLoggerOptions.DefaultWithUtcTime in plaats daarvan cultuuragnostische UTC-tijdstempels, maar zorg ervoor dat alles hetzelfde blijft. Voorbeeld:

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

Dit voorbeeld resulteert in de volgende logboekopmaak:

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.

Loggen met één regel

Soms is het handig om precies één regel per logboekbericht op te halen. Dit kan worden ingeschakeld door DbContextLoggerOptions.SingleLine. Voorbeeld:

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

Dit voorbeeld resulteert in de volgende logboekopmaak:

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.

Andere inhoudsopties

Andere flags in DbContextLoggerOptions kunnen worden gebruikt om de hoeveelheid metadata die in het logboek is opgenomen te verminderen. Dit kan handig zijn in combinatie met enkelregelige logboekregistratie. Voorbeeld:

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

Dit voorbeeld resulteert in de volgende logboekopmaak:

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.

Overstappen van EF6

Eenvoudige logboekregistratie van EF Core verschilt van Database.Log in EF6 op twee belangrijke manieren:

  • Logboekberichten zijn niet beperkt tot alleen databaseinteracties
  • De logboekregistratie moet worden geconfigureerd tijdens de initialisatietijd van de context

Voor het eerste verschil kan het filter dat hierboven wordt beschreven, worden gebruikt om te beperken welke berichten worden geregistreerd.

Het tweede verschil is een opzettelijke wijziging om de prestaties te verbeteren door logboekberichten niet te genereren wanneer ze niet nodig zijn. Het is echter nog steeds mogelijk om een vergelijkbaar gedrag te krijgen als EF6 door een Log eigenschap op uw DbContext te maken en deze vervolgens alleen te gebruiken wanneer deze is ingesteld. Voorbeeld:

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

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