在 .NET 微服务架构中,日志是问题排查、系统监控和业务分析的核心支撑。传统日志存储(如本地文件)存在分散、查询效率低、可视化弱等问题,而 Serilog(日志采集)+ Loki(日志存储与索引)+ Grafana(日志可视化与分析) 组合,能实现“采集-存储-分析-可视化”全链路日志管理,尤其适配微服务分布式场景。
一、技术栈核心角色与优势
先明确三者的分工的协同逻辑,理解为何这套组合适合 .NET 微服务:
组件 核心角色 适配 .NET 微服务的优势
Serilog 日志采集器(Logger) 1. 原生支持 .NET 全框架(.NET Framework/.NET Core/.NET 5+);
2. 结构化日志(JSON 格式),便于 Loki 解析;
3. 丰富的 Sink(输出目标),可直接对接 Loki;
4. 支持微服务核心字段(服务名、实例ID、TraceID)的自动注入。
Loki 日志存储与索引引擎(Log Store) 1. 轻量级设计,占用资源远低于 ELK(Elasticsearch);
2. “标签索引+原始日志”存储模式,查询效率高(只索引关键标签,不全文索引);
3. 原生支持 Prometheus 生态,可与微服务监控(如 Prometheus)联动;
4. 支持分布式场景下的日志聚合,按服务/实例/环境筛选。
Grafana 日志可视化与分析平台(UI) 1. 提供 Loki 专属数据源插件,查询语法直观(LogQL);
2. 支持日志与监控指标(如 Prometheus metrics)联动展示;
3. 可自定义仪表盘(Dashboard),适配微服务多维度日志分析;
4. 支持告警配置,日志异常时实时通知。
二、系统架构与数据流向
在 .NET 微服务集群中,日志的完整流转路径如下:
日志产生:.NET 微服务内部通过 Serilog 记录日志(如接口请求、异常、业务事件);
日志结构化:Serilog 将日志格式化为 JSON 结构,包含 ServiceName(服务名)、InstanceId(实例ID)、TraceId(调用链ID)、Level(日志级别)等核心标签;
日志发送:通过 Serilog.Sinks.Loki 插件,将结构化日志推送到 Loki 服务;
日志存储:Loki 对日志按标签建立索引(如 service=order-service),并存储原始日志数据;
日志查询与可视化:Grafana 连接 Loki 数据源,通过 LogQL 查询日志,并以仪表盘、表格、图表等形式展示,支持多维度筛选和异常告警。
三、分步实现:从环境搭建到微服务集成
1. 前置准备
环境要求:Docker(快速部署 Loki 和 Grafana,避免复杂的本地安装);.NET 6+ 微服务项目(示例以 .NET 8 为例);
工具:Docker Compose(一键启动 Loki 和 Grafana)、Visual Studio/VS Code(开发 .NET 项目)。
2. 第一步:用 Docker Compose 部署 Loki + Grafana
通过 Docker Compose 快速启动依赖服务,无需手动配置 Loki 存储、Grafana 插件。
步骤1:创建 docker-compose.yml 文件
version: "3.8"
services:
# Loki 服务(日志存储)
loki:
image: grafana/loki:2.9.2 # 选择稳定版本
ports:
- "3100:3100" # Loki 默认端口
volumes:
- ./loki/data:/loki # 持久化存储日志数据
command: -config.file=/etc/loki/local-config.yaml # 使用默认配置(适合测试,生产需自定义)
networks:
- log-network
# Grafana 服务(日志可视化)
grafana:
image: grafana/grafana:10.2.2
ports:
- "3000:3000" # Grafana 默认端口
volumes:
- ./grafana/data:/var/lib/grafana # 持久化 Grafana 配置(如数据源、仪表盘)
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin # 初始管理员密码(生产需修改)
- GF_INSTALL_PLUGINS=grafana-loki-datasource # 自动安装 Loki 数据源插件
depends_on:
- loki # 确保 Loki 启动后再启动 Grafana
networks:
- log-network
networks:
log-network:
driver: bridge
步骤2:启动服务
在 docker-compose.yml 所在目录执行命令:
# 启动服务(后台运行)
docker-compose up -d
# 验证服务是否正常启动
docker-compose ps
Loki 验证:访问 http://localhost:3100/ready,返回 ready 表示正常;
Grafana 验证:访问 http://localhost:3000,使用账号 admin、密码 admin 登录,首次登录需修改密码(可选)。
3. 第二步:配置 Grafana 连接 Loki 数据源
Grafana 启动后,需先配置 Loki 数据源,才能查询日志:
登录 Grafana 后,点击左侧菜单 Configuration > Data sources;
点击 Add data source,搜索 Loki 并选择;
在 Settings 页配置:
URL:http://loki:3100(Docker 内部网络,直接用服务名访问;若外部访问,改为 http://localhost:3100);
其他配置默认,点击 Save & test,显示 “Data source is working” 即配置成功。
4. 第三步:.NET 微服务集成 Serilog 并对接 Loki
步骤1:安装 Serilog 相关 NuGet 包
在 .NET 微服务项目中,通过 NuGet 安装以下包:
# 核心日志库
Install-Package Serilog
# .NET 主机集成(适配 ASP.NET Core)
Install-Package Serilog.AspNetCore
# Loki 输出插件(将日志推送到 Loki)
Install-Package Serilog.Sinks.Loki
# 结构化日志格式化(可选,推荐 JSON 格式)
Install-Package Serilog.Formatting.Compact
步骤2:配置 Serilog(Program.cs)
在 .NET 微服务的启动类中,替换默认日志系统为 Serilog,并配置 Loki 输出:
using Serilog;
using Serilog.Sinks.Loki;
var builder = WebApplication.CreateBuilder(args);
// 1. 移除默认日志系统,配置 Serilog
builder.Host.UseSerilog((context, services, configuration) =>
{
// 微服务核心配置(从 appsettings.json 读取,也可硬编码)
var serviceName = context.Configuration["ServiceSettings:ServiceName"] ?? "unknown-service";
var lokiUrl = context.Configuration["Logging:Loki:Url"] ?? "http://localhost:3100";
// 2. 配置 Loki 标签(核心:用于后续筛选日志)
var lokiLabels = new List<LokiLabel>
{
new LokiLabel("service", serviceName), // 服务名(如 order-service)
new LokiLabel("environment", context.HostingEnvironment.EnvironmentName), // 环境(Development/Production)
new LokiLabel("instance", Environment.MachineName) // 实例ID(区分同一服务的多个实例)
};
// 3. 配置 Serilog 规则
configuration
.ReadFrom.Configuration(context.Configuration) // 从 appsettings.json 读取日志级别等配置
.ReadFrom.Services(services) // 集成 ASP.NET Core 服务(如 IHttpContextAccessor)
.Enrich.FromLogContext() // 从日志上下文添加额外字段(如 TraceID)
.Enrich.WithMachineName() // 添加机器名
.WriteTo.Console() // 同时输出到控制台(开发环境调试用)
// 4. 输出到 Loki
.WriteTo.Loki(lokiUrl, lokiLabels,
// 配置日志格式(结构化 JSON,包含所有字段)
outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}");
});
// 其他服务注册(如控制器、Swagger 等)
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// 中间件配置(确保 Serilog 捕获 HTTP 请求日志)
app.UseSerilogRequestLogging(); // 自动记录 HTTP 请求日志(Method、Path、Status Code 等)
// 其他中间件(Swagger、路由等)
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
步骤3:配置 appsettings.json(日志级别、Loki 地址)
在 appsettings.json 中添加日志相关配置,便于环境切换:
{
"ServiceSettings": {
"ServiceName": "order-service" // 当前微服务名称(如订单服务)
},
"Logging": {
"LogLevel": {
"Default": "Information", // 默认日志级别
"Microsoft.AspNetCore": "Warning", // 过滤 ASP.NET Core 框架的冗余日志
"Microsoft.EntityFrameworkCore": "Warning" // 过滤 EF Core 冗余日志
},
"Loki": {
"Url": "http://localhost:3100" // Loki 服务地址(生产环境需改为集群地址)
}
},
"AllowedHosts": "*"
}
步骤4:测试日志输出
在控制器中添加接口,手动记录日志:
using Microsoft.AspNetCore.Mvc;
using Serilog;
namespace OrderService.Controllers;
[ApiController]
[Route("[controller]")]
public class OrderController : ControllerBase
{
[HttpPost]
public IActionResult CreateOrder([FromBody] string orderId)
{
try
{
// 1. 记录信息日志(业务事件)
Log.Information("订单创建请求:OrderId={OrderId}, UserId={UserId}",
orderId, HttpContext.User.Identity?.Name ?? "anonymous");
// 模拟业务逻辑
if (string.IsNullOrEmpty(orderId))
{
// 2. 记录警告日志
Log.Warning("订单ID为空,请求被拒绝");
return BadRequest("订单ID不能为空");
}
// 3. 模拟异常(记录错误日志)
if (orderId == "error-test")
{
throw new InvalidOperationException("模拟订单创建失败");
}
return Ok($"订单 {orderId} 创建成功");
}
catch (Exception ex)
{
// 4. 记录错误日志(包含异常堆栈)
Log.Error(ex, "订单创建失败:OrderId={OrderId}", orderId);
return StatusCode(500, "服务器内部错误");
}
}
}
5. 第四步:在 Grafana 中查询与可视化日志
步骤1:使用 LogQL 查询日志
登录 Grafana,点击左侧菜单 Explore,选择 Loki 数据源;
在查询框中输入 LogQL(Loki 专用查询语法),示例:
查询所有 order-service 的日志:{service="order-service"}
查询 order-service 的错误日志:{service="order-service"} |= "Error"
查询特定订单ID的日志:{service="order-service"} |= "OrderId=12345"
点击 Run query,下方将展示匹配的日志,支持按时间范围筛选(右上角选择时间,如“Last 5 minutes”)。
步骤2:创建自定义仪表盘(Dashboard)
为了更直观地监控微服务日志(如日志级别分布、请求量趋势),可创建 Grafana 仪表盘:
点击左侧菜单 Dashboards > New dashboard;
点击 Add visualization,选择 Loki 数据源;
配置图表类型:
日志列表:展示原始日志,适合实时查看;
柱状图:统计不同日志级别的数量(如 Error/Warning/Info 的占比);
表格:按字段(如 OrderId、UserId)汇总日志,便于排查特定业务问题;
保存仪表盘,命名为“Order-Service 日志监控”,后续可直接访问。
四、生产环境优化建议
Loki 配置优化:
生产环境需自定义 loki-config.yaml,配置分布式存储(如 S3、MinIO)替代本地存储,避免单点故障;
开启日志压缩(如 Snappy),减少存储占用;
配置日志保留期(如 retention_period: 720h,保留30天),避免磁盘溢出。
Serilog 优化:
避免日志冗余:过滤重复日志(如健康检查接口的 Information 日志);
敏感信息脱敏:使用 Serilog.Enrichers.Sensitive 插件,对密码、手机号等字段脱敏;
批量发送日志:配置 Serilog.Sinks.Loki 的批量发送参数(如 batchPostingLimit: 1000),减少网络请求。
Grafana 优化:
配置日志告警:当错误日志数量超过阈值(如5分钟内10条 Error),通过邮件、Slack 通知;
权限控制:为不同角色(如开发、运维)分配 Grafana 只读/编辑权限,避免误操作;
联动监控指标:将 Loki 日志与 Prometheus 指标(如接口响应时间、错误率)放在同一仪表盘,实现“日志+指标”联动分析。
微服务日志规范:
统一标签:所有微服务必须包含 service、environment、instance 标签,便于跨服务日志聚合;
日志级别规范:Error(业务异常)、Warning(非致命问题)、Information(正常业务事件)、Debug(开发调试,生产禁用);
必含字段:TraceID(调用链追踪)、BusinessId(业务ID,如订单ID、用户ID),便于定位全链路问题。
五、常见问题排查
日志未推送到 Loki:
检查 Loki 地址是否正确(.NET 服务能否访问 Loki,Docker 网络是否互通);
查看 .NET 服务日志:是否有 Serilog.Sinks.Loki 相关错误(如网络超时、404 错误);
验证 Loki 接收日志:访问 http://localhost:3100/loki/api/v1/query?query={service="order-service"},查看是否返回日志。
Grafana 查不到 Loki 日志:
检查数据源配置:URL 是否正确,是否能连通 Loki;
检查 LogQL 语法:标签是否匹配(如 service 标签值是否为 order-service);
检查时间范围:日志是否在 Grafana 选择的时间范围内(如日志是10分钟前的,需选择“Last 15 minutes”)。
日志字段缺失(如 TraceID):
确保 app.UseSerilogRequestLogging() 中间件已添加(自动捕获 HTTP 请求的 TraceID);
若使用分布式追踪(如 OpenTelemetry),需配置 Serilog.Enrich.FromOpenTelemetry() 插件,从追踪上下文获取 TraceID。
通过以上步骤,即可搭建一套适配 .NET 微服务的高可用日志系统,实现日志的集中采集、高效查询和可视化监控,为微服务运维和问题排查提供有力支撑。
————————————————
版权声明:本文为CSDN博主「东百牧码人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/temp0504/article/details/151060446