Középhaladó Olvasási idő: ~8 perc

Monitoring

actuator, health checks, Micrometer, logging, distributed tracing

Monitoring

A jó monitoring nem csak azt mondja meg, hogy baj van, hanem azt is, hogy pontosan hol és miért kezdett romlani a rendszer.

1. Definíció / Definition

Mi ez? / What is it?

A monitoring a Spring alkalmazás operációs láthatósága: health state, metrikák, logok és trace-ek együttese. Nem egyetlen library, hanem egy megfigyelhetőségi réteg, amit tipikusan Spring Boot Actuator, Micrometer, logback alapú logging és distributed tracing eszközök adnak össze.

Miért létezik? / Why does it exist?

Azért, mert productionben nem az a kérdés, hogy lesz-e hiba, hanem az, hogy milyen gyorsan veszed észre, mennyire jól lokalizálod, és tudsz-e bizonyíték alapján dönteni. Monitoring nélkül a csapat találgat: lassú-e a DB, elszállt-e egy downstream service, vagy csak rossz log level maradt bekapcsolva.

Hol helyezkedik el? / Where does it fit?

A monitoring a runtime és az operáció metszetében ül. Fejlesztőként a kódban adsz ki metrikát, request contextet és health információt; üzemeltetés oldalról ezt Prometheus, Grafana, Zipkin vagy Jaeger olvassa és vizualizálja.

2. Alapfogalmak / Core Concepts

2.1 Spring Boot Actuator alap endpointok

Az Actuator a legegyszerűbb belépő. Tipikus endpointok:

Endpoint Mire jó? Tipikus használat
/actuator/health alkalmazás állapot readiness/liveness, load balancer
/actuator/metrics összes elérhető metrika gyors debug, metric discovery
/actuator/prometheus Prometheus scrape formátum monitoring stack integráció
/actuator/info build/app infó verzió, commit hash
/actuator/env environment property view config debug, óvatos exposure
/actuator/loggers runtime log level kezelés incident közbeni finomhangolás

Nem minden endpointot kell kitenni public hálózatra. Sőt, általában nem is szabad.

2.2 Health vs readiness vs liveness

A health önmagában sokszor kevés. Kubernetes környezetben érdemes külön gondolkodni:

Hasznos health dimenziók:

  • Liveness — arra válaszol, hogy él-e még a process.

  • Readiness — arra válaszol, hogy tud-e most forgalmat fogadni a service.

  • Health details / custom checks — a függőségek, például DB, broker vagy cache állapotát mutatja.

  • Liveness: a process nincs beragadva, nem halt meg.

  • Readiness: a service most tényleg tud kiszolgálni kérést.

  • Custom dependency health: például Redis, RabbitMQ, külső API állapota.

Egy DB outage lehet readiness hiba, de nem feltétlen liveness hiba. Ez fontos különbség.

2.3 Micrometer mint metrics abstraction

A Micrometer nem maga a monitoring backend, hanem egy absztrakció. Ugyanazzal az API-val küldhetsz metrikát Prometheus, Datadog vagy más rendszer felé.

Legfontosabb típusaid:

  • Counter: csak növekszik, pl. sikeres rendelések száma.
  • Gauge: pillanatnyi érték, pl. queue méret.
  • Timer: időtartam és hívásszám együtt, pl. API latency.
  • DistributionSummary: eloszlás típusú mérés, pl. payload méret.

2.4 Logging és MDC

A log nem csak debug stringek gyűjteménye. Jó log esetén kérdésre válaszolsz: mi történt, melyik requestben, melyik usernél, melyik trace-ben.

Az MDC (Mapped Diagnostic Context) request-scoped adatot tesz a log contextbe:

  • traceId
  • spanId
  • requestId
  • tenantId
  • userId ha üzletileg indokolt és nem érzékeny adat

2.5 Distributed tracing

Régebben gyakran Spring Cloud Sleuth volt a default, ma inkább Micrometer Tracing az irány Spring Boot 3 környékén. A cél ugyanaz: végigkövetni egy request útját több service-en át.

Tipikus trace útvonal:

  1. A kérés a Gatewayen keresztül indul.
  2. Ugyanaz a trace ID kíséri végig az Order Service, Payment Service és Notification Service hívásláncot.
  3. Minden szolgáltatás vagy művelet saját span ID-t kap.
  • Trace ID: a teljes kéréslánc azonosítója.
  • Span ID: egy konkrét művelet vagy hívás azonosítója.

