关闭

容智睿软件科技

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

2025-09-03 浏览:

在 .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


浏览 小编 主页 关注 投稿量: 粉丝量: 关注量:
标签:
加载中~

推荐文章