.NET 微服务日志系统:Serilog + Loki + Grafana 实践指南

.NET 微服务日志系统:Serilog + Loki + Grafana 实践指南

在 .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 微服务集群中,日志的完整流转路径如下:

  1. 日志产生:.NET 微服务内部通过 Serilog 记录日志(如接口请求、异常、业务事件);
  2. 日志结构化 :Serilog 将日志格式化为 JSON 结构,包含 ServiceName(服务名)、InstanceId(实例ID)、TraceId(调用链ID)、Level(日志级别)等核心标签;
  3. 日志发送:通过 Serilog.Sinks.Loki 插件,将结构化日志推送到 Loki 服务;
  4. 日志存储 :Loki 对日志按标签建立索引(如 service=order-service),并存储原始日志数据;
  5. 日志查询与可视化: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 文件
yaml 复制代码
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 所在目录执行命令:

bash 复制代码
# 启动服务(后台运行)
docker-compose up -d

# 验证服务是否正常启动
docker-compose ps
  • Loki 验证:访问 http://localhost:3100/ready,返回 ready 表示正常;
  • Grafana 验证:访问 http://localhost:3000,使用账号 admin、密码 admin 登录,首次登录需修改密码(可选)。

3. 第二步:配置 Grafana 连接 Loki 数据源

Grafana 启动后,需先配置 Loki 数据源,才能查询日志:

  1. 登录 Grafana 后,点击左侧菜单 Configuration > Data sources
  2. 点击 Add data source ,搜索 Loki 并选择;
  3. Settings 页配置:
    • URLhttp://loki:3100(Docker 内部网络,直接用服务名访问;若外部访问,改为 http://localhost:3100);
    • 其他配置默认,点击 Save & test,显示 "Data source is working" 即配置成功。

4. 第三步:.NET 微服务集成 Serilog 并对接 Loki

步骤1:安装 Serilog 相关 NuGet 包

在 .NET 微服务项目中,通过 NuGet 安装以下包:

bash 复制代码
# 核心日志库
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 输出:

csharp 复制代码
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 中添加日志相关配置,便于环境切换:

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:测试日志输出

在控制器中添加接口,手动记录日志:

csharp 复制代码
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 查询日志
  1. 登录 Grafana,点击左侧菜单 Explore,选择 Loki 数据源;
  2. 在查询框中输入 LogQL(Loki 专用查询语法),示例:
    • 查询所有 order-service 的日志:{service="order-service"}
    • 查询 order-service 的错误日志:{service="order-service"} |= "Error"
    • 查询特定订单ID的日志:{service="order-service"} |= "OrderId=12345"
  3. 点击 Run query,下方将展示匹配的日志,支持按时间范围筛选(右上角选择时间,如"Last 5 minutes")。
步骤2:创建自定义仪表盘(Dashboard)

为了更直观地监控微服务日志(如日志级别分布、请求量趋势),可创建 Grafana 仪表盘:

  1. 点击左侧菜单 Dashboards > New dashboard
  2. 点击 Add visualization,选择 Loki 数据源;
  3. 配置图表类型:
    • 日志列表:展示原始日志,适合实时查看;
    • 柱状图:统计不同日志级别的数量(如 Error/Warning/Info 的占比);
    • 表格:按字段(如 OrderId、UserId)汇总日志,便于排查特定业务问题;
  4. 保存仪表盘,命名为"Order-Service 日志监控",后续可直接访问。

四、生产环境优化建议

  1. Loki 配置优化

    • 生产环境需自定义 loki-config.yaml,配置分布式存储(如 S3、MinIO)替代本地存储,避免单点故障;
    • 开启日志压缩(如 Snappy),减少存储占用;
    • 配置日志保留期(如 retention_period: 720h,保留30天),避免磁盘溢出。
  2. Serilog 优化

    • 避免日志冗余:过滤重复日志(如健康检查接口的 Information 日志);
    • 敏感信息脱敏:使用 Serilog.Enrichers.Sensitive 插件,对密码、手机号等字段脱敏;
    • 批量发送日志:配置 Serilog.Sinks.Loki 的批量发送参数(如 batchPostingLimit: 1000),减少网络请求。
  3. Grafana 优化

    • 配置日志告警:当错误日志数量超过阈值(如5分钟内10条 Error),通过邮件、Slack 通知;
    • 权限控制:为不同角色(如开发、运维)分配 Grafana 只读/编辑权限,避免误操作;
    • 联动监控指标:将 Loki 日志与 Prometheus 指标(如接口响应时间、错误率)放在同一仪表盘,实现"日志+指标"联动分析。
  4. 微服务日志规范

    • 统一标签:所有微服务必须包含 serviceenvironmentinstance 标签,便于跨服务日志聚合;
    • 日志级别规范:Error(业务异常)、Warning(非致命问题)、Information(正常业务事件)、Debug(开发调试,生产禁用);
    • 必含字段:TraceID(调用链追踪)、BusinessId(业务ID,如订单ID、用户ID),便于定位全链路问题。

五、常见问题排查

  1. 日志未推送到 Loki

    • 检查 Loki 地址是否正确(.NET 服务能否访问 Loki,Docker 网络是否互通);
    • 查看 .NET 服务日志:是否有 Serilog.Sinks.Loki 相关错误(如网络超时、404 错误);
    • 验证 Loki 接收日志:访问 http://localhost:3100/loki/api/v1/query?query={service="order-service"},查看是否返回日志。
  2. Grafana 查不到 Loki 日志

    • 检查数据源配置:URL 是否正确,是否能连通 Loki;
    • 检查 LogQL 语法:标签是否匹配(如 service 标签值是否为 order-service);
    • 检查时间范围:日志是否在 Grafana 选择的时间范围内(如日志是10分钟前的,需选择"Last 15 minutes")。
  3. 日志字段缺失(如 TraceID)

    • 确保 app.UseSerilogRequestLogging() 中间件已添加(自动捕获 HTTP 请求的 TraceID);
    • 若使用分布式追踪(如 OpenTelemetry),需配置 Serilog.Enrich.FromOpenTelemetry() 插件,从追踪上下文获取 TraceID。

通过以上步骤,即可搭建一套适配 .NET 微服务的高可用日志系统,实现日志的集中采集、高效查询和可视化监控,为微服务运维和问题排查提供有力支撑。

相关推荐
界面开发小八哥6 小时前
DevExpress WinForms中文教程:Data Grid - 过滤编辑器
人工智能·ui·.net·devexpress·用户界面·winforms
蒋星熠9 小时前
Spring Boot 3.x 微服务架构实战指南
人工智能·spring boot·微服务·性能优化·架构·云计算·量子计算
3Cloudream13 小时前
互联网大厂Java面试:从基础到微服务的深度解析
java·spring·微服务·面试·技术解析·电商场景
EmiAlyx18 小时前
微服务01
微服务·云原生·架构
三贝18 小时前
Java面试现场:Spring Boot+Redis+MySQL在电商场景下的技术深度剖析
spring boot·redis·mysql·微服务·分布式事务·java面试·电商系统
❀͜͡傀儡师20 小时前
docker安装Prometheus和Grafana 监控界面
docker·grafana·prometheus
回家路上绕了弯21 小时前
微服务网关选型指南:从需求匹配到落地决策,选对网关少走弯路
后端·微服务
追逐时光者1 天前
C#/.NET/.NET Core技术前沿周刊 | 第 52 期(2025年8.25-8.31)
后端·.net
好运连连9991 天前
ubuntu promethus+grafana监控多台服务器
linux·ubuntu·grafana