1. 介紹
在程式設計中記錄日誌不只是「在控制台輸出點東西」。記錄日誌是你的黑匣子、GPS 追蹤器和儀表板合而為一。沒有日誌,大型程式會變成謎一般的黑盒:「為什麼服務掛了?」「為什麼使用者沒收到郵件?」「到底有人在過去 3 個月啟動過這個模組嗎?」— 這些問題通常只有在日誌被保存並可取得時才能回答。
為什麼實際上需要記錄日誌?
錯誤查找與診斷
如果程式當機 — 日誌會告訴你在哪裡以及為什麼。如果程式沒當機但行為怪異 — 日誌會指出導致問題的步驟。
狀態監控
透過日誌可以知道系統現在是否運作、伺服器請求量多不多、是否有錯誤發生、誰在做什麼。
安全性
記錄未授權存取嘗試、可疑活動、認證錯誤。
審計
誰、做了什麼、何時做的。如果一個惡意管理員離職並刪除了資料,透過日誌可以恢復一切(或者至少了解他做了什麼)。
開發與運維支援
日誌不只是給程式設計師看的,還給測試人員、運維、系統管理員。六個月後你會對自己說:「謝謝你加了日誌!」
從 Console.WriteLine 到生產級日誌系統
新手的第一反應:「就用 Console.WriteLine 不就行了?」對於小型教學程式這確實夠用。但當應用在伺服器上運行、並行執行、或有數十甚至數百名使用者時,控制台就不夠用了。我們需要:
- 區分重要資訊和除錯資訊。
- 把日誌不只輸出到控制台,還要寫到檔案、資料庫、集中式系統。
- 調整日誌詳細等級(最少 — 只有錯誤,最多 — 全部訊息)。
- 自動在記錄中補上日期、時間、額外資訊。
- 靈活配置。
- 最重要的是 — 不要每次想把日誌導到別處就修改程式碼。
這就是「真正」日誌記錄器的時代開始的地方。
2. .NET 現代日誌系統基礎
在 .NET 生態系裡有一個標準、強大且靈活的日誌框架 — Microsoft.Extensions.Logging。它是隨著 ASP.NET Core 出現的一波新型 .NET 函式庫的一部分,但現在到處都能用:伺服器、桌面、甚至行動應用。
這個框架的優點?
- 抽象化 — 不把你綁定到某個具體實作(logger 可以寫檔、寫控制台、寫雲端,或同時寫到多個地方)。
- 支援日誌等級(Trace, Debug, Information, Warning, Error, Critical)。
- 可注入到 DI 容器並整合到所有現代 .NET 應用。
- 豐富的擴充生態:支援結構化日誌、高級格式化器和整合。
主要概念和類別
讓我們認識一些操作 Microsoft.Extensions.Logging 時會用到的關鍵物件:
| 類別/介面 | 用途 |
|---|---|
|
為某個類別提供類型化的日誌介面 |
|
非泛型的日誌介面 |
|
用來建立 logger 實例的工廠 |
|
允許在應用中設定日誌 |
|
日誌等級列舉 (Trace, Debug, Information, ...) |
日誌等級
按重要性分級可以讓你過濾「噪音」並快速找到想要的訊息:
| 等級 (LogLevel) | 何時使用? |
|---|---|
|
最詳細的除錯訊息,「噪音」,時間性資料 |
|
主要的除錯資訊 |
|
正常運作的關鍵事件訊息 |
|
潛在問題的警告,但系統仍在運作 |
|
需要關注的錯誤,但應用仍在運行 |
|
危及整個系統的致命錯誤 |
3. 實作:在我們的應用中加入日誌
我們不寫抽象程式碼,改用一個示範應用繼續擴充。假設我們已有一個簡單的 Calculator 類別,並會在課程中逐步擴充它。
範例:基本計算器
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
// 其它方法...
}
現在在裡面加入日誌。為此我們需要介面 ILogger<Calculator>,從外部取得(例如透過 Dependency Injection, DI)。
using Microsoft.Extensions.Logging;
public class Calculator
{
private readonly ILogger<Calculator> _logger;
public Calculator(ILogger<Calculator> logger)
{
_logger = logger;
}
public int Add(int a, int b)
{
int result = a + b;
_logger.LogInformation("執行加法: {A} + {B} = {Result}", a, b, result);
return result;
}
}
有趣的事實:
logger 支援模板和命名參數({A}, {B}, {Result}),而不是字串串接,這讓日誌結構化、便於後續自動處理與搜尋。
4. 如何在控制台應用中建立並設定 logger
1. 引入 NuGet 套件
你的專案會需要:
- Microsoft.Extensions.Logging
- Microsoft.Extensions.Logging.Console(如果想輸出到控制台)
- (可選) 其他 provider,如果需要的話
2. 設定 logger
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
// 建立 DI 容器
var serviceProvider = new ServiceCollection()
.AddLogging(builder => {
builder.AddConsole(); // 輸出到控制台
builder.SetMinimumLevel(LogLevel.Debug); // 最低日誌等級
})
.BuildServiceProvider();
// 取得指定型別的 logger 實例
var logger = serviceProvider.GetRequiredService<ILogger<Calculator>>();
var calculator = new Calculator(logger);
calculator.Add(5, 3); // 會在日誌中看到訊息
新手常犯的錯誤
「為什麼日誌沒出現在控制台?」— 檢查是否加入了需要的 provider (.AddConsole()),以及最低日誌等級是否設定正確 (SetMinimumLevel)。如果等級設定得太高,訊息會被「過濾掉」!
5. 有用的小細節
記錄錯誤與非標準情況
假設我們要記錄除以零的情況。加入相應的方法:
public int Divide(int a, int b)
{
if (b == 0)
{
_logger.LogError("嘗試除以零! a={A}", a);
throw new DivideByZeroException();
}
int result = a / b;
_logger.LogInformation("執行除法: {A} / {B} = {Result}", a, b, result);
return result;
}
為什麼要這樣做?
在真實應用中,當出現問題時,具有 Error 等級的日誌通常會被特別關注:它們會自動發送給管理員、在監控中突出顯示,並用於 alert/通知。
使用類別與標籤 (scopes)
.NET 的 logger 支援所謂的 scopes — 這是會自動加入到在某個程式區塊內所有日誌的額外 metadata。例如處理一個 web 請求或使用者 session 時,可以在 scope 裡加入其識別碼。
using (_logger.BeginScope("UserId: {UserId}", 42))
{
_logger.LogInformation("開始處理使用者資料");
// ...
}
區塊內的所有訊息都會帶上標籤 UserId: 42,之後能幫助依使用者或操作找到相關日誌。
範例:日誌等級的實際運作
_logger.LogTrace("這是 Trace — 幾乎沒人會看到");
_logger.LogDebug("這是 Debug — 給開發者看的");
_logger.LogInformation("這是 Information — 正常運作的事件");
_logger.LogWarning("這是 Warning — 可能的問題警告");
_logger.LogError("這是 Error — 需要注意的錯誤");
_logger.LogCritical("這是 Critical — 系統著火,需要消防隊!");
如果你設定了 SetMinimumLevel(LogLevel.Information),那你只會看到 Information, Warning, Error, Critical 這些等級的訊息。
技巧:
在開發階段保留 Trace 和 Debug 以利深度診斷;在生產環境通常只打開 Information 及以上,避免日誌體積爆炸並從「噪音」中漏掉重要資訊。
視覺化示意:現代日誌架構
graph TD
A[應用程式程式碼] --ILogger<YourClass>--> B[Microsoft.Extensions.Logging]
B --> C1[Console Provider]
B --> C2[File Provider]
B --> C3[Cloud/Database Provider]
C1 -.-> D1[日誌到控制台]
C2 -.-> D2[日誌到檔案]
C3 -.-> D3[日誌到監控系統]
subgraph Providers
C1
C2
C3
end
6. 額外功能與擴充
結構化日誌:
參數的值可以不只存在字串裡,還可以作為鍵值對保存,這讓你可以基於參數進行搜尋與彙總(例如透過 Seq、ELK/ElasticSearch 或 Application Insights)。
日誌 Provider:
你可以加入很多不同的 provider:檔案型、Windows EventLog、Azure 等等。
透過 appsettings.json 配置日誌
在 ASP.NET Core 裡日誌可以透過設定檔靈活配置,無需重新編譯應用程式。
透過配置設定最低日誌等級的範例 (appsettings.json):
{
"Logging": {
"LogLevel": {
"Default": "Information",
"MyApp.Calculator": "Debug",
"Microsoft": "Warning"
}
}
}
7. 使用 Microsoft.Extensions.Logging 時常見錯誤
錯誤 №1: 選錯日誌等級。
用 LogInformation 來記錄錯誤而不是 LogError 或 LogCritical 會讓在生產環境中找問題變得困難。
錯誤 №2: 忽視結構化日誌。
用字串串接而不是使用帶 placeholder 的模板({Parameter})會失去結構化日誌的好處,比如基於參數的搜尋。
錯誤 №3: 錯誤設定日誌等級。
如果最低等級設定得太高(例如用 Warning 而不是 Debug),重要訊息可能會被過濾掉。
錯誤 №4: 過多或過少的日誌。
在生產環境記錄過於詳細(例如開啟 Trace)會製造「噪音」,而日誌不足又會讓診斷變得困難。
GO TO FULL VERSION