可觀測性是現代應用的核心能力,通過日誌(Logging)、指標(Metrics)和追蹤(Tracing)幫助開發者快速定位問題、優化性能。本文將重點介紹 Serilog/NLog 集成、Application Insights 配置 和 結構化日誌實踐,幫助構建高效、可維護的日誌與監控體系。
1. Serilog/NLog 集成:結構化日誌框架
1.1 Serilog 核心特性
- 結構化日誌:支持 JSON 格式,便於機器解析和分析。
- 豐富的 Sink(輸出目標):支持文件、數據庫、控制枱、HTTP 等。
- 上下文增強:通過
LogContext添加請求 ID、用户信息等動態字段。 - 與 ASP.NET Core 無縫集成。
1.2 NLog 核心特性
- 基於 XML/代碼的配置:靈活定義日誌規則。
- 多目標輸出:支持文件、數據庫、網絡等。
- 異步日誌寫入:提高性能。
- 與 .NET 全家桶兼容。
1.3 Serilog 集成示例(ASP.NET Core)
步驟1:安裝 NuGet 包
bash
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File
步驟2:配置 Program.cs
csharp
using Serilog;
var builder = WebApplication.CreateBuilder(args);
// 配置 Serilog(最小配置)
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information() // 日誌級別
.WriteTo.Console() // 輸出到控制枱
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day) // 輸出到文件
.CreateLogger();
builder.Host.UseSerilog(); // 替換默認日誌
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
步驟3:結構化日誌示例
csharp
app.MapGet("/error", () =>
{
Log.Information("Processing request {@Request}", new { Path = "/error", Time = DateTime.UtcNow });
Log.Error("Something went wrong! {@Error}", new { Code = 500, Message = "Internal Server Error" });
return Results.Problem("Error occurred", statusCode: 500);
});
輸出(JSON 格式):
json
{
"Timestamp": "2023-10-01T12:00:00Z",
"Level": "Information",
"Message": "Processing request {\"Path\":\"/error\",\"Time\":\"2023-10-01T12:00:00Z\"}",
"Properties": {
"Request": { "Path": "/error", "Time": "2023-10-01T12:00:00Z" }
}
}
1.4 NLog 集成示例
步驟1:安裝 NuGet 包
bash
dotnet add package NLog.Extensions.Logging
步驟2:創建 NLog.config
xml
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="console" xsi:type="Console" />
<target name="file" xsi:type="File" fileName="logs/${shortdate}.log" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="console,file" />
</rules>
</nlog>
步驟3:配置 Program.cs
csharp
var builder = WebApplication.CreateBuilder(args);
// 加載 NLog 配置
builder.Logging.ClearProviders();
builder.Logging.AddNLog(); // 自動加載 NLog.config
var app = builder.Build();
app.MapGet("/", () => "Hello NLog!");
app.Run();
2. Application Insights 配置:雲端監控
2.1 Application Insights 核心功能
- 自動收集:請求、依賴項、異常、指標等。
- 實時監控:儀表盤、警報、性能分析。
- 分佈式追蹤:跨服務調用鏈追蹤。
- 與 Azure 深度集成。
2.2 配置步驟(ASP.NET Core)
步驟1:安裝 NuGet 包
bash
dotnet add package Microsoft.ApplicationInsights.AspNetCore
步驟2:配置 Program.cs
csharp
var builder = WebApplication.CreateBuilder(args);
// 添加 Application Insights(通過連接字符串或密鑰)
builder.Services.AddApplicationInsightsTelemetry(options =>
{
options.ConnectionString = "InstrumentationKey=YOUR_KEY;IngestionEndpoint=https://YOUR_REGION.in.applicationinsights.azure.com/";
});
var app = builder.Build();
app.MapGet("/", () => "Hello Application Insights!");
app.Run();
步驟3:自定義日誌(結合 Serilog)
csharp
// 安裝 Serilog.Sinks.ApplicationInsights
dotnet add package Serilog.Sinks.ApplicationInsights
// 配置 Serilog 輸出到 Application Insights
Log.Logger = new LoggerConfiguration()
.WriteTo.ApplicationInsights(
TelemetryConverter.Traces,
instrumentationKey: "YOUR_INSTRUMENTATION_KEY")
.CreateLogger();
builder.Host.UseSerilog();
2.3 關鍵監控場景
1. 跟蹤自定義事件
csharp
app.MapGet("/track", () =>
{
var telemetry = app.Services.GetRequiredService<TelemetryClient>();
telemetry.TrackEvent("CustomEvent", new Dictionary<string, string>
{
{ "UserId", "123" },
{ "Action", "Click" }
});
return "Event tracked!";
});
2. 記錄異常
csharp
app.MapGet("/error", () =>
{
var telemetry = app.Services.GetRequiredService<TelemetryClient>();
try
{
throw new Exception("Test exception");
}
catch (Exception ex)
{
telemetry.TrackException(ex);
return Results.Problem("Error occurred", statusCode: 500);
}
});
3. 性能指標
csharp
// 自動收集依賴項(如 HTTP 調用、數據庫查詢)
// 手動記錄指標
var metrics = app.Services.GetRequiredService<IMetricsCollector>();
metrics.TrackMetric("ProcessingTime", 120); // 毫秒
3. 結構化日誌實踐:提升日誌價值
3.1 為什麼需要結構化日誌?
- 機器可讀:JSON 格式便於日誌分析工具(如 ELK、Grafana Loki)解析。
- 上下文豐富:通過字段關聯請求、用户、設備等信息。
- 查詢高效:按字段過濾(如
SELECT * FROM logs WHERE Level = "Error")。
3.2 最佳實踐
1. 使用語義化字段名
- 避免模糊字段(如
data、info),使用明確名稱(如UserId、OrderId)。 - 示例:
json
{
"Timestamp": "2023-10-01T12:00:00Z",
"Level": "Error",
"Message": "Database connection failed",
"Properties": {
"ConnectionString": "Server=db;Database=prod",
"ErrorCode": "TIMEOUT"
}
}
2. 添加請求上下文
- 通過中間件為每個請求添加唯一 ID(如
X-Request-ID)。 - 示例(Serilog):
csharp
app.Use(async (context, next) =>
{
var requestId = context.TraceIdentifier;
using (LogContext.PushProperty("RequestId", requestId))
{
await next();
}
});
3. 避免敏感信息
- 不要記錄密碼、令牌等敏感數據。
- 使用脱敏處理:
csharp
Log.Information("User logged in {@User}", new { Username = user.Name, Email = "***@***.com" });
4. 日誌級別指南
|
級別 |
使用場景 |
|
|
調試細節(如 SQL 參數) |
|
|
開發階段排查問題 |
|
|
關鍵業務流程(如訂單創建) |
|
|
非預期行為(如緩存未命中) |
|
|
業務異常(如驗證失敗) |
|
|
系統崩潰(如數據庫不可用) |
3.3 結構化日誌查詢示例
1. Kibana(ELK)查詢
json
{
"query": {
"bool": {
"must": [
{ "term": { "Level": "Error" } },
{ "range": { "Timestamp": { "gte": "now-1d" } } }
]
}
}
}
2. Grafana Loki 查詢
logql
{Level="Error"} |= "Database" | json | UserId="123"
4. 完整示例:ASP.NET Core + Serilog + Application Insights
4.1 配置 Program.cs
csharp
using Serilog;
using Serilog.Sinks.ApplicationInsights;
var builder = WebApplication.CreateBuilder(args);
// Serilog 配置
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.WithProperty("Environment", "Production")
.WriteTo.Console()
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
.WriteTo.ApplicationInsights(
TelemetryConverter.Traces,
instrumentationKey: builder.Configuration["ApplicationInsights:InstrumentationKey"])
.CreateLogger();
builder.Host.UseSerilog();
// Application Insights
builder.Services.AddApplicationInsightsTelemetry();
var app = builder.Build();
// 請求 ID 中間件
app.Use(async (context, next) =>
{
using (LogContext.PushProperty("RequestId", context.TraceIdentifier))
{
await next();
}
});
app.MapGet("/", () => "Hello Observable App!");
app.MapGet("/error", () =>
{
Log.Error("Simulated error {@Error}", new { Code = 500, Message = "Service unavailable" });
return Results.Problem("Error", statusCode: 500);
});
app.Run();
4.2 日誌輸出示例
json
{
"Timestamp": "2023-10-01T12:00:00Z",
"Level": "Error",
"Message": "Simulated error {\"Error\":{\"Code\":500,\"Message\":\"Service unavailable\"}}",
"Properties": {
"RequestId": "0HLVPQ9J12345",
"Environment": "Production"
}
}
5. 總結與選型建議
|
場景 |
推薦方案 |
|
本地開發/調試 |
Serilog + 控制枱 Sink |
|
生產環境日誌收集 |
Serilog + 文件 Sink + ELK/Loki |
|
雲端監控與告警 |
Application Insights |
|
需要靈活配置規則 |
NLog |
|
結構化日誌分析 |
Serilog + JSON 輸出 + Kibana/Grafana |
關鍵建議:
- 統一日誌格式:生產環境使用 JSON 結構化日誌。
- 上下文關聯:通過請求 ID 串聯日誌。
- 分級存儲:熱數據(近期日誌)存高速存儲,冷數據(歷史日誌)存廉價存儲。
- 警報配置:對
Error/Fatal級別日誌設置實時告警。
通過集成 Serilog/NLog 和 Application Insights,結合結構化日誌實踐,可以構建高可觀測性的應用,顯著提升故障排查效率和系統穩定性。