ABP VNext + Grafana Loki:集中式日志聚合
📝 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 日志来测试告警。