BelajarKoding Logobelajarkoding

Platform belajar web development Indonesia. Artikel, cheat sheets, roadmap, dan code challenges untuk developer Indonesia.

Navigasi

  • Artikel
  • Cheat Sheets
  • Roadmap
  • Challenges
  • Pricing
  • Search

Produk Lain

  • JagoHermes
  • KelasClaude
  • KilatKoding
  • BelajarVibeCoding
  • JualanKoding

Support

  • Privacy Policy
  • Terms of Service
  • Email

© 2026 BelajarKoding. All rights reserved.

Galih PratamaBagian dari ekosistem Galih Pratama
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade

Daftar Isi

Observability vs MonitoringTiga SinyalStructured LoggingKenapa Format JSONLevel Log dan Kapan DipakaiLibrary per BahasaRequest ID dan CorrelationAturan Konten LogLoki StackArsitektur dan KomponenDesain Label: KardinalitasLogQL: Filter dan ParseLogQL: Metric QueryRetensiLoki vs ELKMetrics dan PromQLTipe Metric PrometheusPromQL yang Sering DipakaiRecording RulesMenyambungkan OpenTelemetryDashboardsGolden Signals, RED, dan USEPraktik Dashboard GrafanaAlerting RulesAnatomi Rule PrometheusRouting AlertmanagerAnti Alert NoiseSLO dan Error BudgetSLI, SLO, SLAHitung Error BudgetBurn Rate AlertSLO yang RealistisChecklist ImplementasiGlossary
ObservabilityDevOpsMonitoringLogging

Observability, Logging, dan Monitoring Cheat Sheet

Referensi cepat observability: structured logging, Loki stack, metrics Prometheus, dashboard Grafana, alerting rules, dan SLO dengan error budget. Perfect buat DevOps engineer.

YAML13 min read2.547 kata
Silakan login atau daftar untuk membaca cheat sheet ini.

#Observability vs Monitoring

Monitoring adalah mengumpulkan data terstruktur lalu bertindak saat threshold terlampaui. Observability adalah properti sistem: seberapa baik kamu bisa menjawab pertanyaan baru tentang sistem dari output yang dihasilkannya, tanpa deploy ulang atau tambah logging baru.

AspekMonitoringObservability
Pertanyaan"Apakah sistem sehat?""Kenapa sistem tidak sehat?"
DataMetrik utamanyaMetrik, log, dan trace bersamaan
SifatThreshold dan alarmEksploratif saat debugging
Waktu pakaiSebelum dan saat insidenSaat mencari akar masalah

#Tiga Sinyal

SinyalMenjawabContoh pertanyaan
MetricsSeberapa sering dan seberapa parah"Error rate checkout berapa persen?"
LogsApa yang terjadi persisnya"Request ord_123 kenapa gagal?"
TracesDi mana waktunya habis"Checkout lambat gara-gara service mana?"

Ketiganya saling melengkapi. Alert datang dari metrics, investigasi dimulai dari trace, dan detail kronologi ada di log. Buat detail instrumentasi OpenTelemetry (SDK, span, propagation, sampler), lihat cheat sheet /cheat-sheets/opentelemetry.

#Structured Logging

#Kenapa Format JSON

Log teks bebas itu enak dibaca manusia, tapi susah diquery. Log JSON bisa diparse jadi field: level, message, user_id, duration_ms. Saat insiden, kamu filter level=error AND service=checkout dalam hitungan detik, bukan grep manual satu per satu di dua puluh server.

plaintext
# Susah diquery
2026-09-19 10:15:32 ERROR payment failed for user 42
 
# Siap diquery dan diparse
{"ts":"2026-09-19T10:15:32Z","level":"error","msg":"payment_failed","user_id":42,"order_id":"ord_123","amount":150000,"duration_ms":842,"service":"checkout"}

#Level Log dan Kapan Dipakai

LevelMaknaContoh
TRACEAlur internal detailHanya untuk development lokal
DEBUGDiagnostik teknisQuery SQL yang jalan, matikan di produksi
INFOEvent bisnis normalOrder dibuat, request selesai
WARNAnomali yang pulih sendiriRetry sukses setelah gagal sekali
ERROROperasi gagal, butuh perhatianPembayaran gagal, pesan queue ditolak
FATALAplikasi tidak bisa lanjutKonfigurasi hilang, koneksi database awal gagal

