Prometheusa poznałeś już w module o monitoringu: cztery typy metryk, scraping i endpoint
/metrics napisany ręcznie kontrolerem. Działało - ale przy każdej nowej metryce trzeba było pamiętać o rejestrze, a przy każdym module o wstrzyknięciu serwisu, który je trzyma.NestJS ma na to gotowe rozwiązanie.
zbiera i eksponuje metryki aplikacji w formacie Prometheus - a przy okazji sprawia, że metryki stają się zwykłymi providerami, wstrzykiwanymi jak każdy inny.@willsoto/nestjs-prometheus
Metryki deklarujesz w module, przez funkcje pomocnicze pakietu:
1@Module({
2 imports: [PrometheusModule.register()],
3 providers: [
4 makeCounterProvider({
5 name: 'http_requests_total',
6 help: 'Calkowita liczba zadan HTTP',
7 labelNames: ['method', 'route', 'status'],
8 }),
9 makeGaugeProvider({
10 name: 'active_connections',
11 help: 'Liczba aktywnych polaczen',
12 }),
13 makeHistogramProvider({
14 name: 'http_request_duration_seconds',
15 help: 'Czas obslugi zadania HTTP',
16 labelNames: ['method', 'route'],
17 }),
18 ],
19})
20export class MetricsModule {}Kolejność wewnątrz
makeCounterProvider jest stała: makeCounterProvider({, potem name: 'http_requests_total',, dalej help: '...',, na końcu }).PrometheusModule.register() wystawia endpoint /metrics - tego już nie piszesz sam. Trzy pomocnicze funkcje odpowiadają trzem typom metryk, które znasz: Counter tylko rośnie (liczba żądań), Gauge rośnie i maleje (aktywne połączenia), Histogram mierzy rozkład w kubełkach (czasy odpowiedzi). Czwarty typ, Summary, oblicza kwantyle po stronie aplikacji.Zarejestrowaną metrykę wstrzykujesz jak każdy provider - tyle że wskazując ją po nazwie:
1@Injectable()
2export class MetricsService {
3 constructor(
4 @InjectMetric('http_requests_total')
5 private readonly requestsCounter: Counter<string>,
6
7 @InjectMetric('http_request_duration_seconds')
8 private readonly requestDuration: Histogram<string>,
9 ) {}
10
11 recordRequest(method: string, route: string, status: number) {
12 this.requestsCounter.inc({ method, route, status: String(status) });
13 }
14
15 startTimer(method: string, route: string) {
16 return this.requestDuration.startTimer({ method, route });
17 }
18}
działa jak @InjectMetric('nazwa')
@Inject z tokenem, który poznałeś przy providerach z useValue - metryka nie jest klasą, więc NestJS nie dobierze jej po typie.Dwie metody warto rozróżnić.
podnosi licznik o jeden; to wszystko, co Counter potrafi. inc()
zwraca funkcję, którą wywołujesz po zakończeniu pracy - dopiero wtedy histogram zapisuje zmierzony czas:startTimer()
1const timer = this.metricsService.startTimer('GET', '/legions');
2
3await this.legionService.findAll();
4
5timer();Ten wzorzec - weź funkcję, wykonaj pracę, wywołaj funkcję - jest wygodniejszy niż ręczne odejmowanie znaczników czasu i sam dba o jednostkę: histogramy Prometheusa liczą w sekundach, nie milisekundach.
Metryk można dodać setki. RED to skrót wskazujący trzy, od których zawsze zaczynasz, uporządkowane od najważniejszej:
Kolejność nie jest przypadkowa i warto ją zapamiętać: spadek Rate do zera znaczy, że nikt nie może się połączyć - to alarm natychmiastowy. Rosnące Errors to awaria widoczna dla użytkowników. Rosnące Duration to problem, który dopiero się rozwija.
Zwróć uwagę, dlaczego czas podajemy jako percentyle, a nie średnią. Średnia 200 ms brzmi dobrze, nawet gdy co dwudziesty użytkownik czeka pięć sekund - p95 mówi wprost: „95% żądań zmieściło się poniżej tej wartości". To jest liczba, która opisuje doświadczenie, a nie je zaciera, @name.
Endpoint wystawiony przez pakiet oddaje dane w formacie tekstowym Prometheusa - nie JSON, nie XML, nie CSV:
1# HELP http_requests_total Calkowita liczba zadan HTTP
2# TYPE http_requests_total counter
3http_requests_total{method="GET",route="/legions",status="200"} 1027
4http_requests_total{method="POST",route="/legions",status="201"} 43Kształt jest prosty i warto go umieć przeczytać. Linie zaczynające się od
niosą opis metryki, a od # HELP
- jej typ. Dalej idą same pomiary: nazwa, etykiety w nawiasach klamrowych, wartość.# TYPE
Ten format wygląda ubogo obok JSON-a, ale właśnie o to chodzi: Prometheus odpytuje tysiące aplikacji co kilkanaście sekund, więc parsowanie musi być tanie. Gdy zajrzysz pod
/metrics w przeglądarce i zobaczysz ścianę tekstu - to znaczy, że działa poprawnie.Oczy Imperium patrzą, a metryki są zwykłymi providerami:
@willsoto/nestjs-prometheus zbiera i eksponuje metryki w formacie Prometheus - nie obsługuje WebSocketów, nie testuje endpointów, nie generuje dokumentacji,PrometheusModule.register() wystawia endpoint /metrics za Ciebie,makeCounterProvider, makeGaugeProvider, makeHistogramProvider,makeCounterProvider({, name:, help:, }),@InjectMetric('nazwa') wstrzykuje metrykę po nazwie, bo nie jest klasą i typ nie wystarcza,inc() podnosi licznik; startTimer() zwraca funkcję, którą wywołujesz po pracy - histogram liczy w sekundach,/metrics zwraca format tekstowy Prometheusa: # HELP, # TYPE, potem nazwa z etykietami i wartość - nie JSON, XML ani CSV.W następnej lekcji pójdziemy o krok dalej niż liczby - poznasz distributed tracing, który pokazuje drogę pojedynczego żądania przez wiele usług. A na razie zapamiętaj: metryki mówią, ile i jak szybko; RED wskazuje, które trzy liczby sprawdzić najpierw.