Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Tipp
Sie können das Beispiel dieses Artikels von GitHub herunterladen.
Die einfache Protokollierung von Entity Framework Core (EF Core) kann zum einfachen Abrufen von Protokollen beim Entwickeln und Debuggen von Anwendungen verwendet werden. Diese Form der Protokollierung erfordert minimale Konfiguration und keine zusätzlichen NuGet-Pakete.
Tipp
EF Core ist auch in Microsoft.Extensions.Logging integriert, was eine größere Konfiguration erfordert, aber häufig für die Protokollierung in Produktionsanwendungen besser geeignet ist.
Konfiguration
Auf EF Core-Protokolle kann über jede Art von Anwendung mithilfe von LogTo zugegriffen werden, wenn eine DbContext-Instanz konfiguriert wird. Diese Konfiguration wird üblicherweise in einer Überschreibung von DbContext.OnConfiguring durchgeführt. Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(Console.WriteLine);
Alternativ kann LogTo
als Teil von AddDbContext aufgerufen werden oder beim Erstellen einer DbContextOptions Instanz, um an den DbContext
Konstruktor übergeben zu werden.
Tipp
OnConfiguring wird weiterhin aufgerufen, wenn AddDbContext verwendet wird oder eine DbContextOptions-Instanz an den DbContext-Konstruktor übergeben wird. Dadurch ist es der ideale Ort, um die Kontextkonfiguration anzuwenden, unabhängig davon, wie der DbContext erstellt wird.
Leiten von Logdateien
Protokollierung an der Konsole
LogTo
erfordert einen Action<T> Delegat, der einen String akzeptiert. EF Core ruft diesen Delegat mit einer Zeichenfolge für jede generierte Protokollnachricht auf. Es liegt dann an der Stellvertretung, etwas mit der angegebenen Nachricht zu tun.
Die Console.WriteLine Methode wird häufig für diesen Delegat verwendet, wie oben gezeigt. Dies führt dazu, dass jede Protokollnachricht in die Konsole geschrieben wird.
Protokollierung im Debugfenster
Debug.WriteLine kann verwendet werden, um die Ausgabe an das Debugfenster in Visual Studio oder andere IDEs zu senden.
Die Lambda-Syntax muss in diesem Fall verwendet werden, da die Debug
Klasse aus Releasebuilds kompiliert wird. Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(message => Debug.WriteLine(message));
Anmelden bei einer Datei
Zum Schreiben in eine Datei muss ein StreamWriter oder ein ähnliches Objekt für die Datei erstellt werden. Die WriteLine Methode kann dann wie in den anderen Beispielen oben verwendet werden. Denken Sie daran, sicherzustellen, dass die Datei sauber geschlossen wird, indem Sie den Writer ordnungsgemäß entsorgen, wenn der Kontext geschlossen wird. Beispiel:
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();
}
Tipp
Erwägen Sie die Verwendung von Microsoft.Extensions.Logging für die Protokollierung bei Dateien in Produktionsanwendungen.
Abrufen detaillierter Nachrichten
Sensible Daten
Standardmäßig enthält EF Core nicht die Werte von Daten in Ausnahmemeldungen. Dies liegt daran, dass solche Daten vertraulich sein können und in der Produktionsverwendung offenbart werden können, wenn eine Ausnahme nicht behandelt wird.
Das Wissen von Datenwerten, insbesondere für Schlüssel, kann jedoch beim Debuggen sehr hilfreich sein. Dies kann in EF Core durch Aufrufen von EnableSensitiveDataLogging() aktiviert werden. Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine)
.EnableSensitiveDataLogging();
Detaillierte Abfrageausnahmen
Aus Leistungsgründen umschließt EF Core nicht jeden Aufruf, um einen Wert vom Datenbankanbieter in einem Try-Catch-Block zu lesen. Dies führt jedoch manchmal zu Ausnahmen, die schwer zu diagnostizieren sind, insbesondere, wenn die Datenbank einen NULL-Wert zurückgibt, wenn das Modell nicht zulässig ist.
Das Aktivieren von EnableDetailedErrors führt dazu, dass EF dann die Try-Catch-Blöcke einführt und dadurch detailliertere Fehler bereitstellt. Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine)
.EnableDetailedErrors();
Filterung
Protokollebenen
Jede EF Core-Protokollnachricht wird einer durch die LogLevel Enumeration definierten Ebene zugewiesen. Standardmäßig enthält die einfache Protokollierung in EF Core jede Nachricht ab der Ebene Debug
oder höher.
LogTo
kann eine höhere Mindeststufe übergeben werden, um einige Nachrichten herauszufiltern. Beispiel: Das Übergeben von Information
führt zu einem minimalen Set von Logs, die auf den Datenbankzugriff beschränkt sind, und einigen Verwaltungsmeldungen.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
Bestimmte Nachrichten
Jeder Protokollnachrichten wird ein EventId zugewiesen. Auf diese IDs kann über die CoreEventId Klasse oder die RelationalEventId Klasse für relationale Nachrichten zugegriffen werden. Ein Datenbankanbieter verfügt möglicherweise auch über anbieterspezifische IDs in einer ähnlichen Klasse. Beispiel: SqlServerEventId für den SQL Server-Anbieter.
LogTo
kann so konfiguriert werden, dass nur die Nachrichten protokolliert werden, die einer oder mehreren Ereignis-IDs zugeordnet sind. So protokollieren Sie beispielsweise nur Nachrichten für den Kontext, der initialisiert oder verworfen wird:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine, new[] { CoreEventId.ContextDisposed, CoreEventId.ContextInitialized });
Nachrichtenkategorien
Jede Protokollnachricht wird einer benannten hierarchischen Loggerkategorie zugewiesen. Die Kategorien sind:
Kategorie | Meldungen |
---|---|
Microsoft.EntityFrameworkCore | Alle EF Core-Nachrichten |
Microsoft.EntityFrameworkCore.Database | Alle Datenbankinteraktionen |
Microsoft.EntityFrameworkCore.Database.Connection | Verwendung einer Datenbankverbindung |
Microsoft.EntityFrameworkCore.Database.Command | Verwendung eines Datenbankbefehls |
Microsoft.EntityFrameworkCore.Database.Transaction | Verwendung einer Datenbanktransaktion |
Microsoft.EntityFrameworkCore.Update | Speichern von Entitäten mit Ausnahme von Datenbankinteraktionen |
Microsoft.EntityFrameworkCore.Model | Alle Modell- und Metadateninteraktionen |
Microsoft.EntityFrameworkCore.Model.Validation | Modellvalidierung |
Microsoft.EntityFrameworkCore.Query | Abfragen mit Ausnahme von Datenbankinteraktionen |
Microsoft.EntityFrameworkCore.Infrastructure | Allgemeine Ereignisse, wie z. B. Kontextgestaltung |
Microsoft.EntityFrameworkCore.Scaffolding | Reverse Engineering der Datenbank |
Microsoft.EntityFrameworkCore.Migrationen | Migrationen |
Microsoft.EntityFrameworkCore.ChangeTracking | Interaktionen zur Änderungsnachverfolgung |
LogTo
kann so konfiguriert werden, dass nur die Nachrichten aus einer oder mehreren Kategorien protokolliert werden. Um beispielsweise nur Datenbankinteraktionen zu protokollieren:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Name });
Beachten Sie, dass die DbLoggerCategory Klasse eine hierarchische API zum Suchen einer Kategorie bereitstellt und die Notwendigkeit von hartcodierten Zeichenfolgen vermeidet.
Da Kategorien hierarchisch sind, enthält dieses Beispiel, in dem die Database
Kategorie verwendet wird, alle Nachrichten für die Unterkategorien Database.Connection
, Database.Command
und Database.Transaction
.
Benutzerdefinierte Filter
LogTo
ermöglicht die Verwendung eines benutzerdefinierten Filters für Fälle, in denen keine der oben genannten Filteroptionen ausreichend ist. Zum Protokollieren von Nachrichten auf Ebene Information
oder höher, sowie von Nachrichten zum Öffnen und Schließen einer Verbindung:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(
Console.WriteLine,
(eventId, logLevel) => logLevel >= LogLevel.Information
|| eventId == RelationalEventId.ConnectionOpened
|| eventId == RelationalEventId.ConnectionClosed);
Tipp
Das Filtern mit benutzerdefinierten Filtern oder mit einer der hier gezeigten Optionen ist effizienter als das Filtern im LogTo
Delegate. Dies liegt daran, dass, wenn der Filter bestimmt, dass die Nachricht nicht protokolliert werden soll, die Protokollnachricht nicht einmal erstellt wird.
Konfiguration für bestimmte Nachrichten
Mit der EF Core-API ConfigureWarnings können Anwendungen ändern, was passiert, wenn ein bestimmtes Ereignis auftritt. Dies kann verwendet werden, um:
- Ändern der Protokollebene, auf der das Ereignis protokolliert wird
- Protokollierung des Ereignisses vollständig überspringen
- Auslösen einer Ausnahme, wenn das Ereignis auftritt
Ändern der Protokollebene für ein Ereignis
Im vorherigen Beispiel wurde ein benutzerdefinierter Filter verwendet, um jede Nachricht zu LogLevel.Information
zu protokollieren sowie zwei Ereignisse für LogLevel.Debug
zu definieren. Dasselbe kann erreicht werden, indem die Protokollierungsstufe bei den beiden Debug
-Ereignissen auf Information
geändert wird.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.ConfigureWarnings(
b => b.Log(
(RelationalEventId.ConnectionOpened, LogLevel.Information),
(RelationalEventId.ConnectionClosed, LogLevel.Information)))
.LogTo(Console.WriteLine, LogLevel.Information);
Unterdrücken der Protokollierung eines Ereignisses
Auf ähnliche Weise kann ein einzelnes Ereignis aus der Protokollierung unterdrückt werden. Dies ist besonders nützlich, um eine Warnung zu ignorieren, die überprüft und verstanden wurde. Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.ConfigureWarnings(b => b.Ignore(CoreEventId.DetachedLazyLoadingWarning))
.LogTo(Console.WriteLine);
Werfen für eine Veranstaltung
Schließlich kann EF Core so konfiguriert werden, dass es für ein bestimmtes Ereignis ausgelöst wird. Dies ist besonders nützlich, um eine Warnung in einen Fehler zu ändern. (Tatsächlich war dies der ursprüngliche Zweck der ConfigureWarnings
Methode, daher der Name.) Zum Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.ConfigureWarnings(b => b.Throw(RelationalEventId.MultipleCollectionIncludeWarning))
.LogTo(Console.WriteLine);
Nachrichteninhalte und -formatierung
Der Standardinhalt von LogTo
wird über mehrere Zeilen formatiert. Die erste Zeile enthält Nachrichtenmetadaten:
- Das LogLevel Präfix als vierstelliges Zeichen
- Ein lokaler Zeitstempel, formatiert für die aktuelle Kultur
- Das EventId in der Form, das kopiert und eingefügt werden kann, um das Element aus CoreEventId oder einer der anderen
EventId
Klassen abzurufen, plus den rohen ID-Wert. - Die Ereigniskategorie, wie oben beschrieben.
Beispiel:
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.
Dieser Inhalt kann angepasst werden, indem Werte von DbContextLoggerOptions weitergegeben werden, wie in den folgenden Abschnitten gezeigt.
Tipp
Erwägen Sie die Verwendung von Microsoft.Extensions.Logging , um mehr Kontrolle über die Protokollformatierung zu haben.
Verwenden der UTC-Zeit
Standardmäßig sind Zeitstempel für den lokalen Verbrauch beim Debuggen ausgelegt. Verwenden Sie DbContextLoggerOptions.DefaultWithUtcTime stattdessen kulturunabhängige UTC-Zeitstempel, aber behalten Sie alles andere gleich. Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(
Console.WriteLine,
LogLevel.Debug,
DbContextLoggerOptions.DefaultWithUtcTime);
In diesem Beispiel wird die folgende Protokollformatierung angezeigt:
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.
Protokollierung mit einer einzelnen Zeile
Manchmal ist es nützlich, genau eine Zeile pro Protokollnachricht abzurufen. Dies kann durch DbContextLoggerOptions.SingleLineaktiviert werden. Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(
Console.WriteLine,
LogLevel.Debug,
DbContextLoggerOptions.DefaultWithLocalTime | DbContextLoggerOptions.SingleLine);
In diesem Beispiel wird die folgende Protokollformatierung angezeigt:
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.
Weitere Inhaltsoptionen
Andere Flags in DbContextLoggerOptions können verwendet werden, um die Menge der im Protokoll enthaltenen Metadaten zu kürzen. Dies kann in Verbindung mit der Einzelzeilenprotokollierung hilfreich sein. Beispiel:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(
Console.WriteLine,
LogLevel.Debug,
DbContextLoggerOptions.UtcTime | DbContextLoggerOptions.SingleLine);
In diesem Beispiel wird die folgende Protokollformatierung angezeigt:
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.
Wechsel von EF6
Die einfache Protokollierung in EF Core unterscheidet sich in zwei wichtigen Punkten von EF6: Database.Log
- Protokollmeldungen sind nicht nur auf Datenbankinteraktionen beschränkt
- Die Protokollierung muss während der Initialisierung des Kontexts konfiguriert werden.
Für den ersten Unterschied kann die oben beschriebene Filterung verwendet werden, um zu begrenzen, welche Nachrichten protokolliert werden.
Der zweite Unterschied ist eine beabsichtigte Änderung, um die Leistung zu verbessern, indem keine Protokollmeldungen generiert werden, wenn sie nicht benötigt werden. Es ist jedoch weiterhin möglich, ein ähnliches Verhalten wie EF6 zu erhalten, indem eine Log
-Eigenschaft in Ihrem DbContext
erstellt und nur verwendet wird, wenn sie gesetzt ist. Beispiel:
public Action<string> Log { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(s => Log?.Invoke(s));