Belajar Microservices - OpenTelemetry: Tracing, Metric, Log
Episode 20 of 28

Belajar Microservices - OpenTelemetry: Tracing, Metric, Log

Mencatat jejak observability end-to-end di tokokita: distributed tracing dengan span dan propagasi traceparent, metrik RED per endpoint, log JSON terstruktur dengan request_id dan service, serta backend Prometheus, Grafana, Tempo/Jaeger, dan Loki

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

Pendahuluan

Setelah sekian episode membangun arsitektur yang rumit, jujur saja: ini episode yang paling terasa manfaatnya saat produksi. Observability adalah jawaban atas pertanyaan paling mahal di microservices: "di mana request saya tersesat, kenapa lambat, dan di mana errornya?" Di monolith cukup stack trace; di microservices, satu request melewati gateway, product, order, payment, Kafka, sampai notifikasi — tak ada satu pun layanan yang tahu keseluruhan ceritanya.

Mengapa episode ini penting? Karena tanpa observability, semua arsitektur yang kita bangun hanyalah utang yang menumpuk — kamu tidak akan pernah tahu layanan mana yang rusak sampai user mengeluh. OpenTelemetry menyatukan trace, metrics, logs di bawah satu standar terbuka.

Distributed Tracing

Span: Satuan Pekerjaan

OpenTelemetry menamai setiap unit kerja sebagai span: "HTTP POST /orders", "reserve stock", "consume order-events", "SMTP send". Span punya nama, start/end time, attribute, dan relasi parent-child. Rangkaian span yang saling terkait untuk satu request adalah trace, diidentifikasi trace_id (satu trace = satu logika request lintas layanan).

Membuat span di handler Elysia
import { trace } from '@opentelemetry/api'
 
const tracer = trace.getTracer('order-service')
 
export const createOrderHandler = async (req) => {
  const span = tracer.startSpan('POST /orders', { attributes: { http.method: 'POST' } })
  const ctx = trace.setSpan(trace.active(), span)
  try {
    const order = await createOrderWithContext(req, ctx)
    span.setStatus({ code: 1 }) // OK
    return order
  } finally {
    span.end() // catat durasi akurat walau error
  }
}

Propagasi traceparent: Jembatan antar Layanan

Agar trace menyambung, setiap hop harus meneruskan header traceparent (00-{trace_id}-{span_id}-{flags}):

Propagasi via HTTP & Kafka
web-app ──x-request-id + traceparent──→ gateway
gateway ──traceparent──────────────→ order-service
order-service ──traceparent (message header)──→ kafka producer
kafka consumer (notification) ──traceparent──→ SMTP
satu trace_id sepanjang perjalanan

Saat memproduksi event, salin trace context ke header Kafka agar konsumen meneruskan trace yang sama — inilah propogasi lintas transport. Dengan selesainya propagasi, kita bisa melihat satu request melintasi auth → order → payment → kafka → notif dalam satu waterfall di Tempo/Jaeger.

Metrics: Pola RED

Metrics adalah angka agregat yang bisa di-alert. Untuk service API, pola yang paling banyak dipakai adalah RED:

  • Rate — jumlah request per detik.
  • Errors — request yang gagal (5xx / exceptions).
  • Duration — latency, biasanya histogram: p50, p95, p99.
Histogram latency per endpoint
import { metrics } from '@opentelemetry/api'
 
const meter = metrics.getMeter('api-gateway')
const latency = meter.createHistogram('http.server.duration', {
  unit: 'ms',
  description: 'Latency per request',
})
 
// di onAfterHandle: latency.record(durationMs, {
//   http.route, http.method, http.status_code
// })

Histogram menyimpan distribusi — bisa dihitung p99 kapan pun oleh Prometheus. Label (route, status) dipakai untuk agregasi: "endpoint mana yang lambat?" dijawab langsung.

Structured Logs

Log JSON terstruktur lebih berharga daripada teks bebas. Standar field tokokita: timestamp, level, service, request_id, trace_id, span_id, event payload.

Logger JSON terstruktur
export function logger(base: { service: string }) {
  return {
    info: (msg: string, fields: Record<string, unknown> = {}) =>
      console.log(JSON.stringify({ ts: new Date().toISOString(), level: 'info', service: base.service, msg, ...fields })),
    error: (msg: string, fields: Record<string, unknown> = {}) =>
      console.error(JSON.stringify({ ts: new Date().toISOString(), level: 'error', service: base.service, msg, ...fields })),
  }
}
// dipakai: logger({ service: 'order-service' }).error('reserve failed',
//   { requestId, traceId: span.spanContext().traceId, orderId })

Saat semua layanan memakai skema yang sama, Loki bisa meng-agregasi: "tampilkan semua log ber-request_id X" — bahkan lintas layanan. Inilah yang membuat debugging distributed terasa seperti debugging monolith.

Backend Observability (2026)

Stack open-source yang dipakai tokokita:

DataBackendTugas
MetricsPrometheusscrape dari /metrics tiap service; simpan time-series
Dashboard/AlertGrafanavisualisasi + alert rules
TracesTempo (atau Jaeger)simpan & query trace waterfall
LogsLokilog terpusat dengan label

Arsitektur pengumpulannya: tiap service mengekspos /metrics (Prometheus) dan mengirim trace/log via OTLP. Di skala besar, OpenTelemetry Collector menjadi proxy penerima semua sinyal — dan berbagai managed backend (Grafana Cloud) tinggal menyambung.

Ekspose metrics di service
# akses grafana: http://localhost:3000  (admin/admin)

Verifikasi: Satu Request Utuh

Buat satu order, lalu di Grafana/Tempo:

  1. Trace: waterfall POST /api/orders → 4-6 span (gateway, order, reserve, kafka publish, notif consume) — satu trace_id.
  2. Metrics: http.server.duration menunjukkan p99 POST /orders.
  3. Log: {request_id: "..."} di Loki menampilkan semua log lintas service untuk request yang sama.

Tip

Ketiga pilar observability itu saling melengkapi, bukan tumpang tindih: metrics menjawab "ada yang lambat?" (alert, dashboard), traces menjawab "di hop mana?" (per-request), logs menjawab "detailnya apa?" (konteks program). Di incident, urutan yang benar biasanya: metric → trace → log. Latih alur ini — inilah basis runbook di episode 24.

Penutup

Episode 20 melengkapi tokokita dengan mata & telinga:

  • Tracing: span per operasi; propagasi traceparent lintas HTTP dan Kafka; satu trace_id end-to-end.
  • Metrics RED: Rate, Errors, Duration (histogram p50/p95/p99) per endpoint.
  • Structured logs JSON: service, request_id, trace_id, span_id yang seragam.
  • Backend: Prometheus + Grafana (metric), Tempo/Jaeger (trace), Loki (log); OTel Collector di tengah.
  • Alur insiden yang benar: metric → trace → log.

Di episode 21 selanjutnya, kita membahas scaling: KEDA, HPA, dan kapasitas — autoscale berdasarkan CPU/request, scaling worker berbasis consumer lag, pola scaling per tipe, dan capacity planning request vs limit per service. Sampai jumpa di episode 21!

Belajar Microservices - OpenTelemetry: Tracing, Metric, Log | Belajar Microservices