Belajar Chi - Observability & Monitoring
Series/Belajar Chi/Episode 18
Episode 18 of 23

Belajar Chi - Observability & Monitoring

Episode ini membuat aplikasi terlihat: metrics Prometheus sebagai middleware, tracing dengan OpenTelemetry, dan structured logs dengan slog. Kalian juga memakai middleware.Heartbeat untuk health check serta menyusun dashboard dan alerting untuk latency, error rate, dan throughput.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Aplikasi yang berjalan tanpa penglihatan adalah aplikasi yang tidak bisa dioperasikan. Episode 18 menghubungkan router chi ke tiga pilar observability: metrics, traces, dan logs. Ketiganya memberi jawaban atas pertanyaan yang selalu muncul di produksi — lambat di mana, error berapa banyak, dan request berapa tinggi. Karena chi murni net/http, seluruh ekosistem observability Go menempel tanpa adaptor. Kita mulai dari Prometheus, lalu OpenTelemetry, dan menutup dengan health check.

Metrics Prometheus

Middleware Metrics

Ekspos metrik HTTP melalui middleware:

Install client Prometheus
go get github.com/prometheus/client_golang/prometheus/promhttp
Metrics middleware
func prometheusMetrics(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, req)
        httpDuration.WithLabelValues(
            req.URL.Path, req.Method,
        ).Observe(time.Since(start).Seconds())
    })
}

httpDuration.WithLabelValues(path, method).Observe(...) mencatat durasi per rute dan method. Histogram ini menjadi dasar perhitungan latency percentiles di dashboard.

Ekspos Endpoint

Endpoint /metrics
r := chi.NewRouter()
r.Use(prometheusMetrics)
 
r.Get("/metrics", func(w http.ResponseWriter, req *http.Request) {
    promhttp.Handler().ServeHTTP(w, req)
})

promhttp.Handler().ServeHTTP(w, req) melayani output Prometheus pada path /metrics. Pastikan endpoint ini dilindungi atau hanya dapat diakses internal.

Koleksi dengan Prometheus

Konfigurasi scrape
scrape_configs:
  - job_name: chi-service
    static_configs:
      - targets: ["localhost:8080"]

targets: ["localhost:8080"] memberitahu Prometheus untuk menarik metrik dari aplikasi setiap interval scrape.

OpenTelemetry

Tracing Terdistribusi

OpenTelemetry menangkap jejak request antar service:

Install OpenTelemetry
go get go.opentelemetry.io/otel
Middleware tracing
func otelMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        ctx, span := tracer.Start(req.Context(),
            req.Method+" "+req.URL.Path)
        defer span.End()
 
        span.SetAttributes(
            attribute.String("http.method", req.Method),
            attribute.String("http.target", req.URL.Path),
        )
        next.ServeHTTP(w, req.WithContext(ctx))
    })
}

tracer.Start(req.Context(), name) membuka span baru untuk setiap request. Span membawa method dan path sebagai atribut — menghubungkan latensi ke penyebabnya. Saat request diteruskan ke service lain, header propagasi dibawa oleh http.Client, sehingga jejak lintas service tersambung dalam satu trace tree di backend seperti Jaeger atau Tempo.

Structured Logs

Satu Aliran Log

Kombinasikan slog dengan metrik untuk konteks lengkap:

Logging terstruktur
func requestLogger(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, req)
        slog.Info("http_request",
            "method", req.Method,
            "path", req.URL.Path,
            "latency", time.Since(start).Milliseconds(),
        )
    })
}

slog.Info("http_request", "method", req.Method, "latency", ...) menulis satu baris JSON per request. Latency dalam milidetik siap dikirim ke agregator log seperti Loki. Tambahkan trace ID sebagai atribut ("trace_id", traceID) agar log dan span OpenTelemetry terbaca dalam satu utas pencarian saat request bermasalah.

Health Check dengan Heartbeat

Middleware Bawaan

Health check bawaan
r.Use(middleware.Heartbeat("/healthz"))

middleware.Heartbeat("/healthz") membalas OK dengan status 200 pada path /healthz tanpa melibatkan handler lain. Probes Kubernetes dan load balancer memakai endpoint ini untuk memutuskan kapan request dikirim.

Readiness vs Liveness

  • Liveness (/healthz): server hidup? Bila gagal, pod di-restart.
  • Readiness (/readyz): siap menerima traffic? Bila gagal, traffic dihentikan sementara.

Untuk readiness, tambahkan cek dependency seperti database dengan pool.Ping(req.Context()) — server tidak dianggap siap sampai koneksi database sehat, dan load balancer tidak akan mengirim traffic saat dependency bermasalah.

Dashboard dan Alerting

Metrik yang Dipantau

Tiga metrik utama yang selalu dipantau:

  • Latency: percentiles p95 dan p99 dari histogram durasi.
  • Error rate: persentase respons 5xx dari total request.
  • Throughput: jumlah request per detik (RPS).

Aturan Alerting

Alert error rate
groups:
  - name: chi-alerts
    rules:
      - alert: HighErrorRate
        expr: rate(http_requests_total{code=~"5.."}[5m]) > 0.05
        labels:
          severity: critical
        annotations:
          summary: "Error rate melebihi 5 persen"

rate(http_requests_total{code=~"5.."}[5m]) > 0.05 memicu alert saat error 5xx melebihi 5 persen selama 5 menit. Dashboard Grafana menampilkan tren latency dan throughput dari metrik yang sama.

Penutup

Inti yang harus dibawa pulang:

  • Prometheus metrics middleware mencatat durasi per rute.
  • Endpoint /metrics dilayani dengan promhttp.
  • OpenTelemetry span menghubungkan jejak lintas service.
  • slog menulis log JSON terstruktur yang siap di-agregasi.
  • middleware.Heartbeat menyediakan health check instan.
  • Dashboard dan alerting berputar pada latency, error rate, dan throughput.

Di episode 19 selanjutnya kita memoles performa: performance dan troubleshooting — pola rute yang efisien, tuning http.Server, connection pooling, caching dengan Redis, serta solusi masalah umum seperti route conflict, panic handler, deadlock context, dan kebocoran goroutine.