WindPayer.Logging 1.1.6

安装

dotnet add package WindPayer.Logging

快速开始

// Program.cs
using WindPayer.Logging;
builder.UseWindpayerLogging();

// 正常的logger调用
// 文本信息推荐使用占位符形式,框架会自动解析出额外的结构化,以下写法会额外输出字段【id】
logger.LogInformation("这是一条日志 {id}", id);
// 不推荐写法
logger.LogInformation($"这是一条日志 {id}");

// 自定义额外信息,单次
_logger.WithData(new { bid = 20251212 }).LogInformation("这是一条日志 {id}", id);

// 范围生效
using(_logger.WithScope(new { userId = 10001 })
{
    // 业务代码
    _logger.WithData(new { bid = 20251212 }).LogInformation("这是一条日志 {id}", id);
    // 业务代码
    _logger.WithData(new { orderId = "123456" }).LogInformation("这是一条日志 {id}", id);
    // 业务代码
}

// 不推荐直接使用 using(_logger.WithCtx(new { userId = 10001 }), 因为会在调用后整个请求生命周期内生效

// 使用 Module 做额外信息时,写文件日志时会自动把 Module 的值作为日志文件夹名称,可以使用 Module 来区分不同模块的日志
_logger.WithData(new { Module = "文件夹" }).LogCtxInformation("这是一条日志 {id}", id);

RabbitMQ Trace 传播

当业务代码使用 RabbitMQ 时,可以通过本库提供的扩展方法在消息生产和消费时传播 Trace 上下文,使得整个调用链(HTTP → Queue → Consumer)的 Trace 完整连贯。

安装

业务项目需要添加 WindPayer.Logging 的项目引用:

<ProjectReference Include="..\WindPayer.Logging\WindPayer.Logging.csproj" />

然后确保 appsettings.json 中开启了 Trace(EnableOtlp 或 EnableTrace):

"WindpayerLogging": {
  "EnableOtlp": true,
  "OtlpEndpoint": "http://localhost:4317",
  ...
}

使用方式

1. 消费者端 — QueueWorker.ConsumerReceived

在消息到达时提取 Trace 上下文并创建 Consumer Activity:

private async Task ConsumerReceived(object? sender, BasicDeliverEventArgs e)
{
    string content = string.Empty;

    // ========== 提取 Trace 上下文 ==========
    using var activity = e.StartConsumerActivity($"rabbitmq consume {QueueName}");
    // ======================================

    try
    {
        // ... 原有处理逻辑不变
        var body = e.Body.ToArray();
        content = Encoding.UTF8.GetString(body);
        // ...
    }
    catch (Exception ex)
    {
        _logger?.LogError(ex, "处理消息异常");
    }
}

2. 生产者端 — QueueService.Publish(首次发布)

发布时注入当前 Trace 上下文到消息 Headers:

public void Publish(string exchange, string routingKey, string messageId, string body)
{
    var bodyArray = Encoding.UTF8.GetBytes(body);
    var properties = Channel.CreateBasicProperties();
    properties.Persistent = true;
    properties.MessageId = messageId;

    // ========== 注入 Trace 上下文 ==========
    properties.InjectTrace();
    // =====================================

    Channel.BasicPublish(exchange, routingKey, properties, bodyArray, mandatory: true);
}

3. 持久化 Trace Headers(重试时保持同一 TraceId)

如需在消息重试时保持原始 TraceId,首次发布后将 Trace Headers 提取为 JSON 存入数据库,重试时恢复。

TMessageQueue 实体新增字段:

public string? TraceHeaders { get; set; }   // JSON 格式 {"traceparent":"00-xxx-xxx-00","tracestate":"..."}

首次发布 — 保存 Headers:

// 在 Publish 后调用,将 Headers 保存到数据库
msg.TraceHeaders = TraceHelper.ExtractTraceHeadersJson();

重试发布 — 恢复 Headers:

// 从数据库读取保存的 Headers
var savedHeaders = TraceHelper.DeserializeTraceHeaders(msg.TraceHeaders);

_queueService.Publish(exchange, routingKey, msgId, body, savedHeaders);

QueueService.Publish 支持可选的 traceHeaders 参数:

public void Publish(string exchange, string routingKey, string messageId, string body,
    IReadOnlyDictionary<string, string>? traceHeaders = null)
{
    var bodyArray = Encoding.UTF8.GetBytes(body);
    var properties = Channel.CreateBasicProperties();
    properties.Persistent = true;
    properties.MessageId = messageId;

    if (traceHeaders != null)
        properties.InjectTrace(traceHeaders);   // 重试:恢复原 TraceId
    else
        properties.InjectTrace();               // 首次:生成新 Trace

    Channel.BasicPublish(exchange, routingKey, properties, bodyArray, mandatory: true);
}

4. Serilog 日志自动附带 TraceId

在 WindPayer.Logging 的 SerilogBootstrapper 中已注册了 TraceEnricher,只要 Activity.Current 存在(通过上述扩展方法已经创建),所有 Serilog 日志事件都会自动附加 trace_idspan_id 字段,无需额外配置。


配置

在 appsettings.json 中添加如下配置

  "WindpayerLogging": {
    "LogLevel": "Information",              //日志级别
    "ServiceName": "Notification",          //服务名称  
    "EnableConsole": true,
    "EnableOtlp": false,
    "EnableTracing": false,
    "OtlpEndpoint": "http://localhost:4317",// OTLP 地址,EnableOtlp,EnableTracing为 true 时生效
    "OtlpAuthKey": "dXNlcjE6T20xNWNHRnpjMWQ=",   // OTLP key,base64 编码的 "用户名:密码"
    // 请求日志记录配置
    "RequestLogging": {
      "SlowRequestThresholdMs": 1000,
      "GetRequestSamplingRate": 0.1,
      "LogPostRequestsAll": true,
      "LogUnsampledSlowOrErrorRequests": true,
      "MaxRequestBodyLogLength": 10000,
      "MaxResponseBodyLogLength": 10000,      
      "IgnoredPaths": [
        "/health",
        "/healthz",
        "/ready",
        "/live",
        "/metrics"
      ]
    },
    // OTel 心跳包配置
    "HeartbeatOptions": {
      "Enabled": true,
      "IntervalSeconds": 30,
      "Message": "otel_heartbeat"
    }
  },

配置项说明

配置项 类型 默认值 说明
LogLevel string Information 日志级别,可选值:VerboseDebugInformationWarningErrorFatal
ServiceName string "" 服务名称,用于标识日志来源,在 OTLP 导出时作为 service.name 资源属性
EnableConsole bool true 是否启用控制台日志输出
EnableOtlp bool false 是否启用 OpenTelemetry OTLP 导出(Logging + Tracing + Metrics)
EnableTracing bool false 是否启用分布式追踪(Tracing),需配合 EnableOtlp: trueEnableConsole: true 才可导出
OtlpEndpoint string "" OTLP 接收端地址,如 http://localhost:4317,仅在 EnableOtlpEnableTracingtrue 时生效
OtlpAuthKey string "" OTLP 认证密钥,为 用户名:密码 的 Base64 编码。例如 dXNlcjE6T20xNWNHRnpjMWQ= 解码后为 user1:Om15cGFzc1d=
HeartbeatOptions.Enabled bool true 是否启用 OTel 心跳包,心跳日志可用于监控日志管道是否正常
HeartbeatOptions.IntervalSeconds int 30 心跳包发送间隔(秒)
HeartbeatOptions.Message string "otel_heartbeat" 心跳包的日志消息内容

RequestLogging 请求日志配置

配置项 类型 默认值 说明
RequestLogging.SlowRequestThresholdMs int 10000 慢请求阈值(毫秒),超过此阈值的请求将记录为慢请求
RequestLogging.GetRequestSamplingRate double 0.1 GET 请求的日志采样率,范围 0.0 ~ 1.00.1 表示仅记录 10% 的 GET 请求
RequestLogging.LogPostRequestsAll bool true 是否全量记录所有 POST 请求
RequestLogging.LogUnsampledSlowOrErrorRequests bool true 当发生慢请求或错误请求时,即便未命中采样率也强制记录日志。但由于未拦截 Body 流,此时的 Body 内容会记录为"未捕获"
RequestLogging.MaxRequestBodyLogLength int 10000 请求体日志最大记录长度(字符数)。超过的部分将被截断。如果小于等于 0,则全部记录不截断
RequestLogging.MaxResponseBodyLogLength int 10000 响应体日志最大记录长度(字符数)。超过的部分将被截断。如果小于等于 0,则全部记录不截断
RequestLogging.LogHeader bool true 是否记录 HTTP 请求/响应头
RequestLogging.IgnoredPaths string[] ["/health","/ready","/live","/metrics"] 要忽略记录的请求路径列表,支持精确匹配和前缀匹配。用于过滤健康检查、探针、监控等非业务请求

日志级别覆盖

除了 WindpayerLogging:LogLevel 全局日志级别外,还可以通过标准 ASP.NET Core 的 Logging:LogLevel 配置对各个命名空间分别设置日志级别:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.EntityFrameworkCore": "Warning",
      "YourNamespace": "Debug"
    }
  }
}

