可觀測性是現代應用的核心能力,通過日誌(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. 使用語義化字段名

  • 避免模糊字段(如 datainfo),使用明確名稱(如 UserIdOrderId)。
  • 示例:
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. 日誌級別指南

級別

使用場景

Verbose

調試細節(如 SQL 參數)

Debug

開發階段排查問題

Information

關鍵業務流程(如訂單創建)

Warning

非預期行為(如緩存未命中)

Error

業務異常(如驗證失敗)

Fatal

系統崩潰(如數據庫不可用)

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

關鍵建議

  1. 統一日誌格式:生產環境使用 JSON 結構化日誌。
  2. 上下文關聯:通過請求 ID 串聯日誌。
  3. 分級存儲:熱數據(近期日誌)存高速存儲,冷數據(歷史日誌)存廉價存儲。
  4. 警報配置:對 Error/Fatal 級別日誌設置實時告警。

通過集成 Serilog/NLog 和 Application Insights,結合結構化日誌實踐,可以構建高可觀測性的應用,顯著提升故障排查效率和系統穩定性。