WindPayer.Logging.AspNetCore 1.0.0
安装
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.WithScope(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_id 和 span_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 |
日志级别,可选值:Verbose、Debug、Information、Warning、Error、Fatal |
| ServiceName | string |
"" |
服务名称,用于标识日志来源,在 OTLP 导出时作为 service.name 资源属性 |
| EnableConsole | bool |
true |
是否启用控制台日志输出 |
| EnableOtlp | bool |
false |
是否启用 OpenTelemetry OTLP 导出(Logging + Tracing + Metrics) |
| EnableTracing | bool |
false |
是否启用分布式追踪(Tracing),需配合 EnableOtlp: true 或 EnableConsole: true 才可导出 |
| OtlpEndpoint | string |
"" |
OTLP 接收端地址,如 http://localhost:4317,仅在 EnableOtlp 或 EnableTracing 为 true 时生效 |
| 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.0。0.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 在各命名空间下的最小日志级别,便于控制第三方库的日志输出量。
No packages depend on WindPayer.Logging.AspNetCore.
.NET 8.0
- WindPayer.Logging (>= 1.0.0)
- OpenTelemetry.Instrumentation.AspNetCore (>= 1.9.0)