1. Wprowadzenie
Wyobraź sobie, że jesteś administratorem serwisu i codziennie przychodzi do ciebie tysiąc użytkowników. W logach widzisz, że wszystko działa, prawie nie ma błędów (chyba że ktoś czasem zapomni hasło albo potknie się o captcha). „Wszystko świetnie!” — myślisz.
I nagle ktoś pisze do supportu: strona strasznie zamula, strony ładują się po 10 sekund. Zaglądasz do logów — błędów brak! Hurra? Nie do końca, bo logi opowiadają co się stało (lub nie), ale nie mówią, jak szybko lub wolno to się działo, ile zasobów to potrzebowało i jak to zachowanie zmieniało się wraz ze wzrostem obciążenia.
Tu wchodzą metryki — mierzalne charakterystyki działania aplikacji. To nie tylko liczba błędów, ale średni czas odpowiedzi, ilość zużytej pamięci, liczba requestów na sekundę i inne wskaźniki, po których można ocenić zdrowie systemu.
Porównanie:
Logi — to "co się stało".
Metryki — to "jak dobrze/źle działa system".
Trace — to "dlaczego system tak działa (w szczegółach)".
Jakie są metryki i co warto zbierać?
Główne rodzaje metryk:
| Typ metryki | Przykład | Do czego służy |
|---|---|---|
| Licznik (Counters) | Liczba requestów, błędów, awarii | Trendy, alerty, obciążenie |
| Histogramy | Czas odpowiedzi, rozmiar pakietu | Rozkład wartości, percentyle |
| Gauges (Gauge) | Użycie pamięci, CPU | Bieżący stan zasobu |
| Sums (Sum) | Całkowita objętość danych, bajtów | Całkowita objętość operacji w okresie |
Przykłady:
- Średni i 95-ty percentyl czasu odpowiedzi na GET.
- Liczba użytkowników online w tej chwili.
- Użycie pamięci (Private Bytes, Working Set).
- Częstość występowania błędów typu 500/503.
- Zapytania do DB na minutę.
Te wskaźniki pozwalają nie tylko znajdować problemy, ale i zapobiegać im — bo zwiększone obciążenie serwera lub „pełzający” wzrost czasu odpowiedzi mogą sygnalizować przyszłe awarie.
2. Jak działa zbieranie metryk w .NET i ekosystemie OpenTelemetry
Ogólna architektura
We współczesnym .NET (od .NET 6, szczególnie w .NET 8/9) istnieje standardowy system zbierania metryk oparty na OpenTelemetry.
Oto jak to działa:
- Kod aplikacji wywołuje metody do zwiększania liczników, rejestracji gauge'ów, zapisywania histogramów.
- SDK OpenTelemetry Metrics zbiera te metryki (w pamięci) i okresowo je wysyła.
- Eksporter metryk przekazuje je do wybranego systemu monitoringu (Prometheus, Application Insights, Grafana Cloud, Datadog itp.).
- Backend monitoringu agreguje, przechowuje, wizualizuje, buduje alerty i dashboardy.
Schemat blokowy:
[Twoja aplikacja]
⬇
[Zbieranie metryk (OpenTelemetry SDK)]
⬇
[Eksporter metryk (Prometheus, AI, Datadog, ...)]
⬇
[System monitoringu/dashboardy/alerty]
3. Praktyka: Podstawy pracy z metrykami w C#
Proste metryki wewnętrzne: System.Diagnostics.Metrics
.NET dostarcza wbudowany mechanizm do metryk — System.Diagnostics.Metrics.
Główni gracze: Meter, Counter<T>, Histogram<T>, ObservableGauge<T>.
Przykład: licznik odwiedzin strony
// Tworzymy Meter (zwykle jeden dla całej aplikacji)
using System.Diagnostics.Metrics;
static Meter meter = new Meter("MyCompany.MyApp", "1.0");
// Rejestrujemy licznik
static Counter<long> homePageVisits = meter.CreateCounter<long>("HomePageVisits");
// Gdzieś w kodzie kontrolera lub serwisu...
public void HomePageRequested()
{
homePageVisits.Add(1);
// Dalej reszta kodu obsługi strony
}
Komentarze:
- Meter — to „fabryka” metryk, z unikalną nazwą (namespace aplikacji/firmy).
- CreateCounter<long> — tworzy licznik; inkrementacja przez Add(1).
Przykład: mierzenie czasu odpowiedzi
static Histogram<double> pageLoadTime = meter.CreateHistogram<double>("PageLoadTimeMs");
// W handlerze żądania:
public void OnRequest()
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
// ...tu samo żądanie...
stopwatch.Stop();
pageLoadTime.Record(stopwatch.Elapsed.TotalMilliseconds);
}
Zbieramy gauge'y dla wartości dynamicznych
Gauge — wskaźnik, który zmienia się w czasie: liczba podłączonych użytkowników, bieżący rozmiar pamięci itp.
static ObservableGauge<int> onlineUserGauge = meter.CreateObservableGauge(
"OnlineUsers",
() => GetOnlineUserCount());
// Gdzie GetOnlineUserCount - metoda zwracająca aktualną wartość
static int GetOnlineUserCount()
{
// Tu powinna być twoja realna logika!
return ActiveUserList.Count;
}
W realnym świecie wszystko działa asynchronicznie: aplikacja raportuje wartości metryk, a eksporter je pobiera i wystawia na zewnątrz (np. Prometheus scrapuje endpoint "/metrics").
Dodawanie metryk do nowoczesnej aplikacji ASP.NET Core
Dla ASP.NET Core wiele jest dostępne od ręki. Wystarczy dodać pakiet OpenTelemetry.Instrumentation.AspNetCore, i pojawią się metryki HTTP, czasu odpowiedzi, liczby błędów itp.
Przykład konfiguracji w 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() // metryki HTTP
.AddRuntimeInstrumentation() // metryki środowiska uruchomieniowego .NET CLR
.AddProcessInstrumentation() // CPU/pamięć procesu
.AddMeter("MyCompany.MyApp") // twoje metryki
.AddPrometheusExporter(); // eksport do Prometheus
});
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Teraz twoja aplikacja będzie wystawiać metryki pod adresem /metrics, które można scrape'ować przez Prometheus lub inne systemy.
4. Praktyczne przykłady użycia metryk
Monitorowanie wydajności w realnych projektach
Podłączamy metryki, żeby dowiedzieć się:
- Jaki średni i szczytowy RPS (requests per second) wytrzymuje API?
- Gdzie są „wąskie gardła”: jeden endpoint działa 300 ms, inny — 2000 ms?
- Ile czasu zajmują wywołania DB? (dodajemy własne histogramy)
Przykład: kontrolujemy czas odpowiedzi do bazy danych
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;
}
Przykład: zliczamy liczbę błędów
static Counter<long> apiErrors = meter.CreateCounter<long>("ApiErrors");
public IActionResult SomeEndpoint()
{
try
{
// jakieś działanie
return Ok();
}
catch (Exception)
{
apiErrors.Add(1);
throw;
}
}
Praca z labelami (tagami) dla metryk
Ważne jest grupowanie danych według użytecznych atrybutów: endpoint, typ błędu, typ użytkownika itp.
homePageVisits.Add(
1,
KeyValuePair.Create<string, object>("UserType", "Admin"));
A dla histogramu:
dbQueryDuration.Record(
sw.Elapsed.TotalMilliseconds,
KeyValuePair.Create<string, object>("QueryType", "GetProducts"));
Dzięki tagom w Grafanie można budować wykresy nie tylko dla całej aplikacji, ale i dla konkretnych segmentów.
5. Integracja z Prometheus, Application Insights, Datadog, Grafana
Eksportery i integracja
- Prometheus — popularny open-source monitoring, de-facto standard dla chmur i Kubernetes.
- Application Insights — integracja chmurowa dla Azure.
- Datadog, Grafana Cloud — dla profesjonalnej infrastruktury.
Wszystkie te systemy mogą zbierać metryki z .NET przez eksportery OpenTelemetry. Dokumentacja o exporterach OTel
Prometheus (kroki):
- Dodać NuGet package: OpenTelemetry.Exporter.Prometheus.
- Dodać .AddPrometheusExporter() przy rejestracji metryk.
- W Grafanie skonfigurować datasource na Prometheus i zbudować dashboardy.
Przydatne linki:
6. Cechy, pułapki i typowe błędy
Jednym z częstych błędów jest nadmierne szczegółowe tagowanie. Jeśli przypisywać tagom zbyt wiele unikalnych wartości (np. ID użytkownika/zamówienia), liczba time series eksploduje — to prowadzi do przeciążenia storage'u metryk i wzrostu kosztów (tzw. cardinality explosion). Trzymaj tagi „gruboziarniste”.
Programiści czasem ignorują System.Diagnostics.Metrics i gotowe narzędzia, robiąc „rower” przez logi i timery. W rezultacie monitoring gorzej się integruje i trudniej go utrzymać. Używaj standardowych narzędzi i automatycznej instrumentacji.
Kolejny błąd — metryki są zbierane, ale nie eksportowane. Konfiguracja eksportera jest obowiązkowa: dodaj np. .AddPrometheusExporter() i upewnij się, że endpoint /metrics jest dostępny do scrapowania.
I wreszcie, zamieszanie między typami metryk: średni czas odpowiedzi liczony licznikami (Counter), choć trzeba użyć Histogram — wtedy nie zobaczysz pików i rozkładu. Liczniki — do ilości; histogramy — do czasu/rozmiarów/rozkładów; gauge'y — do stanów bieżących.
GO TO FULL VERSION