此配置会覆盖 Serilog 在各命名空间下的最小日志级别,便于控制第三方库的日志输出量。

Showing the top 20 packages that depend on WindPayer.Logging.

Packages Downloads
WindPayer.Logging.AspNetCore
提供 WindPayer.Logging 的 ASP.NET Core 快速配置扩展,包含 IStartupFilter 自动注入请求日志中间件及 WebApplicationBuilder / IHostBuilder 快速配置。
11
WindPayer.Logging.AspNetCore
提供 WindPayer.Logging 的 ASP.NET Core 快速配置扩展,包含 IStartupFilter 自动注入请求日志中间件及 WebApplicationBuilder / IHostBuilder 快速配置。
6
WindPayer.Logging.AspNetCore
提供 WindPayer.Logging 的 ASP.NET Core 快速配置扩展,包含 IStartupFilter 自动注入请求日志中间件及 WebApplicationBuilder / IHostBuilder 快速配置。
4

Version Downloads Last updated
1.1.13 7 07/16/2026
1.1.12 4 07/16/2026
1.1.11 4 07/15/2026
1.1.10 7 06/15/2026
1.1.9 5 06/15/2026
1.1.8 10 06/08/2026
1.1.7 11 06/05/2026
1.1.6 5 06/05/2026
1.1.5 6 06/05/2026
1.1.4 6 06/05/2026
1.1.3 6 06/03/2026
1.1.2 11 05/11/2026
1.1.1 9 04/30/2026
1.1.0 7 04/30/2026
1.0.9 6 04/24/2026
1.0.8 11 04/23/2026
1.0.7 8 04/23/2026
1.0.6 8 04/23/2026
1.0.5 6 03/26/2026
1.0.4 848 11/06/2025
1.0.3 845 10/29/2025
1.0.2 847 10/29/2025
1.0.1 851 10/29/2025
1.0.0 852 10/29/2025