Aturan praktisnya begini: ERROR berarti "kalau ini terus terjadi, seseorang harus turun tangan". Kalau ERROR di kode kamu cuma jadi catatan dan nggak ada yang peduli, turunkan ke WARN. Jangan bikin orang on-call trauma karena alert ERROR yang tidak butuh aksi.

#Library per Bahasa

pino (Node.js), paling cepat dan default di banyak proyek:

javascript
const pino = require("pino");
 
const logger = pino({
  level: process.env.LOG_LEVEL || "info",
  base: { service: "checkout-api", env: process.env.NODE_ENV },
});
 
logger.info({ user_id: 42, order_id: "ord_123", duration_ms: 120 }, "order_completed");

structlog (Python):

python
import structlog
 
logger = structlog.get_logger()
logger.info("order_completed", user_id=42, order_id="ord_123", duration_ms=120)

zap (Go):

go
import "go.uber.org/zap"
 
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("order_completed",
    zap.Int("user_id", 42),
    zap.String("order_id", "ord_123"),
)

Konsistensi nama field itu wajib: user_id semua, jangan campur userId, UserID, dan user. Tool query tidak akan bisa menggeneralisasi kalau skema tiap service beda.

#Request ID dan Correlation

Setiap request dapat ID unik yang dibawa ke semua log di semua service yang dilalui. Log dengan request_id sama bisa direkonstruksi jadi kronologi lengkap. Kalau kamu sudah pakai OpenTelemetry, propagasikan trace_id dan taruh di setiap baris log; itu kunci menghubungkan log dengan trace.

javascript
// Middleware Express: satu child logger per request
app.use((req, res, next) => {
  req.log = logger.child({ request_id: crypto.randomUUID() });
  next();
});
 
app.get("/orders/:id", (req, res) => {
  req.log.info({ order_id: req.params.id }, "get_order");
  res.json(getOrder(req.params.id));
});

#Aturan Konten Log

  • Jangan pernah log password, token, API key, atau nomor kartu. Kalau payload memang harus dilog, mask field sensitif sebelum serialisasi.
  • Jangan log seluruh body request. Simpan field yang benar-benar dipakai buat debugging.
  • Satu event satu baris. Stack trace tetap satu event; pino dan structlog sudah menanganinya dengan benar.
  • Tulis timestamp di UTC format ISO 8601 biar tidak bingung zona waktu.

#Loki Stack

#Arsitektur dan Komponen

Loki adalah log aggregation buatan Grafana. Bedanya dengan Elasticsearch: Loki hanya mengindeks label, bukan seluruh isi log. Isi log dikompresi jadi chunk di object storage. Hasilnya biaya storage jauh lebih murah, cocok buat tim yang nggak mau bangun cluster log mahal.

Komponen utamanya:

  • loki: server utama (index + chunk storage)
  • Grafana Alloy: agent pengumpul log modern, pengganti Promtail yang statusnya deprecated sejak 2024
  • Grafana: UI query dan dashboard

Konfigurasi Alloy untuk kumpulkan log container Docker:

yaml
discovery.docker "containers" {
  host = "unix:///var/run/docker.sock"
}
 
loki.source.docker "containers" {
  host       = "unix:///var/run/docker.sock"
  targets    = discovery.docker.containers.targets
  forward_to = [loki.write.default.receiver]
}
 
loki.write "default" {
  endpoint {
    url = "http://loki:3100/loki/api/v1/push"
  }
}

Untuk kasus simpel, ada juga Docker logging driver resmi:

bash
docker plugin install loki/docker-driver:latest --alias loki
docker run --log-driver=loki \
  --log-opt loki-url="http://loki:3100/loki/api/v1/push" \
  --log-opt labels="service" \
  -l service="checkout-api" \
  nginx:1.27

#Desain Label: Kardinalitas

Label itu index, jadi jumlahnya sedikit dan nilainya terbatas: cluster, env, service, job. Jangan pernah pakai user_id, request_id, atau email sebagai label. Nilainya tak terbatas (high cardinality) dan bikin index Loki membengkak sampai tidak bisa dipakai. Data seperti itu tetap ada di dalam baris log, tinggal difilter sebagai konten.

plaintext
# Benar: nilai label sedikit dan tetap
{cluster="prod-1", env="prod", service="checkout"}
 
# Salah: user_id dan trace_id jangan jadi label
{user_id="42", trace_id="abc123def"}

#LogQL: Filter dan Parse

