1. 介绍
假设你是个网站管理员,每天有一千个用户访问你的网站。看日志的时候你发现一切正常,几乎没有错误(顶多有人偶尔忘记密码或验证码输错)。你会想「一切都很好!」。
然后有人告诉客服:网站特别卡,页面要 10 秒才打开。你去看日志——没有错误!是好事吗?不是。因为日志告诉你的是 发生了什么(或者没发生什么),但不会告诉你 有多快或多慢、用了多少资源、以及在负载增加时这个行为如何变化。
这时候指标就派上用场了——应用可测的运行特征。不只是错误数,还有平均响应时间、内存占用、每秒请求数等能反映系统健康的指标。
对比:
日志 — 是 「发生了什么」。
指标 — 是 「系统运行得好还是不好」。
追踪(tracing) — 是 「为什么系统这样工作(详细原因)」。
有哪些类型的指标,应该收集哪些?
主要指标类型:
| 指标类型 | 示例 | 用途 |
|---|---|---|
| 计数器 (Counters) | 请求数、错误数、故障数 | 趋势、告警、负载 |
| 直方图 (Histograms) | 响应时间、包大小 | 值分布、percentiles(百分位) |
| 仪表 (Gauge) | 内存使用、CPU | 资源的当前状态 |
| 求和器 (Sum) | 数据总量、字节数 | 一段时间内的总操作量 |
示例:
- GET 请求响应时间的平均值和第 95 百分位。
- 当前在线的用户数量。
- 内存使用(Private Bytes, Working Set)。
- 500/503 类错误的发生频率。
- 每分钟的数据库请求数。
这些指标不仅能帮你发现问题,还能帮你 提前预防——因为服务器负载上升或响应时间缓慢增长通常预示着未来的故障。
2. .NET 与 OpenTelemetry 指标收集的工作机制
总体架构
在现代的 .NET(从 .NET 6 开始,尤其是 .NET 8/9)里,有基于 OpenTelemetry 的标准指标收集体系。
工作流程大致是这样的:
- 应用代码 调用方法来增加计数器、注册仪表、记录直方图等。
- OpenTelemetry Metrics SDK 在内存中收集这些指标并定期发送。
- 指标导出器 把它们传到选定的监控系统(Prometheus、Application Insights、Grafana Cloud、Datadog 等)。
- 监控后端 聚合、存储、可视化,并提供告警和仪表盘。
示意流程图:
[你的应用]
⬇
[指标收集 (OpenTelemetry SDK)]
⬇
[指标导出器 (Prometheus, AI, Datadog, ...)]
⬇
[监控系统/仪表盘/告警]
3. 实践:在 C# 中使用指标的基础
简单的内部指标: System.Diagnostics.Metrics
.NET 提供了内置的指标机制 — System.Diagnostics.Metrics。
主要角色:Meter, Counter<T>, Histogram<T>, ObservableGauge<T>。
示例:页面访问计数器
// 创建 Meter(通常整个应用只一个)
using System.Diagnostics.Metrics;
static Meter meter = new Meter("MyCompany.MyApp", "1.0");
// 注册计数器
static Counter<long> homePageVisits = meter.CreateCounter<long>("HomePageVisits");
// 在控制器或服务的某处...
public void HomePageRequested()
{
homePageVisits.Add(1);
// 页面处理的其余逻辑
}
注释:
- Meter 是创建指标的「工厂」,有唯一名字(通常用应用/公司命名空间)。
- CreateCounter<long> 创建计数器;通过 Add(1) 来递增。
示例:测量响应时间
static Histogram<double> pageLoadTime = meter.CreateHistogram<double>("PageLoadTimeMs");
// 在请求处理器中:
public void OnRequest()
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
// ...这里是请求处理的主体...
stopwatch.Stop();
pageLoadTime.Record(stopwatch.Elapsed.TotalMilliseconds);
}
为动态值收集 Gauge
Gauge 是随时间变化的指标,比如在线用户数、当前内存占用等。
static ObservableGauge<int> onlineUserGauge = meter.CreateObservableGauge(
"OnlineUsers",
() => GetOnlineUserCount());
// GetOnlineUserCount 是返回当前值的方法
static int GetOnlineUserCount()
{
// 这里应该是你真实的逻辑!
return ActiveUserList.Count;
}
在真实环境中这些都是异步工作的:应用提供指标值,导出器定期抓取并发送出去(比如 Prometheus 会 scrape "/metrics" endpoint)。
在现代 ASP.NET Core 应用中添加指标
对于 ASP.NET Core 来说很多东西开箱可用。只需要加入包 OpenTelemetry.Instrumentation.AspNetCore,就会产生 HTTP 请求、响应时间、错误数等指标。
在 Program.cs 中的配置示例:
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyApp"))
.AddAspNetCoreInstrumentation() // HTTP 指标
.AddRuntimeInstrumentation() // .NET CLR 的运行时指标
.AddProcessInstrumentation() // 进程的 CPU/内存 指标
.AddMeter("MyCompany.MyApp") // 你自己的 Meter
.AddPrometheusExporter(); // 导出到 Prometheus
});
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
现在你的应用会在 /metrics 地址暴露指标,可以被 Prometheus 或其他系统抓取。
4. 指标的实际使用示例
在真实项目中监控性能
接入指标可以帮你了解:
- API 的平均和峰值 RPS(requests per second)能承受多少负载?
- 瓶颈在哪里:某个 endpoint 平均 300 ms,另一个是 2000 ms?
- DB 调用花了多少时间?(可以添加自定义的直方图)
示例:监控数据库查询时间
static Histogram<double> dbQueryDuration = meter.CreateHistogram<double>("DbQueryDurationMs");
public async Task<List<Product>> GetProductsAsync()
{
var sw = Stopwatch.StartNew();
var result = await _db.Products.ToListAsync();
sw.Stop();
dbQueryDuration.Record(sw.Elapsed.TotalMilliseconds);
return result;
}
示例:统计错误次数
static Counter<long> apiErrors = meter.CreateCounter<long>("ApiErrors");
public IActionResult SomeEndpoint()
{
try
{
// 某些操作
return Ok();
}
catch (Exception)
{
apiErrors.Add(1);
throw;
}
}
为指标使用标签(tags/labels)
按有意义的维度对数据做分组很重要:endpoint、错误类型、用户类型等。
homePageVisits.Add(
1,
KeyValuePair.Create<string, object>("UserType", "Admin"));
或者对直方图:
dbQueryDuration.Record(
sw.Elapsed.TotalMilliseconds,
KeyValuePair.Create<string, object>("QueryType", "GetProducts"));
有了标签,在 Grafana 里你就可以按应用整体或具体细分维度画图。
5. 与 Prometheus、Application Insights、Datadog、Grafana 的集成
导出器与集成
- Prometheus — 流行的开源监控方案,云和 Kubernetes 场景的事实标准。
- Application Insights — Azure 的云端集成方案。
- Datadog, Grafana Cloud — 专业级的基础设施方案。
这些系统都可以通过 OpenTelemetry 的导出器从 .NET 收集指标。关于 OTel 导出器的文档
Prometheus(步骤):
- 添加 NuGet 包: OpenTelemetry.Exporter.Prometheus。
- 在指标注册里添加 .AddPrometheusExporter()。
- 在 Grafana 配置数据源指向 Prometheus,并构建仪表盘。
有用链接:
6. 特性、坑和常见错误
一个常见的错误是 标签过度细化。如果给标签赋予太多唯一值(比如把用户 ID、订单 ID 当成标签),时间序列的数量会爆炸——这会导致指标存储被压垮、成本上升(也就是所谓的 cardinality explosion)。把标签做成「粗粒度」的。
开发者有时会忽略 System.Diagnostics.Metrics 和现成的工具,自己用日志加定时器去实现「轮子」。结果监控难以集成,也难以维护。尽量使用标准工具和自动化的 instrumentations。
另一个常见问题是 指标被收集了但没有导出。导出器的配置是必须的:比如添加 .AddPrometheusExporter(),并确保 /metrics endpoint 对 scraper 可访问。
最后,常见的混淆是 指标类型用错:有人用 Counter 去统计平均响应时间,但应该用 Histogram ——否则你看不到峰值和分布。Counters 用来计数;Histograms 用来记录时间/大小/分布;Gauges 用来表示当前状态。
GO TO FULL VERSION