健康检查和可观测性
日志告诉我们应用发生了什么,健康检查帮助宿主判断实例是否可用,指标和分布式追踪则用于观察趋势和定位跨服务问题。本篇笔记介绍ASP.NET Core健康检查以及OpenTelemetry的基本接入方式。NLog和日志级别等基础内容可参考软件工程/dotNet/dotNet扩展库/Logging-日志框架和本系列日志章节。
注册健康检查
最简单的健康检查只需要注册服务并映射端点:
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/health");
app.Run();
默认情况下,健康端点会执行所有已注册的检查,并返回Healthy、Degraded或Unhealthy对应的结果。实际项目通常需要区分两类探针:
- Liveness(存活):进程是否仍然需要被重启,通常只检查进程自身,不应因数据库等外部依赖短暂故障而重启应用。
- Readiness(就绪):实例当前是否适合接收流量,可以检查数据库、必要配置和关键依赖。
下面使用ready标签筛选就绪检查,并让存活端点不执行外部依赖检查:
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
builder.Services.AddHealthChecks()
.AddCheck(
"self",
() => HealthCheckResult.Healthy("应用正在运行"),
tags: new[] { "live" })
.AddCheck(
"database",
() => HealthCheckResult.Healthy("数据库可用"),
tags: new[] { "ready" });
var app = builder.Build();
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = _ => false
});
app.Run();
这里的数据库检查只是展示注册方式,真实项目应执行有超时控制的数据库探测,而不是固定返回健康结果。
自定义健康检查
实现IHealthCheck可以封装依赖检查:
using Microsoft.Extensions.Diagnostics.HealthChecks;
public class ExternalApiHealthCheck : IHealthCheck
{
private readonly HttpClient _httpClient;
public ExternalApiHealthCheck(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
using HttpResponseMessage response = await _httpClient.GetAsync(
"/health", cancellationToken);
return response.IsSuccessStatusCode
? HealthCheckResult.Healthy()
: HealthCheckResult.Unhealthy($"HTTP {(int)response.StatusCode}");
}
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException)
{
return HealthCheckResult.Unhealthy("外部服务不可用", ex);
}
}
}
注册检查时可以同时注册其HttpClient:
builder.Services.AddHttpClient<ExternalApiHealthCheck>(client =>
{
client.BaseAddress = new Uri("https://example.com");
client.Timeout = TimeSpan.FromSeconds(3);
});
builder.Services.AddHealthChecks()
.AddCheck<ExternalApiHealthCheck>("external-api", tags: new[] { "ready" });
EF Core、Redis等依赖也有相应的健康检查扩展包。引入时应核对目标框架和提供程序版本,并设置合理超时,避免健康端点本身长时间占用资源。
健康端点可能暴露依赖和拓扑信息,生产环境应根据部署方式限制访问来源,避免在响应中输出连接串和异常堆栈。Kubernetes可以将readiness和liveness探针分别映射到上述端点,具体探针配置可参考笔记库的Kubernetes相关章节。
结构化日志和请求关联
日志应使用消息模板而不是字符串插值:
_logger.LogInformation(
"查询学生 {StudentId},追踪ID {TraceId}",
studentId,
Activity.Current?.TraceId.ToString() ?? HttpContext.TraceIdentifier);
Activity.TraceId是W3C分布式追踪上下文中的追踪ID,HttpContext.TraceIdentifier是当前ASP.NET Core请求的标识,两者不是同一个属性。使用Activity和OpenTelemetry时,可以在日志Scope中输出TraceId、SpanId,将日志与追踪关联起来。密码、令牌、连接串和个人敏感信息不应直接写入日志。
使用OpenTelemetry收集追踪和指标
OpenTelemetry提供统一的追踪、指标和日志接入规范。ASP.NET Core常用组件包括:
OpenTelemetry.Extensions.HostingOpenTelemetry.Instrumentation.AspNetCoreOpenTelemetry.Instrumentation.HttpOpenTelemetry.Instrumentation.Runtime- 根据导出目标选择
OpenTelemetry.Exporter.Prometheus.AspNetCore或OpenTelemetry.Exporter.OpenTelemetryProtocol
下面是一个基础配置示例,具体包版本应与目标框架和所选OpenTelemetry版本匹配:
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter())
.WithMetrics(metrics => metrics
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation()
.AddPrometheusExporter());
var app = builder.Build();
app.MapPrometheusScrapingEndpoint();
app.Run();
OTLP Exporter通常将数据发送到OpenTelemetry Collector,再由Collector转发到Jaeger、SkyWalking或其他后端;Prometheus Exporter则暴露指标抓取端点。生产环境需要配置导出地址、采样率、资源名称和端点访问控制,不能无差别记录所有高基数标签。
自定义业务追踪可以创建ActivitySource:
using System.Diagnostics;
public static class DemoTelemetry
{
public static readonly ActivitySource Source = new("DemoWebAPI");
}
using Activity? activity = DemoTelemetry.Source.StartActivity("SyncStudents");
activity?.SetTag("students.count", students.Count);
同时在追踪配置中添加.AddSource(DemoTelemetry.Source.Name),自定义Activity才会被收集。指标则可以使用System.Diagnostics.Metrics.Meter创建Counter、Histogram等仪器。
可观测性实施建议
优先建立少量高价值信号:请求成功率、延迟、错误率、依赖可用性和Trace ID关联。随后再逐步增加业务指标和详细Span。无限制地记录所有请求体、所有SQL参数或高基数用户标签,既可能泄露敏感信息,也会显著增加存储和计算成本。