plaintext
# Stream selector plus filter kata
{service="checkout"} |= "error"
 
# Filter regex lalu parse isi JSON
{service="api"} |~ "timeout|deadline" | json
 
# Akses field hasil parse
{service="api"} | json | level="error" | line_format "{{.msg}}"
 
# Parse format logfmt lalu bandingkan angka
{service="gateway"} | logfmt | status >= 500

Urutan operasinya penting: selector label dulu (murah, pakai index), filter baris kemudian (scan), parse terakhir. Ini kebalikan dari kebiasaan grep: di Loki, makin sempit stream selector makin cepat query kamu.

#LogQL: Metric Query

Log bisa dihitung jadi metrik langsung dari Loki:

plaintext
# Baris error per service per detik
sum by (service) (rate({env="prod"} |= "error" [5m]))
 
# Latensi p99 dari field duration_ms
quantile_over_time(0.99,
  {service="api"} | json | unwrap duration_ms [1m]
) by (service)

Trik ini berguna buat service lama yang belum ekspor metrik tapi sudah log JSON terstruktur.

#Retensi

yaml
limits_config:
  retention_period: 168h   # 7 hari; naikkan untuk kebutuhan audit
compactor:
  retention_enabled: true

Log 30 hari di object storage itu murah; log 30 hari di SSD itu mahal. Kalau ada kebutuhan audit panjang, arsipkan ke S3 atau GCS, jangan panjangkan retensi aktif sembarangan.

#Loki vs ELK

AspekLokiELK (Elasticsearch)
IndexingHanya labelFull-text semua field
Biaya storageRendah, kompresi tinggiTinggi
Pencarian full-text bebasTerbatas, linear scanKuat
SetupRingan, satu binary plus object storageBerat, cluster tersendiri
Cocok untukTim kecil sampai menengah, budget hematPencarian lanjutan, audit kompleks

Kalau kebutuhanmu "filter cepat per service lalu baca lognya", Loki cukup dan lebih murah. Kalau butuh agregasi bebas di semua field dan pencarian full-text kompleks, ELK masih menang.

#Metrics dan PromQL

#Tipe Metric Prometheus

TipeSifatContoh
CounterNaik monoton, reset saat restartTotal request, total error
GaugeNaik turun bebasMemori aktif, ukuran queue
HistogramDistribusi dalam bucketLatensi request per bucket
SummaryQuantile dihitung sisi klienLatensi p95 langsung dari app

Untuk latensi, pilih histogram: bisa diagregasi lintas instance dan quantile-nya dihitung saat query. Summary tidak bisa dijumlah antar instance.

#PromQL yang Sering Dipakai

promql
# Request per detik
rate(http_requests_total[5m])
 
# Rasio error 5xx
sum(rate(http_requests_total{status=~"5.."}[5m]))
  / sum(rate(http_requests_total[5m]))
 
# Latensi p95 dari histogram
histogram_quantile(0.95,
  sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)
 
# Memori kerja per pod dalam GB
container_memory_working_set_bytes{container!=""}
  / (1024 * 1024 * 1024)

#Recording Rules

Query agregasi berat yang dipakai berulang sebaiknya disimpan jadi time series baru:

yaml
groups:
  - name: http
    interval: 30s
    rules:
      - record: job:http_error_ratio:rate5m
        expr: |
          sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))
            / sum by (job) (rate(http_requests_total[5m]))

Dashboard dan alert jadi lebih cepat karena agregasi sudah dihitung duluan secara berkala, bukan diulang setiap kali ada query.

#Menyambungkan OpenTelemetry

Pipeline observability modern biasanya begini: aplikasi pakai SDK OpenTelemetry, kirim data ke OTel Collector, Collector meneruskan ke backend masing-masing sinyal. Kode aplikasi tidak perlu tahu backend apa yang dipakai, jadi backend bisa ditukar tanpa deploy ulang.

plaintext
app (OTel SDK) -> OTel Collector --> Prometheus / Mimir (metrics)
                                   |-> Loki (logs)
                                   |-> Tempo / Jaeger (traces)

Detail SDK, span, context propagation, sampling, dan konfigurasi Collector ada di cheat sheet /cheat-sheets/opentelemetry.

#Dashboards

#Golden Signals, RED, dan USE

Empat golden signals dari buku Google SRE: latency, traffic, errors, saturation. Dua kerangka turunannya:

  • RED untuk service: Rate, Errors, Duration
  • USE untuk resource: Utilization, Saturation, Errors

