关于 logging
面向 .NET 应用日志与可观测性的技能,涵盖健康端点、关联 ID 和日志级别策略,并将 Serilog 与 OpenTelemetry 配置交由对应技能处理。
主要功能
- 01区分存活与就绪健康端点
- 02在请求处理中传递关联 ID
- 03按环境制定日志级别策略
- 04避免在日志中记录敏感数据
适用场景
- 01为服务添加健康检查
- 02追踪跨下游调用的请求
- 03按环境选择日志级别
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ codewithmukesh/ dotnet-claude-kit --skill logging安装后在 Claude Code 中输入/logging确认已加载。
详细介绍
日志记录与可观测性
核心原则
- 使用 Serilog 进行结构化日志记录 — 每条日志都是包含命名属性的结构化事件,而非格式化字符串。这样便于搜索、筛选和设置警报。所有设置(两阶段引导、
AddSerilog()、接收器、增强器)都归 serilog 技能负责——该技能中关于使用AddSerilog()而非UseSerilog()的指导是权威依据。 - 使用 OpenTelemetry 进行分布式追踪 — 追踪用于关联跨服务的请求;指标用于持续跟踪系统运行状况。完整设置由 opentelemetry 技能负责。
- 使用运行状况检查确保运行就绪 — 每个服务都提供
/health端点,供负载均衡器和编排器使用。存活状态和就绪状态是两个不同的问题,应使用不同的端点。 - 使用关联 ID 追踪请求 — 每个请求都会获得一个唯一 ID,并贯穿所有日志条目和下游服务调用,因此一次用户投诉可以对应到一个经过筛选的日志流。
模式
各部分如何协同工作
| 关注点 | 负责人 | 技能 |
|---|---|---|
| 结构化应用日志 | Serilog(AddSerilog()) | serilog |
| 请求摘要日志记录 | UseSerilogRequestLogging() | serilog |
| 追踪 + 指标 + OTLP 导出 | OpenTelemetry SDK | opentelemetry |
| 运行状况端点、关联 ID、日志级别策略 | 本技能 | logging |
先接入日志记录(调试其他部分时需要日志),然后是运行状况检查,最后是追踪。
关联 ID
// Middleware to set correlation ID
public class CorrelationIdMiddleware(RequestDelegate next)
{
private const string CorrelationIdHeader = "X-Correlation-Id";
public async Task InvokeAsync(HttpContext context)
{
var correlationId = context.Request.Headers[CorrelationIdHeader].FirstOrDefault()
?? Guid.NewGuid().ToString();
context.Items["CorrelationId"] = correlationId;
context.Response.Headers[CorrelationIdHeader] = correlationId;
using (LogContext.PushProperty("CorrelationId", correlationId))
{
await next(context);
}
}
}
// Program.cs — register early so every downstream log carries the ID
app.UseMiddleware<CorrelationIdMiddleware>();
为什么使用中间件:在管道入口处推送一次该属性,即可将其附加到请求作用域内的每条日志事件,无需在每个调用点单独处理。通过 DelegatingHandler 在发出的 HttpClient 调用中传递相同的标头(参见 httpclient-factory 技能)。
运行状况检查
// Program.cs
builder.Services.AddHealthChecks()
.AddNpgSql(builder.Configuration.GetConnectionString("Default")!,
name: "database", tags: ["ready"])
.AddRedis(builder.Configuration.GetConnectionString("Redis")!,
name: "redis", tags: ["ready"])
.AddRabbitMQ(builder.Configuration.GetConnectionString("RabbitMq")!,
name: "rabbitmq", tags: ["ready"]);
// Map endpoints
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = _ => false // No dependency checks — just "am I running?"
});
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
为什么需要两个端点:存活检查失败表示“重启我”;就绪检查失败表示“停止向我发送流量”。将两者混为一谈,会导致数据库响应变慢时应用陷入反复重启的循环。
日志级别策略
| 级别 | 用途 | 环境默认值 |
|---|---|---|
| Debug | 诊断详情、负载转储(生产环境绝不记录 PII) | 仅开发环境 |
| Information | 业务事件:下单、任务完成 | 开发 + 预发布环境 |
| Warning | 可恢复的异常:触发重试、使用回退方案 | 所有环境——生产环境默认值 |
| Error | 需要关注的操作失败 | 所有环境 |
| Fatal/Critical | 应用无法继续运行 | 所有环境 |
为什么生产环境默认使用 Warning:规模扩大后,Information 级别的请求噪声会产生实际的日志存储成本,并淹没重要信号。通过命名空间覆盖保留真正的业务事件的 Information 级别日志(参见 serilog 技能中的 MinimumLevel.Override 模式)。
反模式
不要记录敏感数据
// BAD — logging credentials
logger.LogInformation("User logged in: {Email} with password {Password}", email, password);
// GOOD — log identifiers, never secrets or PII at Information level
logger.LogInformation("User {UserId} logged in", userId);
不要跳过运行状况检查标签
// BAD — all checks run for liveness AND readiness
app.MapHealthChecks("/health");
// GOOD — separate liveness (am I running?) from readiness (can I serve traffic?)
app.MapHealthChecks("/health/live", new() { Predicate = _ => false });
app.MapHealthChecks("/health/ready", new() { Predicate = c => c.Tags.Contains("ready") });
不要重新实现负责技能已提供的功能
// BAD — hand-rolling Serilog bootstrap here from memory
builder.Host.UseSerilog(...); // legacy API — the serilog skill forbids this
// GOOD — load the serilog skill and use its two-stage AddSerilog() bootstrap
builder.Services.AddSerilog((services, lc) => lc.ReadFrom.Configuration(builder.Configuration)...);
决策指南
| 场景 | 建议 |
|---|---|
| 应用日志设置 | 加载 serilog — 两阶段引导使用 AddSerilog() |
| 分布式追踪 / 指标 | 加载 opentelemetry — OTLP 导出器 |
| 自定义业务指标 | IMeterFactory + 计数器/直方图(opentelemetry 技能) |
| 请求追踪 | 关联 ID 中间件(本技能) |
| 容器运行状况 | /health/live 和 /health/ready 端点(本技能) |
| 日志存储 | Seq(开发环境)、Elastic/Grafana/OTLP 后端(生产环境) |
| 日志级别 | 开发环境使用 Debug,预发布环境使用 Information,生产环境默认使用 Warning |
评论
支持 Markdown