Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Monitoring i Alerting - systemy wczesnego ostrzegania

Aplikacja działa. Żaden wyjątek nie poleciał, logi milczą, health check świeci na zielono. A jednak użytkownicy piszą, że „strona zamula". Sprawdzasz - odpowiedzi przychodzą po ośmiuset milisekundach zamiast po stu. Od kiedy? Nie wiadomo. Logi zapisują zdarzenia, a to jest trend: coś, co narastało tygodniami i czego nie widać w żadnym pojedynczym wpisie.

Rzym stawiał na granicach wieże sygnałowe. Nie meldowały pojedynczych zdarzeń - mierzyły ruch: ilu jeźdźców przejechało, jak długo trwa przeprawa, ilu wartowników nie wróciło. Dopiero z tych liczb widać było, że coś się psuje, zanim padła brama. Tym są metryki.

Cztery typy metryk

Standardem zbierania metryk jest Prometheus - system, który zbiera i przechowuje liczby opisujące aplikację. Oferuje cztery typy pomiaru, od najprostszego do najbardziej złożonego:

  1. Counter - tylko rośnie. Liczba obsłużonych żądań, liczba błędów. Nigdy nie maleje; przy restarcie zaczyna od zera.
  2. Gauge - może rosnąć i maleć. Liczba aktywnych połączeń, zajęta pamięć, długość kolejki. Zdejmuje bieżącą wartość, jak wskazówka na mierniku.
  3. Histogram - rozkład wartości w kubełkach (buckets). Zamiast jednej liczby zapamiętuje, ile pomiarów wpadło w przedział 0-100 ms, ile w 100-500 ms i tak dalej.
  4. Summary - podobnie jak histogram, ale percentyle liczone po stronie klienta, czyli w Twojej aplikacji, a nie w Prometheusie.

Do czasu trwania żądań HTTP właściwy jest Histogram. Counter powiedziałby tylko, ile żądań było, a Gauge - ile trwało ostatnie. Histogram pokazuje kształt: że dziewięć na dziesięć żądań mieści się poniżej 200 ms, a co dziesiąte przekracza sekundę. To ten kształt zdradza problem, którego średnia by nie pokazała.

Definicja metryki

Metrykę tworzysz raz, opisując ją trzema polami:

1import { Counter } from 'prom-client';
2
3@Injectable()
4export class PrometheusService {
5  private readonly httpRequestCounter = new Counter({
6    name: 'http_requests_total',
7    help: 'Total HTTP requests',
8    labelNames: ['method', 'route', 'status'],
9  });
10
11  recordRequest(method: string, route: string, status: number) {
12    this.httpRequestCounter.inc({ method, route, status: String(status) });
13  }
14}

Kolejność pól jest umowna, ale zawsze ta sama:

name
to identyfikator metryki,
help
- opis czytany przez człowieka,
labelNames
- lista wymiarów, po których będzie można ciąć dane.

Etykiety są tu najciekawsze. Dzięki nim jeden licznik odpowiada na wiele pytań: ile było żądań POST, ile trafiło na

/legions
, ile zakończyło się kodem 500. Bez etykiet potrzebowałbyś osobnego licznika na każdą kombinację.

Uwaga na pułapkę: etykieta o wielu możliwych wartościach mnoży liczbę serii danych. Wstawienie identyfikatora użytkownika jako etykiety utworzy tyle serii, ilu masz użytkowników - i położy Prometheusa. Etykiety mają mieć skończony, mały zbiór wartości, @name.

Metryki systemowe za darmo

Zanim napiszesz własne metryki, warto włączyć te wbudowane:

1import { collectDefaultMetrics, register } from 'prom-client';
2
3collectDefaultMetrics();

collectDefaultMetrics()
zbiera domyślne metryki systemowe - zużycie procesora, pamięci i opóźnienie pętli zdarzeń Node.js. Ta ostatnia jest szczególnie cenna: rosnące opóźnienie event loopu znaczy, że coś blokuje wątek i aplikacja przestaje nadążać, choć żaden endpoint jeszcze nie pada.

Zwróć uwagę, czego ta funkcja nie robi: niczego nie wysyła, nie tworzy wykresów ani nie zeruje liczników. Jedynie zaczyna zbierać.

Endpoint /metrics

Prometheus nie przyjmuje danych przysyłanych przez aplikację - sam po nie przychodzi. Twoim zadaniem jest wystawić je pod ustalonym adresem:

1@Controller()
2export class MetricsController {
3  @Get('/metrics')
4  async getMetrics(@Res() res: Response) {
5    res.set('Content-Type', register.contentType);
6    res.send(await register.metrics());
7  }
8}

register
to rejestr wszystkich zdefiniowanych metryk, a
register.metrics()
zwraca je w formacie tekstowym, który Prometheus rozumie. Nagłówek
Content-Type
musi wskazywać tekst zwykły, nie JSON - stąd
register.contentType
, które ustawia właściwą wartość za Ciebie.

Ten model nazywa się scrapingiem: Prometheus co kilkanaście sekund odpytuje ten adres i zapisuje to, co zastał. Aplikacja nie musi wiedzieć, kto ją obserwuje ani czy ktokolwiek w ogóle.

Pięć etapów wdrożenia

Cała droga od zera do wykresu wygląda tak:

  1. Instalacja pakietu
    prom-client
    .
  2. Zdefiniowanie metryk - Counter, Histogram, tyle ile potrzeba.
  3. Utworzenie endpointu
    /metrics
    .
  4. Konfiguracja scrapera Prometheus, żeby wiedział, gdzie i jak często pytać.
  5. Wizualizacja metryk w Grafanie - i dopiero tu powstają wykresy oraz alerty.

Podział ról między dwoma ostatnimi bywa myląco zacierany. Prometheus zbiera i przechowuje, Grafana rysuje i alarmuje. To rozdzielenie sprawia, że możesz wymienić jedno bez drugiego - i że aplikacja nie zna żadnego z nich.

Podsumowanie

Wieże sygnałowe stoją, ruch jest mierzony:

  • logi zapisują zdarzenia, metryki pokazują trendy - tego drugiego nie widać w żadnym pojedynczym wpisie,
  • Prometheus zbiera i przechowuje metryki; nie loguje błędów ani niczego nie naprawia,
  • cztery typy od najprostszego: Counter (tylko rośnie), Gauge (rośnie i maleje), Histogram (rozkład w kubełkach), Summary (percentyle liczone po stronie klienta),
  • do czasu trwania żądań HTTP właściwy jest Histogram - pokazuje kształt, którego średnia nie zdradzi,
  • metrykę definiują trzy pola:
    name
    ,
    help
    ,
    labelNames
    ,
  • etykiety pozwalają ciąć dane, ale muszą mieć mały zbiór wartości - identyfikator użytkownika jako etykieta położy Prometheusa,
  • collectDefaultMetrics()
    zbiera metryki systemowe
    - CPU, pamięć, opóźnienie event loopu; niczego nie wysyła,
  • Prometheus sam przychodzi po dane (scraping), więc wystawiasz endpoint
    /metrics
    z
    Content-Type
    ustawionym na tekst zwykły przez
    register.contentType
    ,
  • pięć etapów: instalacja
    prom-client
    , zdefiniowanie metryk, endpoint
    /metrics
    , konfiguracja scrapera, wizualizacja w Grafanie,
  • Prometheus zbiera, Grafana rysuje i alarmuje.

W następnej lekcji zejdziemy z poziomu trendów do pojedynczego błędu - poznasz techniki debugowania, gdy wiadomo już, że coś jest nie tak, ale nie wiadomo gdzie. A na razie zapamiętaj: log mówi, co się stało raz; metryka mówi, co dzieje się stale - i to ona ostrzega, zanim padnie brama.

Przejdź do CodeWorlds