Dashboard service pakai RED; dashboard infrastruktur (node, disk, network) pakai USE.

#Praktik Dashboard Grafana

  • Dashboard per service, bukan per server. Satu server mati dari lima bukan insiden kalau traffic dilayani sisanya; error rate naik baru insiden.
  • Urutan panel standar: traffic, error ratio, latensi p95 dan p99, lalu saturasi (CPU, memori, disk).
  • Pakai dashboard variables untuk service dan environment supaya satu template dipakai semua tim.
  • Set threshold visual sesuai SLO. Panel error ratusan jangan tampil hijau kalau targetmu 99.9%.
  • Jangan menumpuk 50 panel. Dashboard yang tidak dibaca siapa pun tandaannya kebanyakan; potong.

#Alerting Rules

#Anatomi Rule Prometheus

yaml
groups:
  - name: checkout-alerts
    rules:
      - alert: CheckoutHighErrorRate
        expr: |
          sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))
            / sum by (job) (rate(http_requests_total[5m])) > 0.05
        for: 10m
        labels:
          severity: page
          team: payments
        annotations:
          summary: "Error rate {{ $labels.job }} di atas 5%"
          runbook_url: "https://runbooks.internal/checkout-high-error-rate"

Peran tiap field:

  • expr: kondisi boolean; alert aktif saat nilainya true
  • for: berapa lama kondisi harus bertahan sebelum firing; mencegah alarm dari spike sesaat
  • labels: dipakai Alertmanager untuk routing
  • annotations: pesan yang dibaca on-call; selalu sertakan link runbook

#Routing Alertmanager

yaml
route:
  receiver: email-default
  group_by: [alertname, job]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - matchers: [severity="page"]
      receiver: pagerduty
    - matchers: [severity="ticket"]
      receiver: jira
 
inhibit_rules:
  - source_matchers: [severity="page"]
    target_matchers: [severity="ticket"]
    equal: [job]

group_by menggabungkan alert serupa jadi satu notifikasi. Inhibition menutup alert severity rendah kalau alert severity tinggi dengan konteks sama sudah menyala, jadi on-call tidak kebanjiran notifikasi duplikat.

#Anti Alert Noise

  • Alert berbasis gejala yang dirasakan user (error rate, latensi), bukan sebab (CPU 80%). Sebab ada banyak dan sering tidak berdampak ke user.
  • Setiap alert page harus punya aksi konkret. Kalau satu-satunya tindakan on-call adalah "nunggu sampai baikan sendiri", turunkan jadi ticket.
  • Review daftar alert tiap bulan. Alert yang tidak pernah firing atau selalu di-silence, hapus aja atau perbaiki.
  • Kalau sudah ada SLO, alert langsung ke error budget (lihat bawah), bukan threshold mentah.

#SLO dan Error Budget

#SLI, SLO, SLA

  • SLI (Service Level Indicator): indikator terukur, contohnya rasio request sukses atau latensi di bawah 300 ms.
  • SLO (Service Level Objective): target internal untuk SLI dalam periode waktu, misal 99.9% request sukses dalam 30 hari.
  • SLA (Service Level Agreement): kontrak dengan pelanggan beserta konsekuensinya (kredit servis, refund). SLA hampir selalu lebih longgar dari SLO supaya ada ruang aman.

#Hitung Error Budget

Error budget adalah 100% dikurangi SLO: jumlah gangguan yang boleh kamu pakai dalam satu periode.

SLODowntime maksimal per 30 hari
99%7 jam 12 menit
99.5%3 jam 36 menit
99.9%43 menit 12 detik
99.99%4 menit 19 detik

Untuk service berbasis request, hitung dari error ratio. SLO 99.9% berarti maksimal 0.1% request boleh gagal. Kalau traffic 10 juta request per bulan, 10.000 request boleh gagal. Angka itu error budget kamu.

Budget dipakai buat keputusan, bukan pajangan. Sisa masih banyak: rilis agresif boleh jalan. Sudah terkuras 80%: tunda rilis fitur, fokus perbaikan reliabilitas. Aturan main seperti ini yang bikin SLO punya gigi, bukan cuma angka di slide.

#Burn Rate Alert

Burn rate adalah laju konsumsi budget dibanding laju normal. Burn 1 berarti budget habis tepat jatah di akhir periode. Burn 14.4 berarti 2% budget terkuras dalam 1 jam. Pola multiwindow multi-burn-rate dari Google SRE Workbook:

