在 C# 和 .NET 中記錄
.NET 透過 ILoggerAPI 支援高效能、結構化的記錄,以協助監視應用程式行為並診斷問題。 可以藉由設定不同的記錄提供者,將記錄寫入不同的目的地。 基本記錄提供者是內建的,且有許多第三方提供者可供使用。
開始使用
第一個範例會示範基本概念,但僅適用於簡單的主控台應用程式。 此範例主控台應用程式依賴以下 NuGet 套件:
在下一節中,您將了解如何改善考慮縮放、效能、設定和一般程式設計模式的程式碼。
using Microsoft.Extensions.Logging;
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
上述範例:
- 建立 ILoggerFactory。
ILoggerFactory
會儲存決定傳送記錄訊息位置的所有設定。 在此情況下,您會設定主控台 記錄提供者 ,以便將記錄訊息寫入主控台。 - 使用名為「Program」的類別建立 ILogger。 「類別」是與
ILogger
物件所記錄之每個訊息相關聯的string
。 在搜尋或篩選記錄時,它會用來將來自相同類別(或分類) 的記錄訊息分組在一起。 - 呼叫 LogInformation 以在
Information
層級記錄訊息。 記錄層級 表示記錄事件的嚴重性,並用來篩選出較不重要的記錄訊息。 記錄輸入也包含 訊息範本"Hello World! Logging is {Description}."
和機碼值組Description = fun
。 索引鍵名稱 (或預留位置) 來自範本中大括號內的文字,而值則來自其餘方法引數。
此範例的此專案檔包含兩個 NuGet 封裝:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
</ItemGroup>
</Project>
提示
所有記錄範例原始程式碼都可在範例瀏覽器中下載。 如需詳細資訊,請參閱 瀏覽程式碼範例: 在 .NET 中記錄。
在重要應用程式中記錄
在較不重要的案例中登入時,您應該考慮對上一個範例進行這幾項變更:
如果您的應用程式使用 相依性插入 (DI) 或例如 ASP.NET 的 WebApplication 或 泛型主機 等主機,那麽您應該使用來自其個別 DI 容器的
ILoggerFactory
和ILogger
物件,而不是直接建立它們。 如需詳細資訊,請參閱 與 DI 和主機整合。記錄 編譯時間來源產生 通常是
ILogger
擴充方法更好的替代方案,例如LogInformation
。 記錄來源產生可提供更佳的效能、更強的類型,並避免在方法中散佈string
常數。 取捨是,使用這項技術需要較多的程式碼。
using Microsoft.Extensions.Logging;
internal partial class Program
{
static void Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
LogStartupMessage(logger, "fun");
}
[LoggerMessage(Level = LogLevel.Information, Message = "Hello World! Logging is {Description}.")]
static partial void LogStartupMessage(ILogger logger, string description);
}
- 記錄類別名稱的建議作法是使用建立記錄訊息之類別的完整名稱。 這有助於將記錄訊息與產生它的程式代碼產生關聯,並在篩選記錄時提供良好的控制層級。 CreateLogger 會接受
Type
以讓此命名變得容易完成。
using Microsoft.Extensions.Logging;
internal class Program
{
static void Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger<Program>();
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
}
}
- 如果您不是使用主控台記錄作為唯一的生產監視解決方案,請新增您計劃使用的 記錄提供者。 例如,您可以使用 OpenTelemetry 透過 OpenTelemetry 通訊協定 (OTLP) 傳送記錄:
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
using ILoggerFactory factory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(logging =>
{
logging.AddOtlpExporter();
});
});
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
與主機和相依性插入整合
如果您的應用程式使用 相依性插入 (DI) 或例如 ASP.NET 的 WebApplication 或 泛型主機 等主機,那麽您應該使用來自 DI 容器的 ILoggerFactory
和 ILogger
物件,而不是直接建立它們。
從 DI 取得 ILogger
此範例會使用 ASP.NET 最小 API,在託管的應用程式中取得 ILogger 物件:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();
var handler = app.Services.GetRequiredService<ExampleHandler>();
app.MapGet("/", handler.HandleRequest);
app.Run();
partial class ExampleHandler(ILogger<ExampleHandler> logger)
{
public string HandleRequest()
{
LogHandleRequest(logger);
return "Hello World";
}
[LoggerMessage(LogLevel.Information, "ExampleHandler.HandleRequest was called")]
public static partial void LogHandleRequest(ILogger logger);
}
上述範例:
- 建立名為
ExampleHandler
的單一資料庫服務,並對應傳入的 Web 要求來執行ExampleHandler.HandleRequest
函式。 - 第 8 行會定義 ExampleHandler 的主要建構函式,這是 C# 12 中新增的功能。 使用較舊的樣式 C# 建構函式可以正常運作,但會更詳細一點。
- 建構函式會定義類型為
ILogger<ExampleHandler>
的參數。 ILogger<TCategoryName> 衍生自 ILogger,並指出ILogger
物件具有的類別。 DI 容器會找出具有正確類別的ILogger
,並提供它做為建構函式引數。 如果尚未存在該類別的ILogger
,DI 容器會自動從服務提供者中的ILoggerFactory
建立它。 - 建構函式中收到的
logger
參數已用於在HandleRequest
函式中記錄。
主機提供的 ILoggerFactory
主機建立器會初始化 預設設定,然後在組建主機時,將已設定的 ILoggerFactory
物件新增至主機的 DI 容器。 在組建主機之前,您可以透過 HostApplicationBuilder.Logging、WebApplicationBuilder.Logging 或其他主機上類的 API 來調整記錄設定。 主機也會將來自預設設定來源的記錄設定套用為 appsettings.json 和環境變數。 如需詳細資訊,請參閱 .NET 中的設定。
這個範例會展開前一個範例,以自訂 WebApplicationBuilder
所提供的 ILoggerFactory
。 它會將 OpenTelemetry 新增為記錄提供者,以透過 OTLP (OpenTelemetry 通訊協定) 傳輸記錄:
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddOpenTelemetry(logging => logging.AddOtlpExporter());
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();
使用 DI 建立 ILoggerFactory
如果您在使用不含主機的 DI 容器,請使用 AddLogging 來設定和新增 ILoggerFactory
至容器。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
// Add services to the container including logging
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
services.AddSingleton<ExampleService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
// Get the ExampleService object from the container
ExampleService service = serviceProvider.GetRequiredService<ExampleService>();
// Do some pretend work
service.DoSomeWork(10, 20);
class ExampleService(ILogger<ExampleService> logger)
{
public void DoSomeWork(int x, int y)
{
logger.LogInformation("DoSomeWork was called. x={X}, y={Y}", x, y);
}
}
上述範例:
- 已建立 DI 服務容器,其中包含
ILoggerFactory
設定為寫入主控台的 - 已將單一資料庫
ExampleService
新增至容器 - 已從 DI 容器建立
ExampleService
的執行個體,也已自動建立一個ILogger<ExampleService>
以當做建構函式引數使用。 - 已叫用
ExampleService.DoSomeWork
,它會用ILogger<ExampleService>
來將訊息記錄至主控台。
設定記錄
記錄設定得在程式碼中設定,或透過外部來源 (例如設定檔和環境變數) 來設定。 如果可能的話,使用外部設定會很有幫助,因為它不需要重建應用程式便可變更。 不過,某些工作,例如設定記錄提供者,只能從程式碼進行設定。
在沒有程式碼的情況下設定記錄
對於使用主機的應用程式,記錄設定通常是由 appsettings.{Environment}
.json 檔案的 "Logging"
區段提供。 對於不使用主機的應用程式,外部設定來源會 明確設定 或改為 在程式碼中設定。
下列 appsettings.Development.json 檔案是由 .NET 背景工作角色服務範本產生:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
在上述 JSON 中:
- 會指定
"Default"
、"Microsoft"
和"Microsoft.Hosting.Lifetime"
記錄層級類別。 - 此值
"Default"
會套用至未指定的所有類別,實際上會讓所有類別的所有預設值都成為"Information"
。 您可以透過指定類別的值來覆寫此行為。 "Microsoft"
類別會套用至開頭為"Microsoft"
的所有類別。"Microsoft"
類別會以Warning
和更高的記錄層級記錄。"Microsoft.Hosting.Lifetime"
類別比"Microsoft"
類別更具體,因此"Microsoft.Hosting.Lifetime"
類別會以"Information"
和更高的記錄層級記錄。- 未指定特定的記錄提供者,因此
LogLevel
適用於 Windows EventLog 以外的所有已啟用記錄提供者。
Logging
屬性可以有 LogLevel 與記錄提供者屬性。 LogLevel
會指定要針對所選類別記錄的最小層級。 在上述 JSON 中,會指定 Information
和 Warning
記錄層級。 LogLevel
表示記錄的嚴重性,範圍從 0 到 6:
Trace
= 0、Debug
= 1、Information
= 2、Warning
= 3、Error
= 4、Critical
= 5 和 None
= 6。
指定 LogLevel
時,會針對指定層級和更高層級的訊息啟用記錄。 在上述 JSON 中,Default
類別會以 Information
和更高層級記錄。 例如,會記錄 Information
、Warning
、Error
和 Critical
訊息。 如果未指定 LogLevel
,則記錄預設為 Information
層級。 如需詳細資訊,請參閱記錄層級。
提供者屬性可以指定 LogLevel
屬性。 LogLevel
在提供者下,會指定要針對該提供者記錄的層級,並覆寫非提供者記錄設定。 以下列 appsettings.json 檔案為例:
{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting": "Trace"
}
},
"EventSource": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
Logging.{ProviderName}.LogLevel
中的設定會覆寫 Logging.LogLevel
中的設定。 在上述 JSON 中,Debug
提供者的預設記錄層級會設定為 Information
:
Logging:Debug:LogLevel:Default:Information
上述設定會指定每個 Logging:Debug:
類別的 Information
記錄層級,但 Microsoft.Hosting
除外。 列出特定類別時,特定類別會覆寫預設類別。 在上述 JSON 中,Logging:Debug:LogLevel
類別 "Microsoft.Hosting"
和 "Default"
會覆寫 Logging:LogLevel
中的設定
您可以針對下列任一項目指定最小記錄層級:
- 特定提供者:例如
Logging:EventSource:LogLevel:Default:Information
- 特定類別:例如
Logging:LogLevel:Microsoft:Warning
- 所有提供者和所有類別:
Logging:LogLevel:Default:Warning
低於最低層級的任何記錄都不是:
- 傳遞至提供者。
- 已記錄或顯示。
若要隱藏所有記錄,請指定 LogLevel.None。 LogLevel.None
的值為 6,其高於 LogLevel.Critical
(5)。
若提供者支援記錄範圍,IncludeScopes
會指出是否已啟用記錄範圍。 如需詳細資訊,請參閱記錄範圍
下列 appsettings.json 檔案包含所有內建提供者的設定:
{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft.Extensions.Hosting": "Warning",
"Default": "Information"
}
},
"EventSource": {
"LogLevel": {
"Microsoft": "Information"
}
},
"EventLog": {
"LogLevel": {
"Microsoft": "Information"
}
},
"AzureAppServicesFile": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Warning"
}
},
"AzureAppServicesBlob": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Information"
}
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information"
}
}
}
}
在上述範例中:
- 類別和層級不是建議的值。 會提供此範例以顯示所有的預設提供者。
Logging.{ProviderName}.LogLevel
中的設定會覆寫Logging.LogLevel
中的設定。 例如,Debug.LogLevel.Default
中的層級會覆寫LogLevel.Default
中的層級。- 會使用每個提供者的「別名」。 每個提供者都會定義「別名」,可在設定中用來取代完整類型名稱。 內建提供者的別名如下:
Console
Debug
EventSource
EventLog
AzureAppServicesFile
AzureAppServicesBlob
ApplicationInsights
依命令列、環境變數和其他組態設定記錄層級
記錄層級可由任何設定提供者進行設定。 例如,您可以建立名為 Logging:LogLevel:Microsoft
且值為 Information
的永續性環境變數。
搭配指定的記錄層級值建立並指派永續性環境變數。
:: Assigns the env var to the value
setx "Logging__LogLevel__Microsoft" "Information" /M
在「新的」命令提示字元執行個體中,讀取環境變數。
:: Prints the env var value
echo %Logging__LogLevel__Microsoft%
上述環境設定會保存在環境中。 若要在使用以 .NET 背景工作角色服務範本建立的應用程式時測試設定,請在指派環境變數之後,在專案目錄中使用 dotnet run
命令。
dotnet run
提示
設定環境變數之後,請重新啟動您的整合式開發環境 (IDE),以確保新增的環境變數可供使用。
在 Azure App Service 上,選取 [設定] > [組態] 頁面上的 [新增應用程式設定]。 Azure App Service 應用程式設定為:
- 在待用及透過加密通道傳輸時加密。
- 公開為環境變數。
如需使用環境變數設定 .NET 設定值的詳細資訊,請參閱 環境變數。
使用程式碼設定記錄
若要在程式碼中設定記錄,請使用 ILoggingBuilder API。 這可以從不同的位置存取:
- 直接建立
ILoggerFactory
時,請在 LoggerFactory.Create 中設定。 - 在沒有主機的情況下使用 DI 時,請在 LoggingServiceCollectionExtensions.AddLogging 中設定。
- 使用主機時,請使用 HostApplicationBuilder.Logging、 WebApplicationBuilder.Logging 或其他主機特定 API 進行設定。
using Microsoft.Extensions.Logging;
using var loggerFactory = LoggerFactory.Create(static builder =>
{
builder
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogDebug("Hello {Target}", "Everyone");
在上述範例中, AddFilter 會用來調整針對各種類別啟用的 記錄層級。 已使用 AddConsole 來新增主控台記錄提供者。 根據預設,不會啟用嚴重性為 Debug
的記錄,但因為設定已調整篩選條件,因此主控台上會顯示偵錯訊息 「Hello Everyone」。
如何套用篩選規則
建立 ILogger<TCategoryName> 物件時,ILoggerFactory 物件會針對每個提供者選取一個規則來套用到該記錄器。 由 ILogger
執行個體寫入的所有訊息都會根據選取的規則進行篩選。 系統會從可用的規則中,選取對每個提供者和類別配對最明確的規則。
當建立指定類別的 ILogger
時,系統會針對每個提供者使用下列演算法:
- 選取所有符合提供者或其別名的規則。 如果找不到符合的項目,請選取所有規則搭配空白提供者。
- 從上一個步驟的結果中,選取具有最長相符類別前置字元的規則。 如果找不到符合的項目,請選取未指定類別的所有規則。
- 如果選取多個規則,請使用最後一個。
- 如果未選取任何規則,請使用 LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) 來指定最小記錄層級。
記錄類別
建立 ILogger
物件時,會指定「類別」。 該類別會包含在每個由該 ILogger
執行個體所產生的記錄訊息中。 類別字串為任意值,但慣例是使用完整類別名稱。 例如,在具有已定義為類似下列物件之服務的應用程式中,類別可能是 "Example.DefaultService"
:
namespace Example
{
public class DefaultService : IService
{
private readonly ILogger<DefaultService> _logger;
public DefaultService(ILogger<DefaultService> logger) =>
_logger = logger;
// ...
}
}
如果需要進一步分類,慣例是藉由將子類別目錄附加至完整類別名稱,並使用 LoggerFactory.CreateLogger 明確地指定類別目錄,以使用階層式名稱:
namespace Example
{
public class DefaultService : IService
{
private readonly ILogger _logger;
public DefaultService(ILoggerFactory loggerFactory) =>
_logger = loggerFactory.CreateLogger("Example.DefaultService.CustomCategory");
// ...
}
}
在多個類別/類型中使用時,以固定名稱呼叫 CreateLogger
可能會很實用,因為可依類別來組織事件。
ILogger<T>
相當於使用 T
的完整類型名稱來呼叫 CreateLogger
。
記錄等級
下表列出 LogLevel 值、便利的 Log{LogLevel}
擴充方法和建議的使用方式:
LogLevel | 值 | 方法 | 描述 |
---|---|---|---|
追蹤 | 0 | LogTrace | 包含最詳細的訊息。 這些訊息可能包含敏感性應用程式資料。 這些訊息預設會停用,且不應在生產環境中啟用。 |
偵錯 | 1 | LogDebug | 用於偵錯和開發。 由於大量,因此在生產環境中請謹慎使用。 |
資訊 | 2 | LogInformation | 追蹤一般應用程式流程。 可能具有長期值。 |
警告 | 3 | LogWarning | 針對異常或意外事件。 通常包含不會導致應用程式失敗的錯誤或狀況。 |
錯誤 | 4 | LogError | 發生無法處理的錯誤和例外狀況。 這些訊息指出目前作業或要求中發生失敗,而不是整個應用程式的失敗。 |
重大 | 5 | LogCritical | 發生需要立即注意的失敗。 範例:資料遺失情況、磁碟空間不足。 |
None | 6 | 指定不應該寫入任何訊息。 |
在上表中,會從最低到最高嚴重性列出 LogLevel
。
Log 方法的第一個參數 (LogLevel) 表示記錄的嚴重性。 大部分開發人員會呼叫 Log{LogLevel} 擴充方法,而不是呼叫 Log(LogLevel, ...)
。 Log{LogLevel}
擴充方法會呼叫 Log
方法,並指定 LogLevel
。 例如,下列兩個記錄呼叫在功能上相等,並產生相同的記錄:
public void LogDetails()
{
var logMessage = "Details for log.";
_logger.Log(LogLevel.Information, AppLogEvents.Details, logMessage);
_logger.LogInformation(AppLogEvents.Details, logMessage);
}
AppLogEvents.Details
是事件識別碼,並以常數 Int32 值隱含表示。 AppLogEvents
是公開各種具名識別碼常數的類別,會顯示在記錄事件識別碼區段中。
下列程式碼會建立 Information
與 Warning
記錄:
public async Task<T> GetAsync<T>(string id)
{
_logger.LogInformation(AppLogEvents.Read, "Reading value for {Id}", id);
var result = await _repository.GetAsync(id);
if (result is null)
{
_logger.LogWarning(AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
}
return result;
}
在上述程式代碼中,第一個 Log{LogLevel}
參數 AppLogEvents.Read
是 Log事件標識碼。 第二個參數是訊息範本,其中的預留位置會置入其餘方法參數所提供的引數值。 此文章稍後的訊息範本小節將詳細說明方法參數。
設定適當的記錄層級,並呼叫正確的 Log{LogLevel}
方法來控制寫入特定儲存媒體的記錄輸出量。 例如:
- 生產環境:
Trace
或Debug
層級的記錄會產生大量的詳細記錄訊息。 若要控制成本,且不超過資料儲存體限制,請將Trace
和Debug
層級訊息記錄為大量、低成本的資料存放區。 請考慮將Trace
和Debug
限制為特定類別。- 透過
Critical
層級的Warning
記錄應該會產生一些記錄訊息。- 成本和儲存體限制通常無所謂。
- 記錄數少可讓資料存放區選擇更具彈性。
- 開發中:
- 設定為
Warning
。 - 進行疑難排解時,新增
Trace
或Debug
訊息。 若要限制輸出,請僅針對調查中的類別設定Trace
或Debug
。
- 設定為
下列 JSON 會設定 Logging:Console:LogLevel:Microsoft:Information
:
{
"Logging": {
"LogLevel": {
"Microsoft": "Warning"
},
"Console": {
"LogLevel": {
"Microsoft": "Information"
}
}
}
}
記錄事件識別碼
每個記錄檔都可以指定事件識別碼,EventId 是具有 Id
和選擇性的 Name
唯讀屬性的結構。 範例原始程式碼會使用 AppLogEvents
類別來定義事件識別碼:
using Microsoft.Extensions.Logging;
internal static class AppLogEvents
{
internal static EventId Create = new(1000, "Created");
internal static EventId Read = new(1001, "Read");
internal static EventId Update = new(1002, "Updated");
internal static EventId Delete = new(1003, "Deleted");
// These are also valid EventId instances, as there's
// an implicit conversion from int to an EventId
internal const int Details = 3000;
internal const int Error = 3001;
internal static EventId ReadNotFound = 4000;
internal static EventId UpdateNotFound = 4001;
// ...
}
提示
如需將 int
轉換成 EventId
的詳細資訊,請參閱 EventId.Implicit(Int32 轉換為 EventId) 運算子。
事件識別碼會將一組事件關聯。 例如,與從存放庫讀取值相關的所有記錄可能是 1001
。
記錄提供者可能會將事件識別碼存放到識別碼欄位、存放到記錄訊息中,或是完全不存放。 偵錯提供者不會顯示事件識別碼。 主控台提供者會在類別後面以括弧顯示事件識別碼:
info: Example.DefaultService.GetAsync[1001]
Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
GetAsync(a1b2c3) not found
有些記錄提供者會將事件識別碼儲存在欄位中,以允許篩選識別碼。
記錄訊息範本
每個記錄 API 都會使用訊息範本。 訊息範本可以包含有關提供哪個引數的預留位置。 使用名稱而非數字做為預留位置。 預留位置的順序 (而不是其名稱) 會決定使用哪些參數來提供其值。 在下列程式碼中,參數名稱在訊息範本中並未依順序出現:
string p1 = "param1";
string p2 = "param2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);
上述程式碼會使用依順序的參數值建立記錄訊息:
Parameter values: param1, param2
注意
在單一訊息範本內使用多個預留位置時請務必留意,因為其是以序數為基礎。 系統「不會」使用這些名稱來將引數與預留位置對齊。
此方法可讓記錄提供者實作語意或結構化記錄。 引數本身會被傳遞到記錄系統,而不只是格式化的訊息範本。 這可讓記錄提供者將參數值儲存為欄位。 以下列記錄器方法為例:
_logger.LogInformation("Getting item {Id} at {RunTime}", id, DateTime.Now);
例如,登入 Azure 資料表儲存體時:
- 每個 Azure 資料表實體都可以有
ID
和RunTime
屬性。 - 具有屬性的資料表可簡化針對記錄資料的查詢。 例如,查詢可以尋找特定
RunTime
範圍內的所有記錄,而不必剖析文字訊息中的時間。
記錄訊息範本格式設定
記錄訊息範本支援預留位置格式設定。 範本可以針對指定的類型引數自由指定任何有效格式。 例如,以下列 Information
記錄器訊息範本為例:
_logger.LogInformation("Logged on {PlaceHolderName:MMMM dd, yyyy}", DateTimeOffset.UtcNow);
// Logged on January 06, 2022
在上述範例中,DateTimeOffset
執行個體是對應至記錄器訊息範本中的 PlaceHolderName
的類型。 這個名稱可以是任何名稱,因為值是以序數為基礎。 MMMM dd, yyyy
格式對 DateTimeOffset
類型有效。
如需 DateTime
和 DateTimeOffset
格式設定的詳細資訊,請參閱自訂日期和時間格式字串。
範例
下列範例示範如何使用 {}
預留位置語法設定訊息範本格式。 此外,其輸出會顯示逸出 {}
預留位置語法的範例。 最後,也會顯示具有範本化預留位置的字串插補:
logger.LogInformation("Number: {Number}", 1); // Number: 1
logger.LogInformation("{{Number}}: {Number}", 3); // {Number}: 3
logger.LogInformation($"{{{{Number}}}}: {{Number}}", 5); // {Number}: 5
提示
- 在大部分情況下,您應該在記錄時使用記錄訊息範本格式設定。 使用字串內插補點可能會導致效能問題。
- 程式碼分析規則 CA2254: 範本應該是靜態表達式,可協助警示您記錄訊息未使用適當格式的位置。
記錄例外狀況
記錄器方法具有多載可接受例外狀況參數:
public void Test(string id)
{
try
{
if (id is "none")
{
throw new Exception("Default Id detected.");
}
}
catch (Exception ex)
{
_logger.LogWarning(
AppLogEvents.Error, ex,
"Failed to process iteration: {Id}", id);
}
}
例外狀況記錄是提供者特定的。
預設記錄層級
如果未設定預設記錄層級,則預設記錄層級值為 Information
。
例如,以下列背景工作角色服務應用程式為例:
- 已使用 .NET 背景工作角色範本建立。
- 已刪除或重新命名 appsettings.json 和 appsettings.Development.json。
在上述設定中,瀏覽至隱私權或首頁會產生許多類別名稱中具有 Microsoft
的 Trace
、Debug
和 Information
訊息。
下列程式碼會在設定中未設定預設記錄層級時,設定預設記錄層級:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.SetMinimumLevel(LogLevel.Warning);
using IHost host = builder.Build();
await host.RunAsync();
Filter 函式
針對組態或程式碼未指派規則的所有提供者和類別,會叫用篩選函式:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddFilter((provider, category, logLevel) =>
{
return provider.Contains("ConsoleLoggerProvider")
&& (category.Contains("Example") || category.Contains("Microsoft"))
&& logLevel >= LogLevel.Information;
});
using IHost host = builder.Build();
await host.RunAsync();
上述程式碼會在類別包含 Example
或 Microsoft
且記錄層級為 Information
或更高層級時顯示主控台記錄。
記錄範圍
「範圍」可用來將邏輯作業分組。 此分組功能可用來將相同的資料附加到已建立為集合之一部分的每個記錄。 例如,在處理邀交易時建立的每個記錄都可以包括該交易識別碼。
範圍:
- 這是 BeginScope 方法所傳回的 IDisposable 類型。
- 會持續到處置為止。
下列提供者支援範圍:
透過將記錄器呼叫封裝在 using
區塊中以使用範圍:
public async Task<T> GetAsync<T>(string id)
{
T result;
var transactionId = Guid.NewGuid().ToString();
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("TransactionId", transactionId),
}))
{
_logger.LogInformation(
AppLogEvents.Read, "Reading value for {Id}", id);
var result = await _repository.GetAsync(id);
if (result is null)
{
_logger.LogWarning(
AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
}
}
return result;
}
下列 JSON 會啟用主控台提供者的範圍:
{
"Logging": {
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Warning",
"Default": "Information"
}
},
"LogLevel": {
"Default": "Debug"
}
}
}
下列程式碼會啟用主控台提供者的範圍:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole(options => options.IncludeScopes = true);
using IHost host = builder.Build();
await host.RunAsync();
在 Main 中建立記錄
下列程式碼會藉由在建置主機之後,從 DI 取得 Main
執行個體來登入 ILogger
:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using IHost host = Host.CreateApplicationBuilder(args).Build();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Host created.");
await host.RunAsync();
上述程式碼需依賴兩個 NuGet 套件:
其專案檔看起來應該像下面這樣:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
</ItemGroup>
</Project>
無非同步記錄器方法
記錄速度應該很快,不值得花費非同步程式碼的效能成本來處理。 若記錄資料存放區很慢,請不要直接寫入其中。 請考慮一開始將記錄寫入到快速的存放區,稍後再將其移到慢速存放區。 例如,當記錄到 SQL Server 時,請勿在 Log
方法中直接執行,因為 Log
方法是同步的。 相反地,以同步方式將記錄訊息新增到記憶體內佇列,並讓背景工作角色提取出佇列的訊息,藉此執行推送資料到 SQL Server 的非同步工作。
變更執行中應用程式的記錄層級
記錄 API 不包含應用程式執行時變更記錄層級的案例。 不過,某些組態提供者能夠重新載入組態,這會立即對記錄組態生效。 例如,檔案設定提供者預設會重新載入記錄設定。 如果在應用程式執行時變更程式碼中的設定,則應用程式可以呼叫 IConfigurationRoot.Reload 來更新應用程式的記錄設定。
NuGet 套件
ILogger<TCategoryName> 和 ILoggerFactory 介面和實作會包含在大部分 .NET SDK 中,做為隱含套件參考。 若未以隱含方式參考,這些套件也會在下列 NuGet 套件中明確提供:
如需 .NET SDK 包含隱含套件參考的詳細資訊,請參閱 .NET SDK:隱含命名空間的資料表。
另請參閱
- .NET 中的記錄提供者
- 在 .NET 中實作自訂記錄提供者
- 主控台記錄格式
- .NET 中的高效能記錄
- 程式庫作者的 .NET 記錄指導
- 應該在 github.com/dotnet/runtime/ (英文) 存放庫中建立記錄錯誤 (Bug)