This episode covers observability for production: distributed tracing with OpenTelemetry, centralized logging and correlation IDs, production-ready metrics, health, and alerting, as well as incident response, error tracing, and service troubleshooting.

Episodes 9 and 21 made your application healthy and deployed. But production is where the unexpected happens: latency spikes, occasional errors, one service complaining about another. To survive, you need production-grade observability — not just a health check.
Episode 22 covers advanced observability: distributed tracing with OpenTelemetry, centralized logging with correlation IDs, production-ready metrics, health, and alerting, as well as incident response, error tracing, and service troubleshooting.
OpenTelemetry is the modern observability standard. Quarkus integrates with it natively:
./mvnw quarkus:add-extension \
-Dextensions=opentelemetry,opentelemetry-exporter-otlpSend traces to a collector or a backend like Jaeger, Tempo, or Grafana Cloud:
quarkus.opentelemetry.tracer.exporter.otlp.endpoint=http://collector:4317
quarkus.opentelemetry.tracer.sampler=on
quarkus.application.name=belajar-quarkusquarkus.opentelemetry.tracer.exporter.otlp.endpoint points to the OpenTelemetry Collector, which forwards traces to the backend. With the on sampler, every request is traced.
HTTP requests are traced automatically. For custom spans inside a service:
import io.opentelemetry.api.trace.Span;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class PaymentService {
public void prosesPembayaran() {
Span span = Span.current().makeCurrent();
span.setAttribute("payment.method", "transfer");
// process the payment
span.end();
}
}Distributed tracing follows a single request across many services: each service adds a span, and the backend assembles them into one complete trace.
When a request crosses many services, you need to connect the logs from all of them. A correlation ID (or trace ID) links everything together. With OpenTelemetry, the trace ID is automatically available.
Add the trace ID to your logs via configuration:
quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss} %-5p [%c] trace=%X{traceId} %s%e%n%X{traceId} reads the MDC value for the trace ID. Now every log line carries the trace ID, and you can find all the logs belonging to a single request.
Send logs to a centralized system like Loki, Elasticsearch, or CloudWatch. Structured JSON logging configuration (episode 9) makes parsing easier. An example of sending to Loki:
quarkus.log.handler.gelf.enabled=true
quarkus.log.handler.gelf.host=loki-gateway
quarkus.log.handler.gelf.port=12201Or export logs through an agent like Promtail on the cluster side. The important thing: all logs from all services gather in one queryable place.
Combine metrics (episode 9) and health (episode 9) as the foundation of monitoring:
curl http://localhost:8080/q/metrics
curl http://localhost:8080/q/healthPrometheus pulls /q/metrics periodically; Kubernetes uses /q/health/live and /q/health/ready for probes.
Good alerts catch problems before users notice them:
groups:
- name: quarkus
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
/ sum(rate(http_server_requests_seconds_count[5m])) > 0.05
for: 10m
labels:
severity: page
annotations:
summary: Error rate di atas 5% selama 10 menitThis rule fires an alert if 5xx errors exceed 5% for 10 minutes. Alerts are routed to PagerDuty, Slack, or email according to severity.
When an alert fires, the response must be structured:
1. Recognize: check dashboards and alerts to confirm
2. Isolate: identify the service and time range
3. Trace: query traces by trace ID and related logs
4. Recover: rollback, restart, or scale
5. Learn: postmortem and long-term fixesWhen a user reports an error, grab the trace ID from the report (or find it via logs), then:
curl "http://jaeger/query?service=belajar-quarkus&operation=POST"Use the trace to see which requests failed, which service was problematic, and the duration of each span. Query via the API curl "http://jaeger/query?service=belajar-quarkus" or through the Jaeger UI. The trace ID is the bridge between user reports and root causes.
If an endpoint is slow, the trace shows which span consumes the time — a slow database query, an external call, or a queue. The combination of trace + metrics + log (the three pillars of observability) gives a complete answer without guessing.
Episode 22 prepares you for production: understanding distributed tracing with OpenTelemetry, centralized logging with correlation IDs, production-ready metrics, health, and alerting, as well as trace-based incident response and troubleshooting.
Key takeaways:
/q/metrics and /q/health are the foundation of production monitoring.In episode 23, the final episode of the series, we'll cover stable modern features and future trends — the latest stable Quarkus features, modern Quarkus 3.x capabilities, the SmallRye, Camel, Kafka, gRPC, and Kubernetes ecosystem, as well as strategies to keep your Quarkus applications future-proof and cloud-native.