Burn rateWindow panjangWindow pendekAksi
14.41 jam5 menitPage
66 jam30 menitPage
31 hari2 jamTicket
13 hari6 jamTicket

Dua window dipakai bersamaan supaya alert hanya menyala kalau masalah benar-benar berlanjut, bukan sekadar spike tiga menit.

yaml
- alert: ErrorBudgetFastBurn
  expr: |
    (
      sum(rate(http_requests_total{status=~"5.."}[1h]))
        / sum(rate(http_requests_total[1h])) > 14.4 * 0.001
    )
    and
    (
      sum(rate(http_requests_total{status=~"5.."}[5m]))
        / sum(rate(http_requests_total[5m])) > 14.4 * 0.001
    )
  for: 2m
  labels:
    severity: page
  annotations:
    summary: "Error budget terkuras cepat (fast burn)"
    runbook_url: "https://runbooks.internal/error-budget-burn"

Angka 0.001 di expr adalah 1 dikurangi SLO (99.9%). Kalau SLO kamu 99.5%, ganti dengan 0.005. Konstanta burn rate-nya tidak berubah.

#SLO yang Realistis

  • Mulai dari 2 atau 3 SLI per service: availability, latensi, mungkin satu yang spesifik domain (persentase order sukses diproses).
  • Ukur dari sisi yang dirasakan user, misal error ratio di gateway, bukan health check internal yang selalu hijau padahal user gagal login.
  • Jangan buru-buru mengejar 99.99%. Tiap tambahan sembilan memotong error budget jadi sepuluh kali lebih kecil. Tim tiga orang dengan 99.99% akan menghabiskan hidupnya memadamkan alert.
  • Review SLO tiap kuartal bersama tim produk. SLO terlalu longgar tidak melindungi user; terlalu ketat tidak realistis operasionalnya.

#Checklist Implementasi

  • Log JSON terstruktur dengan level dan request_id di semua service
  • Nama field log konsisten lintas service
  • Label Loki rendah kardinalitas: env, service, job
  • Metrik RED terekspor: rate, error, duration
  • Dashboard per service dengan golden signals dan variables
  • Alert berbasis gejala dengan runbook yang bisa diikuti
  • SLO didefinisikan dan disepakati bersama tim produk
  • Burn rate alert terpasang, bukan cuma threshold CPU

#Glossary

  • Observability: kemampuan menjawab pertanyaan baru tentang sistem dari outputnya (log, metric, trace) tanpa perlu deploy ulang.
  • Telemetry: data yang dikirim aplikasi tentang kondisi dan aktivitas dirinya sendiri.
  • SLI: indikator terukur kesehatan service, misal rasio request sukses.
  • SLO: target internal untuk satu SLI dalam periode waktu tertentu.
  • SLA: janji kontraktual ke pelanggan lengkap dengan konsekuensi kalau dilanggar.
  • Error budget: sisa toleransi kegagalan dalam satu periode SLO; 100% dikurangi nilai SLO.
  • Burn rate: kecepatan konsumsi error budget relatif terhadap jatah normal.
  • Kardinalitas: jumlah nilai unik sebuah label; makin tinggi makin besar ukuran index.
  • LogQL: bahasa query Loki untuk filter dan agregasi log.
  • PromQL: bahasa query Prometheus untuk membaca time series.
  • Recording rule: hasil query PromQL yang disimpan berkala jadi time series baru.
  • Trace: jejak perjalanan satu request lintas service, tersusun dari span.
  • Span: satu unit kerja di dalam trace, misal satu panggilan HTTP atau satu query database.
  • Scrape: aktivitas Prometheus menarik metrik dari endpoint /metrics secara berkala.
  • Inhibition: mekanisme Alertmanager menutup alert lain ketika alert yang lebih berat dengan konteks sama sudah menyala.
  • Golden signals: empat sinyal kesehatan service dari Google SRE: latency, traffic, errors, saturation.
  • RED: kerangka metrik untuk service, singkatan Rate, Errors, Duration.
  • USE: kerangka metrik untuk resource, singkatan Utilization, Saturation, Errors.
  • Distroless: kelas image container tanpa shell dan package manager, cuma berisi runtime aplikasi.

Baca Cheat Sheet Lengkap

Login atau daftar akun gratis untuk membaca cheat sheet ini.

LoginDaftar Gratis
Share: