Monitoring dan logging adalah mata dan telinga sistem kamu. Tanpa keduanya, kamu buta terhadap masalah sampai user yang melaporkan.
Tiga pilar observability:
1. Logs — apa yang terjadi Record detail kejadian di sistem. Berguna untuk debugging.
// Structured logging (JSON) — lebih mudah di-search dan analyze
const log = {
timestamp: "2024-01-15T10:30:00Z",
level: "error",
service: "order-service",
message: "Payment failed",
userId: 42,
orderId: "ORD-123",
error: "Insufficient balance",
duration_ms: 234
};
Log levels:
| Level | Kapan Dipakai |
|---|---|
| DEBUG | Info detail untuk development |
| INFO | Kejadian normal (user login, order created) |
| WARN | Sesuatu yang perlu diperhatikan (disk 80%, retry) |
| ERROR | Error yang perlu ditangani (payment gagal) |
| FATAL | Sistem crash, harus segera ditindak |
2. Metrics — angka-angka performa
// Metrics yang harus dimonitor:
- Request rate: 500 req/sec
- Error rate: 0.1%
- Response time (p50, p95, p99): 50ms, 200ms, 500ms
- CPU usage: 45%
- Memory usage: 60%
- Database connections: 15/100
- Queue depth: 42 messages pending
RED Method (untuk services):
- Rate — berapa request per detik
- Error — berapa persen yang error
- Duration — berapa lama response time
USE Method (untuk infrastructure):
- Utilization — seberapa sibuk resource
- Saturation — antrian yang menunggu
- Errors — error count
3. Traces — perjalanan request Melacak satu request yang melewati banyak service (distributed tracing):
Request ID: abc-123
├── API Gateway (2ms)
├── Auth Service (15ms)
├── Order Service (45ms)
│ ├── Database query (20ms)
│ └── Cache lookup (1ms)
├── Payment Service (200ms) ← bottleneck!
│ └── External API call (190ms)
└── Total: 262ms
Alerting — jangan hanya monitor, bertindak!
# Contoh alert rules:
alerts:
- name: High Error Rate
condition: error_rate > 1% for 5 minutes
severity: critical
action: page on-call engineer
- name: Slow Response Time
condition: p95_latency > 2s for 10 minutes
severity: warning
action: send Slack notification
- name: Database Connection Pool Full
condition: db_connections > 90%
severity: critical
action: page DBA + auto-scale
Tools:
| Kategori | Tools |
|---|---|
| Logging | ELK Stack, Loki + Grafana, Datadog |
| Metrics | Prometheus + Grafana, Datadog, New Relic |
| Tracing | Jaeger, Zipkin, Datadog APM |
| Alerting | PagerDuty, OpsGenie, Grafana Alerts |
| All-in-one | Datadog, New Relic, Sentry |
Best practices:
- Log semua request masuk dan keluar (dengan request ID untuk korelasi)
- Jangan log data sensitif (password, token, PII)
- Set alert yang actionable — jangan alert untuk hal yang tidak perlu tindakan
- Monitor business metrics juga (sign-ups, orders, revenue), bukan hanya teknis
- Buat runbook untuk setiap alert — apa yang harus dilakukan saat alert muncul
Distributed Tracing Deep Dive
Saat request melewati 10 service, log biasa tidak cukup. Kamu butuh distributed tracing — pelacakan satu request end-to-end antar proses.
Trace, Span, dan Struktur Pohon
Trace (abc-123) ← satu request end-to-end
├─ Span: "GET /api/dashboard" (220ms) ← operasi di API Gateway
│ ├─ Span: "auth.verify" (12ms)
│ └─ Span: "order.getUserOrders" (180ms)
│ ├─ Span: "db.query users" (8ms)
│ └─ Span: "db.query orders" (160ms) ← bottleneck terlihat jelas
└─ Span: "cache.warm" (30ms)
- Trace — keseluruhan perjalanan request.
- Span — satu unit pekerjaan (HTTP call, DB query, internal operation).
- Parent/child relationship — membentuk pohon yang menunjukkan causality.
OpenTelemetry — Standard Industri
OpenTelemetry (OTel) adalah standard yang menggantikan jaringan library proprietary (Zipkin format, Jaeger format, dll). Sekali instrument aplikasi dengan OTel SDK, data trace bisa dikirim ke backend mana pun: Jaeger, Tempo, Honeycomb, Datadog, New Relic.
Instrumentasi umum (Node.js):
const { trace } = require("@opentelemetry/api");
const tracer = trace.getTracer("order-service");
app.get("/orders", async (req, res) => {
const span = tracer.startSpan("getOrders");
try {
span.setAttribute("user.id", req.user.id);
const orders = await db.query("SELECT ..."); // akan auto-instrument DB call
res.json(orders);
} finally {
span.end();
}
});
Correlation ID / Trace ID
Setiap request dapat trace ID unik (mis. UUID) yang di-forward ke setiap downstream service via HTTP header. Semua log, metric, dan span yang dihasilkan request itu di-tag dengan trace ID yang sama.
Hasilnya: saat ada error di service keenam, kamu bisa filter semua log dari semua service dengan trace_id=abc-123 dan merekonstruksi kejadian lengkap.
W3C Trace Context — Format Header Standar
Dulu tiap vendor punya header sendiri (X-B3-TraceId, X-Datadog-Trace-Id, dll). Sekarang ada standard W3C Trace Context:
traceparent: 00-{trace-id-32-hex}-{span-id-16-hex}-{flags}
tracestate: vendor1=value1,vendor2=value2
Contoh real:
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
Semua service modern (gRPC, Envoy, AWS SDK, fetch client) support header ini secara default. Propagasi jadi otomatis.
Sampling Strategies
Mencatat 100% trace mahal — kalau sistem handle 1 juta req/s, trace lengkap = terabytes per jam. Solusi: sampling.
- Head-based sampling — keputusan sampling dibuat di awal trace (di entry point). Contoh: sample 1% request, atau 100% untuk endpoint
/checkout. Murah tapi bisa miss error event (karena error belum terdeteksi di awal). - Tail-based sampling — tunggu sampai trace selesai, lalu putuskan apakah simpan atau buang. Bisa sample 100% error dan 1% sukses. Lebih akurat, tapi butuh buffer memory untuk trace yang sedang berlangsung.
Tail-based biasanya diimplementasi di collector (mis. OTel Collector) bukan di aplikasi.
Tools Populer
| Tool | Jenis | Catatan |
|---|---|---|
| Jaeger | Open source (CNCF) | Matang, banyak deployment |
| Tempo | Grafana | Storage murah di object store, integrasi Grafana |
| Honeycomb | SaaS | High-cardinality query, observability-first |
| Datadog APM | SaaS | All-in-one (metrics + logs + traces) |
| AWS X-Ray | Managed AWS | Integrasi tight dengan AWS services |
Prinsip universal: correlation ID di setiap log + trace context di setiap call = kamu bisa menjawab "apa yang terjadi di request pukul 14:23:45?" dalam hitungan menit, bukan jam.
🎭 Analogi sehari-hari: Operasi kapal pesiar.
- Logs = catatan kapten ("16:00 mesin no. 2 berisik")
- Metrics = dashboard di anjungan (kecepatan, BBM, posisi)
- Traces = pelacakan rute kapal end-to-end (Singapura → Jakarta → Surabaya)
- Alerts = sirine kalau anjungan kehilangan kontrol
Tanpa observability = nakhoda buta. Tahu kapal lambat tapi gak tahu kenapa.
💡 "You can't fix what you can't see": Production bug paling mahal = bug yang gak terdeteksi. User komplain, kamu gak tau. Itu kondisi paling buruk. Observability = membalik dari "user yang ngabarin masalah" jadi "kamu yang tau duluan".
⚠️ Jebakan klasik:
- Log everything = lupa privacy — password, token, PII di log = compliance nightmare
- Alert fatigue — alert untuk segala hal = abaikan semua alert. Set actionable threshold
- Cuma log technical metric — lupa business metric (orders/day, signups/hour, revenue). Bisnis stakeholder care
- No log retention policy = log meledak storage cost
- Monitor production tapi tidak staging = bug naik ke prod tanpa peringatan
🎯 Observability checklist minimal:
- Structured logging (JSON) di semua service
- Trace ID propagation lewat HTTP header
- RED metrics (Rate, Error, Duration) untuk tiap service
- USE metrics (Utilization, Saturation, Errors) untuk infra
- Actionable alerts dengan runbook
- Dashboard untuk on-call (1-glance situational awareness)
- Error tracking (Sentry, Rollbar) terintegrasi
TL;DR: Logs (apa terjadi) + Metrics (angka) + Traces (perjalanan request) = observability. Wajib di production. OpenTelemetry standard. Trace ID propagation kritikal di microservices. Set alert actionable, jangan noise.