小提示
您可以從 GitHub 下載本文的範例 。
Entity Framework Core (EF Core) 簡單的記錄可用來在開發和偵錯應用程式時輕鬆取得記錄。 此形式的記錄需要最少的設定,而且不需要額外的 NuGet 套件。
小提示
EF Core 也會與 Microsoft.Extensions.Logging 整合,這需要更多設定,但通常更適合在生產應用程式中記錄。
設定
在LogTo時,可以透過使用 從任何類型的應用程式存取 EF Core 記錄。 此組態通常會在DbContext.OnConfiguring的覆寫中完成。 例如:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(Console.WriteLine);
或者,LogTo
可以作為AddDbContext的一部分呼叫,或者建立DbContextOptions實例以傳遞至DbContext
建構函式。
小提示
使用 AddDbContext 或 DbContextOptions 實例傳遞至 DbContext 建構函式時,仍會呼叫 OnConfiguring。 不論 DbContext 的建構方式為何,這都適合套用內容組態。
管理日誌
記錄至控制台
LogTo
需要一個接受字串的 Action<T> 委派。 EF Core 會針對產生的每個記錄訊息,使用字串呼叫此委派。 接著,由委派的人決定如何處理此訊息。
方法 Console.WriteLine 通常用於此委派,如上所示。 這會導致每個日誌訊息寫入控制台。
記錄至偵錯視窗
Debug.WriteLine 可用來將輸出傳送至 Visual Studio 或其他 IDE 中的 [偵錯] 視窗。 在此案例中必須使用 Lambda 語法,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。 您可以從CoreEventId類別或RelationalEventId類別存取這些用於關係型特定訊息的識別碼。 資料庫提供者也可能在類似的類別中具有提供者特定的標識碼。 例如, SqlServerEventId 針對 SQL Server 提供者。
LogTo
可以設定為只記錄與一或多個事件標識符相關聯的訊息。 例如,只記錄與正在初始化或處置的內容相關的訊息:
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
可以設定為只記錄來自一或多個類別的訊息。 例如,若要只記錄資料庫互動:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Name });
請注意,類別 DbLoggerCategory 會提供階層式 API 來尋找類別,並避免需要硬式程式代碼字串。
由於類別是階層式的,因此使用類別的 Database
這個範例會包含子類別 Database.Connection
、 Database.Command
、 和 Database.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
定義的兩個事件。 將兩 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 可以設定為針對指定的事件擲回。 此功能特別有助於將警告變更為錯誤。 (事實上,這方法的原始目的便是如此,因此才有這個名稱。)例如:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.ConfigureWarnings(b => b.Throw(RelationalEventId.MultipleCollectionIncludeWarning))
.LogTo(Console.WriteLine);
郵件內容和格式設定
預設的LogTo
內容會跨多行進行格式化。 第一行包含訊息元資料:
- LogLevel 作為四個字元的前綴
- 針對目前文化格式化的當地時間戳
- EventId 表單中的EventId,可透過複製/貼上來取得成員,從
EventId
或另一個類別,並加上原始識別碼值。 - 事件類別,如上所述。
例如:
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.
單行記錄
有時候,每個記錄訊息只取得一行就很有用。 可以透過 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 中的 Database.Log 有兩個重大差異:
- 記錄訊息不限於資料庫互動
- 紀錄必須在上下文初始化時進行設定
針對第一個差異,上述篩選可用來限制記錄的訊息。
第二個差異是刻意進行的變更,透過不在不需要時生成記錄訊息來改善效能。 不過,您仍然可以藉由在您的物件Log
上建立DbContext
屬性,並僅在屬性已設定時使用它來達到與 EF6 類似的行為。 例如:
public Action<string> Log { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(s => Log?.Invoke(s));