当前位置: 首页 > news >正文

C# 日志写入loki

在 C# 中实现日志写入 Loki 最常用的方式是结合 Serilog 日志框架和 Serilog.Sinks.Grafana.Loki 扩展包。这种方式支持结构化日志、自定义标签和灵活的配置,以下是完整的实现步骤:

一、准备工作

  1. 安装 NuGet 包
    在项目中安装必要的依赖包(通过 NuGet 包管理器或命令行):

    # Serilog 核心包
    Install-Package Serilog -Version 3.1.1
    Install-Package Serilog.AspNetCore -Version 8.0.0  # 集成 ASP.NET Core# Loki 接收器(用于将日志发送到 Loki)
    Install-Package Serilog.Sinks.Grafana.Loki -Version 8.0.0
    
  2. 确保 Loki 服务可用
    确认 Loki 已启动并可访问(默认地址:http://localhost:3100),可通过访问 http://localhost:3100/ready 验证,返回 ready 即表示正常运行。

二、配置 Serilog 连接 Loki

Program.cs 中配置 Serilog,设置 Loki 服务地址、日志标签、租户信息(可选)和日志格式:

using Serilog;
using Serilog.Sinks.Grafana.Loki;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;var builder = WebApplication.CreateBuilder(args);// 1. 从配置文件读取 Loki 地址和租户 ID(建议通过 appsettings.json 配置)
var lokiUri = builder.Configuration["Loki:Uri"] ?? "http://localhost:3100";
var tenantId = builder.Configuration["Loki:TenantId"] ?? "default-tenant"; // 多租户标识// 2. 配置 Serilog 日志系统
List<LokiLabel> labels = new List<LokiLabel>();
labels.Add(new LokiLabel { Key = "App", Value = "testproject" });// 1.获取应用程序名称和版本(用于 Elasticsearch 索引命名)
var appName = Assembly.GetExecutingAssembly().GetName().Name;
var appVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "1.0.0";Log.Logger = new LoggerConfiguration().Enrich.FromLogContext()  // 从日志上下文获取属性(如追踪 ID).Enrich.WithProperty("service", "ServiceB")  // 全局标签:服务名.Enrich.WithProperty("environment", "development") // 全局标签:环境.WriteTo.GrafanaLoki("http://localhost:3100", labels).WriteTo.Console()// 设置最小日志级别(Information 及以上).MinimumLevel.Information()// 针对特定命名空间调整日志级别(可选).MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Warning).CreateLogger();
Log.Information("Hello, Grafana Loki!");
// 替换默认日志工厂为 Serilog
builder.Host.UseSerilog();// 3. 替换 ASP.NET Core 默认日志系统为 Serilog
builder.Host.UseSerilog();// 4. 注册服务和中间件
builder.Services.AddControllers();
var app = builder.Build();// 5. 可选:添加分布式追踪 ID 到日志(便于链路追踪)
app.Use(async (context, next) =>
{var activity = System.Diagnostics.Activity.Current;if (activity != null){// 将 TraceId 和 SpanId 注入日志上下文using (LogContext.PushProperty("trace_id", activity.TraceId.ToString()))using (LogContext.PushProperty("span_id", activity.SpanId.ToString())){await next();}}else{await next();}
});app.MapControllers();
app.Run();// 自定义 HTTP 处理器:添加 Loki 租户头(多租户场景)
public class LokiTenantHandler : DelegatingHandler
{private readonly string _tenantId;public LokiTenantHandler(string tenantId){_tenantId = tenantId;InnerHandler = new HttpClientHandler(); // 基础 HTTP 处理器}protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){// 添加 Loki 租户标识头(多租户必需)request.Headers.Add("X-Scope-OrgID", _tenantId);return base.SendAsync(request, cancellationToken);}
}

三、在业务代码中记录日志

通过 ILogger<T> 接口记录日志,日志会自动序列化为 JSON 并发送到 Loki:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;namespace LokiLoggingDemo.Controllers;[ApiController]
[Route("[controller]")]
public class OrderController : ControllerBase
{private readonly ILogger<OrderController> _logger;// 构造函数注入日志接口public OrderController(ILogger<OrderController> logger){_logger = logger;}[HttpPost]public IActionResult CreateOrder([FromBody] OrderRequest request){// 1. 记录信息日志(包含结构化参数)_logger.LogInformation("用户 {UserId} 发起订单创建请求,商品 ID:{ProductId},数量:{Quantity}",request.UserId, request.ProductId, request.Quantity);try{if (request.Quantity <= 0){throw new ArgumentException("商品数量必须大于 0");}// 模拟订单创建逻辑var orderId = Guid.NewGuid().ToString();// 2. 记录成功日志(包含复杂对象)var orderResult = new { OrderId = orderId, Status = "Created", TotalAmount = request.Quantity * 99.99m };_logger.LogInformation("订单创建成功:{OrderResult}", orderResult);return Ok(new { OrderId = orderId });}catch (Exception ex){// 3. 记录错误日志(包含异常堆栈)_logger.LogError(ex, "用户 {UserId} 订单创建失败,商品 ID:{ProductId}",request.UserId, request.ProductId);return BadRequest(ex.Message);}}
}// 订单请求模型
public class OrderRequest
{public string UserId { get; set; }public string ProductId { get; set; }public int Quantity { get; set; }
}

四、验证日志是否写入 Loki

  1. 运行应用程序
    调用 OrderController.CreateOrder 接口(可通过 Postman 或 Swagger 发送请求),生成测试日志。

  2. 在 Grafana 中查询日志 在这里插入图片描述

    1. 打开 Grafana(默认地址:http://localhost:3000),添加 Loki 数据源(地址填写 Loki 的 HTTP 地址,如 http://localhost:3100)。
    2. 进入 Explore 页面,选择 Loki 数据源,使用标签筛选日志:
      # 筛选 order-service 的日志
      {service="order-service", environment="Development"}
      
    3. 若日志为 JSON 格式,可通过 | json 解析字段并筛选:
      # 筛选用户 ID 为 123 的错误日志
      {service="order-service"} | json | UserId="123" and Level="Error"
      

![(https://i-blog.csdnimg.cn/direct/7aced4b706fa458784f296f279635a87.png)

五、关键配置说明

  1. Loki 地址与租户

    • uri:Loki 的 HTTP 接口地址(默认 http://localhost:3100),若 Loki 部署在远程服务器,需替换为实际 IP 或域名。
    • 多租户场景:通过 X-Scope-OrgID 头指定 tenantId,实现不同租户日志隔离。
  2. 日志标签(labels)
    标签是 Loki 日志筛选的核心,建议包含 service(服务名)、environment(环境)等固定标识,便于后续按服务、环境查询日志。

  3. JSON 格式化
    使用 JsonFormatter 输出 JSON 格式日志,Loki 可直接解析其中的字段(如 UserIdOrderId),支持复杂查询(如按用户 ID 筛选)。

  4. 批量发送
    通过 batchPostingLimitperiod 控制日志批量发送策略,减少网络请求次数,优化性能。

六、常见问题解决

  1. 日志未发送到 Loki

    • 检查 Loki 地址是否正确,确保 http://localhost:3100/ready 可访问。
    • 查看应用程序控制台输出,是否有 Failed to send log batch to Loki 错误(通常是网络不通或 Loki 未启动)。
    • 确认防火墙未拦截 3100 端口。
  2. 多租户日志隔离问题

    • 若租户日志混淆,检查 X-Scope-OrgID 头是否正确添加(可通过抓包工具验证请求头)。
    • 在 Loki 配置中设置 allow_empty_org_id: false,强制客户端必须指定租户 ID。
  3. 日志字段解析失败

    • 若 JSON 日志字段未被 Loki 解析,确保 textFormatter 使用 JsonFormatter,且日志格式为标准 JSON。

通过以上配置,C# 应用程序的日志可无缝发送到 Loki,结合 Grafana 可实现日志的集中管理、查询和可视化,非常适合分布式系统的日志监控。

http://www.dtcms.com/a/357289.html

相关文章:

  • 海外广告流量套利:为什么需要使用移动代理IP?
  • 接吻数问题:从球体堆叠到高维空间的数学奥秘
  • 告别K8s部署繁琐!用KubeOperator可视化一键搭建生产级集群
  • 玄机靶场 | 冰蝎3.0-jsp流量分析
  • ACID分别如何实现
  • Dockerfile实现java容器构建及项目重启(公网和内网)
  • SOME/IP-SD IPv4组播的通信参数由谁指定?
  • React学习教程,从入门到精通, ReactJS - 特性:初学者的指南(4)
  • C++链表双杰:list与forward_list
  • ElasticSearch对比Solr
  • Node.js 的流(Stream)是什么?有哪些类型?
  • DQL单表查询相关函数
  • STM32F2/F4系列单片机解密和芯片应用介绍
  • Ubuntu虚拟机磁盘空间扩展指南
  • AI视频安防,为幼儿园安全保驾护航
  • 基于 GPT-OSS 的成人自考口语评测 API 开发全记录
  • 深度解密SWAT模型:遥感快速建模、DEM/LU/气象数据不确定性、子流域/坡度划分、未来土地利用与气候变化情景模拟及措施效益评估
  • 龙巍:探究青铜器在木雕中的运用
  • VS Code C#调试完全指南
  • [AI人脸替换] docs | 环境部署指南 | 用户界面解析
  • 红色视频剪辑制作——走进广州农讲所:在红墙黄瓦间感悟初心与传承
  • “游戏手柄”线性霍尔传感器IC替代方案:赛卓SC470X
  • Instance Normalization(实例归一化)
  • Stage应用模型及状态存储
  • 【Android 16】Android W 的冻结机制内核分析
  • 车载以太网通信测试:牢筑车载网络的质量防线
  • 【51单片机】【protues仿真】 基于51单片机叫号系统
  • 基于EB的K3XX_GPT定时器中断的实现方法
  • 精通与AI对话的艺术:如何通过角色扮演获得精准输出
  • 【Rust】 6. 字符串学习笔记