Выход ASP.NET Core 9 порадовал возможностью выборочно отключать http-метрики. В статье сценарии использования с примерами и детальный разбор того, как всё устроено под капотом.
Зачем нужны метрики
Метрики дают общее представление текущего состояния сервиса. Например, можно посмотреть количество запросов к сервису или определённым endpoint-ам в единицу времени, а еще длительность выполнения запросов. В отличие от логов, метрики не показывают детали какого-то конкретного единичного действия.
Зачем отключать метрики
Обычно у приложения есть технические endpoint-ы — например, health check или ручка скрейпинга метрик для сборщика. По этим endpoint-ам чаще всего никто не смотрит метрики. А при расчёте статистик они могут быть даже вредны. Для таких endpoint-ов полезно иметь инструмент с простым синтаксисом, который выборочно отключит сбор метрик.
Как отключать метрики
Отключение метрик в ASP.NET Core 9 работает для minimal API, контроллеров и middleware.
Minimal API
Тут всё просто. Для IEndpointConventionBuilder есть метод расширения DisableHttpMetrics. Его можно вызвать для любых endpoint-ов и групп, чтобы отключить сбор http-метрик:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry();
var app = builder.Build();
app.MapGet("/", () => "endpoint").DisableHttpMetrics();
var group = app.MapGroup("/group").DisableHttpMetrics();
app.MapHealthChecks("/healthz").DisableHttpMetrics();
app.Run();
Под капотом тоже всё понятно. К метаданным endpoint-а добавляется атрибут DisableHttpMetricsAttribute. Он реализует интерфейс IDisableHttpMetricsMetadata.
Контроллеры
Тут всё ещё проще. DisableHttpMetrics можно добавить либо ко всему контроллеру, либо к его методам:
[ApiController]
[Route("")]
[DisableHttpMetrics]
public class CustomController : ControllerBase
{
[DisableHttpMetrics]
[HttpGet]
public string Get() => "Hello World!";
}
Middleware
А вот тут всё интереснее. В middleware отключение метрик регулируется свойством MetricsDisabled объекта IHttpMetricsTagsFeature. И работает это не для указанного Endpoint-а, а для определённого условия. Это открывает большие возможности для гибкой настройки сбора http-метрик. Например, если запрос содержит какой-то специальный header, метрики не будут собираться независимо от маршрута:
internal partial class CustomMiddleware(RequestDelegate next)
{
public Task Invoke(HttpContext context)
{
var metricsFeature = context.Features.Get<IHttpMetricsTagsFeature>();
if (metricsFeature != null &&
context.Request.Headers.ContainsKey("x-disable-metrics"))
{
metricsFeature.MetricsDisabled = true;
}
return next(context);
}
}
Что под капотом
С отключением http-метрик всё прояснилось, но как именно это работает под капотом? Обычно для работы с метриками используются готовые библиотеки. Но если посмотреть в исходный код какого-нибудь популярного решения, например OpenTelemetry, там не будет ни одной проверки, связанной с DisableHttpMetricsAttribute, IDisableHttpMetricsMetadata или HttpMetricsTagsFeature.MetricsDisabled. Как тогда библиотека отслеживает, для каких запросов метрики собирать нужно, а для каких — нет?
А вот как. Начиная с восьмой версии, ASP.NET Core умеет в сбор основных метрик без подключения сторонних библиотек. А OpenTelemetry и аналогичные решения оперируют уже собранными данными. Соответственно, все проверки находятся в самом ASP.NET Core. Сначала проверяется, есть ли у endpoint-а IDisableHttpMetricsMetadata и не выставлен ли флаг HttpMetricsTagsFeature.MetricsDisabled. Результат проверки передаётся в HostingMetrics.RequestEnd, где и прописана основная логика сбора или игнорирования метрик.
Итог
Минималистичный синтаксис делает инструмент простым и полезным
Можно назначить на конкретные маршруты. Либо для всех маршрутов, но при выполнении условий
Будет работать только для собственных метрик ASP.NET Core
Полезные ссылки
Introducing ASP.NET Core metrics and Grafana dashboards in .NET 8 | Devblogs