2.6 Log, metric, trace együtt értékes

A három jel külön-külön hasznos, de együtt erős:

  • a metric megmutatja, hogy romlik valami;
  • a trace megmutatja, melyik hívási lánc a lassú;
  • a log megmutatja a konkrét üzleti és technikai kontextust.

3. Gyakorlati használat / Practical Usage

Productionben az első reális use case a health endpointok helyes kialakítása. Például egy rendelési service induláskor még migrációt futtat, cache-t tölt, kapcsolódik a DB-hez. Ilyenkor ne vegyen még forgalmat, tehát readiness legyen DOWN, miközben a JVM simán él, tehát liveness maradhat UP.

A második tipikus eset a latency romlás követése. Van egy /checkout endpointod, ami normálisan 120 ms körül fut, de peak időben felmegy 2 másodpercre. Egy jól elnevezett Timer megmutatja a p95 és p99 latenciát, Grafanában látszik a trend, a trace pedig megmondja, hogy a késés a payment callban vagy a DB queryben jött.

A harmadik gyakorlati use case a runtime logging finomhangolás. Incident alatt nem akarsz új deployt csak azért, hogy egy package log levelét INFO-ról DEBUG-ra rakd. Az Actuator /loggers endpointtal ezt runtime tudod állítani, de kontrolláltan, auditálható módon.

A negyedik valós scenario a multi-service debugging. Egy felhasználó azt mondja, hogy a fizetés sikeres volt, de e-mail nem jött. Ha van trace propagation, ugyanazzal a traceId-val végig tudod követni a gateway, order, payment és notification service logjait. Ez nagyságrendi gyorsulás incidenskezelésben.

4. Kód példák / Code Examples

4.1 Actuator és Prometheus kiexponálása

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus,loggers
  endpoint:
    health:
      probes:
        enabled: true
      show-details: when_authorized
  metrics:
    tags:
      application: order-service

4.2 Custom HealthIndicator

@Component
public class InventoryApiHealthIndicator implements HealthIndicator {

    private final InventoryClient inventoryClient;

    public InventoryApiHealthIndicator(InventoryClient inventoryClient) {
        this.inventoryClient = inventoryClient;
    }

    @Override
    public Health health() {
        try {
            boolean reachable = inventoryClient.ping();
            if (reachable) {
                return Health.up()
                        .withDetail("dependency", "inventory-api")
                        .withDetail("latencyMs", 42)
                        .build();
            }
            return Health.down()
                    .withDetail("dependency", "inventory-api")
                    .withDetail("reason", "ping returned false")
                    .build();
        } catch (Exception ex) {
            return Health.down(ex)
                    .withDetail("dependency", "inventory-api")
                    .build();
        }
    }
}

4.3 Micrometer timer és counter

@Service
public class CheckoutService {

    private final Counter checkoutSuccessCounter;
    private final Timer checkoutTimer;

    public CheckoutService(MeterRegistry registry) {
        this.checkoutSuccessCounter = Counter.builder("checkout.success.total")
                .description("Successful checkout operations")
                .register(registry);
        this.checkoutTimer = Timer.builder("checkout.duration")
                .publishPercentileHistogram()
                .register(registry);
    }

    public OrderResult placeOrder(OrderCommand command) {
        return checkoutTimer.record(() -> {
            OrderResult result = doPlaceOrder(command);
            checkoutSuccessCounter.increment();
            return result;
        });
    }

    private OrderResult doPlaceOrder(OrderCommand command) {
        return new OrderResult(command.orderId(), "ACCEPTED");
    }
}

record OrderCommand(String orderId) {}
record OrderResult(String orderId, String status) {}

4.4 MDC filter request contexttel

@Component
public class RequestContextLoggingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            MDC.put("requestId", Optional.ofNullable(request.getHeader("X-Request-Id"))
                    .orElse(UUID.randomUUID().toString()));
            MDC.put("path", request.getRequestURI());
            filterChain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }
}

5. Trade-offok / Trade-offs

Előnyök

  • gyorsabb incident detection és diagnózis;
  • business és technikai SLA jobban mérhető;
  • runtime debugging deploy nélkül is javulhat;
  • capacity planninghez valódi adatot kapsz.

Költségek és árnyoldalak

  • túl sok metrika magas cardinality problémát okoz;
  • túl részletes DEBUG log drága és zajos;
  • a trace mintavételezés kompromisszum a költség és a részletesség között;
  • rosszul védett Actuator endpoint security kockázat.

Mikor használd erősen? Microservice, kritikus üzleti folyamat, SLO/SLA környezet, Kubernetes, event-driven rendszerek.

Mikor ne ess túlzásba? Kis belső toolnál nem kell rögtön 200 egyedi metric és teljes tracing mesh. Az observability is lehet overengineered.

6. Gyakori hibák / Common Mistakes

6.1 Mindent kiexponálni Actuatorból

Az env, beans, configprops típusú endpointok sokat segíthetnek debugban, de érzékeny infót is felfedhetnek. Mindig limitáld, mi érhető el és honnan.

6.2 High-cardinality tag-ek használata

Ne tegyél userId, email, teljes URL query string vagy random request ID értéket metric tagbe. Ez megöli a backendet és használhatatlanná teszi az aggregációt.

6.3 A health check túl sokat csinál

A health endpoint ne futtasson fél alkalmazásnyi üzleti logikát. Egy health check legyen gyors, olcsó és determinisztikus. Ha túl komplex, maga is hibaforrás lesz.

6.4 Logolás érzékeny adattal

Token, jelszó, bankkártyaadat, személyes adat logba írása klasszikus production hiba. A “majd később kiszedjük” általában azt jelenti, hogy bent marad.

6.5 Trace van, de nincs propagáció

Ha az egyik service generál trace-et, a másik meg nem viszi tovább a headeröket, a teljes lánc szétesik. A tracing csak akkor működik jól, ha end-to-end gondolkodsz.

7. Senior szintű meglátások / Senior-level Insights

Senior szemmel a monitoring nem dashboard-gyűjtés, hanem döntéstámogató rendszer. Olyan metrikát érdemes mérni, amihez akció tartozik. Ha senki nem nézi, nincs alert rá, és nem segít diagnózisban, akkor lehet, hogy csak zaj.

A jó health modell üzleti szemantikát is tükröz. Egy recommendation service lehet részben degradált, de még hasznos. Egy ledger service viszont nem “félig egészséges”: ott sokkal szigorúbb readiness logika kell. Nem minden dependency ugyanolyan fontosságú.

A log level runtime állítás erős fegyver, de veszélyes is. Érdemes guardrail: csak belső hálózatból, auth mögött, auditáltan. Incident közben könnyű túl sok logot bekapcsolni, aztán IO bottleneckkel még rontani a helyzeten.

A metrikákat naming és tagging policy alapján vezesd be. Ha minden team saját stílust használ, pár hónap múlva kereshetetlen lesz a rendszer. A monitoringnál a konzisztencia legalább annyira fontos, mint maga a technológia.

8. Szószedet / Glossary

  • Actuator: Spring Boot modul operációs endpointokkal.
  • HealthIndicator: komponens, ami egy dependency vagy belső állapot egészségét jelenti.
  • Micrometer: metrics facade különböző backendekhez.
  • Counter: monoton növekvő számláló.
  • Timer: időtartam és hívásszám mérésére való eszköz.
  • MDC: thread-local log context kulcs-érték párokkal.
  • Trace ID: egy teljes request chain azonosítója.
  • Span ID: egyetlen művelet vagy szakasz azonosítója egy trace-en belül.
  • Prometheus: metrics gyűjtő és lekérdező rendszer.
  • Grafana: dashboard és vizualizációs platform.

9. Gyorsreferencia / Cheatsheet

Téma Jó default Mire figyelj
Actuator exposure csak szükséges endpointok security és hálózati korlátozás
Health check gyors és olcsó ne legyen üzleti workflow
Counter eseményszám ne decrementáld
Gauge pillanatnyi állapot stabil objektumhoz kösd
Timer latency mérés histogram és percentilisek
Logging strukturált, contextes PII tiltás
MDC request/trace context async határokon propagáció
Tracing traceId + spanId sampling és backend költség
Runtime loggers incidentre hasznos auth és audit kell

🎮 Játékok

8 kérdés