📝 ABP VNext + Grafana Loki:集中式日志聚合
📚 目錄
- 📝 ABP VNext + Grafana Loki:集中式日志聚合
- 一、引言
- ? TL;DR
- 二、環境與依賴
- 🛠? 平臺版本
- 🔗 NuGet 包
- ?? 基礎服務
- 三、Serilog + Loki 集成
- 3.1 安裝與配置
- 3.2 添加標簽
- 四、Loki 數據源與推送格式
- 錯誤處理與重試機制
- 五、Grafana 配置
- 5.1 新建 Loki Data Source
- 5.2 日志面板(Explore)
- 六、儀表盤與告警
- 6.1 自定義儀表盤
- 6.2 告警規則
- 告警通知配置
- 七、多服務與多環境
- 📦 統一標簽策略
- 🔄 Grafana Dashboard 復用
- Grafana Provisioning
- 八、性能與可靠性最佳實踐
- ?? 批量/緩沖
- 💾 Durable Sink 保障可靠性
- 📊 Loki 索引策略
- ? Loki 高可用部署
- 九、端到端示例
- 日志流轉流程
一、引言
? TL;DR
- 用 Serilog 將 ABP 應用日志推送到 Grafana Loki,實現結構化、標簽化存儲,便于查詢和追蹤。
- 在 Grafana 中配置日志面板、查詢模板與告警規則,支持多服務多環境的統一日志聚合。
- 實現高可用、可擴展的日志管理體系,輕松定位問題并提高運維效率。
📚 背景與動機
在 ABP 微服務架構下,日志分散在多個服務實例中,排查問題時需要登錄多個實例查看日志,運維成本高且不便實時監控。Grafana Loki 通過僅索引日志的標簽,結合 Grafana 原生支持,能夠實現零運維的集中式日志解決方案,極大提升查詢性能與可用性。本教程展示了如何在 ABP VNext 應用中,結合 Serilog 和 Grafana Loki 構建高效、可復現的日志管理系統。
二、環境與依賴
🛠? 平臺版本
- .NET 6 + ABP VNext 6.x
- Grafana ≥ 9.x、Loki ≥ 2.x
🔗 NuGet 包
Serilog.AspNetCore
Serilog.Sinks.Grafana.Loki
(支持/loki/api/v1/push
接口)Serilog.Formatting.Compact
(結構化 JSON 格式)
?? 基礎服務
- Loki ? HTTP 接收器(
<loki_host>:3100
) - Grafana ? 配置 Data Source 指向 Loki
三、Serilog + Loki 集成
3.1 安裝與配置
首先在項目中安裝必需的 NuGet 包:
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Grafana.Loki
dotnet add package Serilog.Formatting.Compact
在 Program.cs
中進行 Serilog 配置:
using Serilog;
using Serilog.Formatting.Compact;Log.Logger = new LoggerConfiguration().Enrich.FromLogContext().Enrich.WithProperty("Application", "OrderService").Enrich.WithProperty("Environment", builder.Environment.EnvironmentName).Enrich.WithProperty("TraceId", Activity.Current?.TraceId.ToString() ?? "unknown").Enrich.WithProperty("TenantId", "Tenant123").WriteTo.Console(new RenderedCompactJsonFormatter()).WriteTo.GrafanaLoki("http://loki:3100", labels: new Dictionary<string, string>{{ "Application", "OrderService" },{ "Environment", builder.Environment.EnvironmentName }}).CreateLogger();builder.Host.UseSerilog();
3.2 添加標簽
為了進一步增強日志的可查詢性,可以在日志中加入更多的標簽,如 TraceId
和 TenantId
,使得日志查詢更加精確。
Log.Logger = new LoggerConfiguration().Enrich.FromLogContext().Enrich.WithProperty("Application", "OrderService").Enrich.WithProperty("Environment", builder.Environment.EnvironmentName).Enrich.WithProperty("TraceId", Activity.Current?.TraceId.ToString() ?? "unknown").Enrich.WithProperty("TenantId", "Tenant123").WriteTo.Console(new RenderedCompactJsonFormatter()).WriteTo.GrafanaLoki("http://loki:3100", labels: new Dictionary<string, string>{{ "Application", "OrderService" },{ "Environment", builder.Environment.EnvironmentName }}).CreateLogger();
四、Loki 數據源與推送格式
Loki Push API 的日志數據格式要求如下:
{"streams": [{"stream": { "label1": "value1", "label2": "value2" },"values": [[ "1621373445000000000", "log line 1" ],[ "1621373446000000000", "log line 2" ]]}]
}
Serilog.Sinks.Grafana.Loki
會自動批量打包日志,并推送到 Loki。每條日志會自動包裝成這種 JSON 格式,因此你不需要自己處理日志格式。
錯誤處理與重試機制
為了確保日志的可靠性,當 Loki 推送失敗時,我們可以使用 Serilog Sink 的失敗重試機制,或者將失敗日志臨時保存在本地文件中進行重試。以下是配置重試機制的示例:
.WriteTo.GrafanaLoki("http://loki:3100", labels: new Dictionary<string, string> {{"Application", "OrderService"}})
.WithAutomaticRetries(maxRetries: 5, delayBetweenRetries: TimeSpan.FromSeconds(5))
五、Grafana 配置
5.1 新建 Loki Data Source
在 Grafana 中創建 Loki 數據源:
- 在 Grafana 控制臺選擇 Data Sources,點擊 Add Data Source。
- 選擇 Loki,輸入 Loki 的地址
http://<loki_host>:3100
,然后點擊 Save & Test 驗證連接是否成功。
5.2 日志面板(Explore)
示例查詢:
{Application="OrderService", Environment="Production"}|= "Error"|= "Exception"
使用模板變量 $app
和 $env
來動態切換服務和環境:
{Application="$app", Environment="$env"} |= "Error"
六、儀表盤與告警
6.1 自定義儀表盤
每分鐘日志量:
count_over_time({Application="$app"}[1m])
錯誤級別趨勢:
rate({level="Error",Application="$app"}[5m])
請求延遲分布(假設記錄了 Duration
字段):
histogram_quantile(0.95, sum(rate({Application="$app"} |= "Duration" [$__interval])) by (le))
推薦的布局包括概覽、異常詳情、容量監控。
6.2 告警規則
示例告警規則:
- Rule 1:5 分鐘內 Error 日志 ≥ 50 ? 發送通知。
- Rule 2:日志包含關鍵異常關鍵字(如
NullReferenceException
)? 立即告警。
通知渠道:Email、Slack、Teams。
告警通知配置
Grafana 支持將告警通知集成到 Slack、Teams、Email 等渠道。在 Grafana 的 Alerting 設置中,可以創建通知渠道,并將其配置到特定告警規則中。
七、多服務與多環境
📦 統一標簽策略
為了在不同環境和服務之間聚合日志,推薦使用統一的標簽策略:Application
、Environment
、Tenant
等。這樣可以幫助開發者在查詢日志時使用一致的命名方式。
🔄 Grafana Dashboard 復用
通過模板變量,如 $app
、$env
,實現多實例監控,避免重復配置。你可以在一個 Grafana 儀表盤中查看多個服務和環境的數據,只需動態切換模板變量即可。
Grafana Provisioning
為了自動化管理多個 Grafana 實例的配置,可以使用 Provisioning 功能,將儀表盤和數據源配置文件化,從而實現批量部署和配置同步。
八、性能與可靠性最佳實踐
?? 批量/緩沖
調整 batchPostingLimit
與 period
配置,以優化推送頻率和批量大小。不同的日志 Sink 可能有不同的配置選項,請參考官方文檔調整。
💾 Durable Sink 保障可靠性
為了確保日志的可靠性,建議配置帶本地文件緩沖的 Durable Sink,例如:
.WriteTo.DurableHttpUsingFileSizeRolledBuffers("http://loki:3100", fileSizeLimitBytes: 50_000_000)
📊 Loki 索引策略
- 避免標簽的高基數(Cardinality),如每個日志條目的
UserId
或SessionId
。 - 只使用必要的標簽,避免過多的字段導致存儲和查詢性能下降。
? Loki 高可用部署
對于生產環境,建議將 Loki 部署為分布式系統,使用 Distributor
、Ingester
、Querier
等組件來提升可用性和水平擴展能力。可以使用 Docker Compose 或 Kubernetes 配置 Loki 集群。
九、端到端示例
-
啟動 Loki & Grafana Docker 容器
使用以下docker-compose.yml
啟動 Loki 和 Grafana:version: '3.7' services:loki:image: grafana/loki:2.8.2command: -config.file=/etc/loki/local-config.yamlports:- "3100:3100"grafana:image: grafana/grafana:9.5.0ports:- "3000:3000"
-
配置 ABP 項目 Serilog
在 ABP 項目的Program.cs
中按照 3.1 配置 Serilog。 -
生成多級別日志
在代碼中添加不同級別的日志:Log.Information()
,Log.Warning()
,Log.Error()
。 -
在 Grafana Explore 中執行查詢
使用示例查詢來可視化日志。 -
創建告警規則
設置一個告警規則,并模擬觸發 Error 日志來測試告警。