Kodeloka
  1. Kodeloka
  2. Kelas Grafana
  3. Materi

Modul 12 ยท Modul 12 dari 15

Korelasi: Loki, Tempo, Exemplar, dan Explore

Menghubungkan metrik, log, dan trace sehingga investigasi berpindah dari "grafik naik" ke "baris kode yang salah" dalam beberapa klik.

Nilai Sebenarnya Grafana Ada di Sini

Dashboard metrik memberi tahu bahwa ada masalah. Log memberi tahu apa yang terjadi. Trace memberi tahu di mana waktunya habis. Grafana adalah satu-satunya tempat ketiganya bisa dijelajahi dengan time range yang sama.

๐ŸŽฏ Alur investigasi yang ideal
  1. Alert berbunyi: p99 checkout melewati 3 detik.
  2. Buka dashboard, lihat lonjakan mulai 14:32.
  3. Anotasi menunjukkan deploy pada 14:30. Tersangka pertama.
  4. Klik satu titik pada grafik โ†’ exemplar โ†’ trace request yang lambat.
  5. Trace menunjukkan 2,8 detik dihabiskan di satu kueri database.
  6. Dari span itu, klik ke log Loki yang punya trace_id sama.
  7. Log menunjukkan kueri tanpa indeks pada kolom baru dari deploy tadi.

Tujuh langkah, tanpa SSH ke satu server pun. Inilah yang perlu dikonfigurasi di bab ini.

LogQL โ€” Cukup untuk Dipakai Sehari-hari

logqltext
// 1. stream selector โ€” WAJIB, dan wajib selektif
{layanan="ledger", env="produksi"}

// 2. filter baris
{layanan="ledger"} |= "error"              // mengandung
{layanan="ledger"} != "healthcheck"        // tidak mengandung
{layanan="ledger"} |~ "timeout|refused"    // regex
{layanan="ledger"} !~ "debug.*"            // regex negatif

// 3. parser โ€” ekstrak field dari baris
{layanan="ledger"} | json                       // log JSON
{layanan="ledger"} | logfmt                     // format key=value
{layanan="ledger"} | pattern " - - <_> \"  <_>\""
{layanan="ledger"} | regexp "durasi=(?P[0-9.]+)"

// 4. filter label hasil parsing
{layanan="ledger"} | json | status >= 500 | durasi > 1.5

// 5. format keluaran
{layanan="ledger"} | json | line_format "{{.method}} {{.uri}} -> {{.status}}"

// 6. metrik dari log โ€” inilah yang membuat Loki kuat
sum by (status) (rate({layanan="ledger"} | json [5m]))
sum(count_over_time({layanan="ledger"} |= "error" [5m]))
quantile_over_time(0.95,
  {layanan="ledger"} | json | unwrap durasi [5m]) by (route)
โ›” Kardinalitas di Loki jauh lebih mematikan daripada di Prometheus

Setiap kombinasi label unik di Loki membuat stream terpisah, dan setiap stream punya chunk sendiri. Label seperti trace_id, user_id, atau request_id akan menciptakan jutaan stream dan melumpuhkan Loki.

text
โŒ {layanan="api", trace_id="abc123", user_id="4821"}
โœ… {layanan="api", env="produksi", level="error"}
   lalu cari trace_id di dalam ISI baris: |= "abc123"

Filosofi Loki: index kecil, isi baris di-scan. Label hanya untuk hal berkardinalitas rendah; semua yang bervariasi tinggi tetap di dalam teks log dan dicari lewat filter.

Derived Fields: Dari Log Melompat ke Trace

provisioning/datasources/loki.ymlyaml
datasources:
  - name: Loki
    uid: loki-utama
    type: loki
    access: proxy
    url: http://loki:3100
    jsonData:
      maxLines: 1000
      derivedFields:
        - name: TraceID
          matcherRegex: 'trace_id=(\w+)'
          url: '${__value.raw}'
          datasourceUid: tempo-utama
          urlDisplayLabel: 'Lihat trace'
        - name: OrderID
          matcherRegex: 'order_id=(\w+)'
          url: 'https://admin.internal/pesanan/${__value.raw}'

Hasilnya: setiap baris log yang mengandung trace_id=abc123 mendapat tombol "Lihat trace" yang langsung membuka Tempo.

Exemplar: Dari Metrik Melompat ke Trace

Exemplar adalah contoh request yang dilekatkan pada sebuah sampel histogram, lengkap dengan trace_id. Ia muncul sebagai titik kecil berlian pada grafik.

prometheus.yml โ€” aktifkan exemplaryaml
global:
  scrape_interval: 15s

storage:
  exemplars:
    max_exemplars: 100000     # WAJIB, default 0 = fitur mati
data source Prometheusyaml
jsonData:
  exemplarTraceIdDestinations:
    - name: trace_id
      datasourceUid: tempo-utama
      urlDisplayLabel: 'Lihat trace'

Di sisi aplikasi, exemplar harus ikut diekspos โ€” pada client Prometheus modern ini berarti mengaktifkan format OpenMetrics dan menyertakan trace_id saat mencatat observasi histogram.

Trace to Logs dan Logs to Metrics

provisioning/datasources/tempo.ymlyaml
datasources:
  - name: Tempo
    uid: tempo-utama
    type: tempo
    access: proxy
    url: http://tempo:3200
    jsonData:
      tracesToLogsV2:
        datasourceUid: loki-utama
        spanStartTimeShift: '-5m'
        spanEndTimeShift: '5m'
        tags: [{ key: 'service.name', value: 'layanan' }]
        filterByTraceID: true
        filterBySpanID: false
      tracesToMetrics:
        datasourceUid: prom-utama
        spanStartTimeShift: '-2m'
        spanEndTimeShift: '2m'
        tags: [{ key: 'service.name', value: 'layanan' }]
        queries:
          - name: 'Request rate layanan ini'
            query: 'sum(rate(http_requests_total{layanan="$${__tags.layanan}"}[5m]))'
      serviceMap:
        datasourceUid: prom-utama
      nodeGraph:
        enabled: true
๐Ÿ’ก spanStartTimeShift bukan detail sepele

Sebuah span berlangsung 300 ms, tetapi log yang relevan mungkin tercatat beberapa detik sebelum atau sesudahnya. Tanpa pergeseran waktu, pencarian log yang dipicu dari span sering mengembalikan nol hasil dan orang menyimpulkan "korelasinya tidak jalan".

Correlations: Menghubungkan Data Source Apa Pun

Fitur Correlations membuat aturan lompatan yang lebih umum daripada derived fields โ€” dari data source apa pun ke data source apa pun, dengan variabel yang diambil dari hasil kueri.

contoh correlationtext
Sumber : Prometheus, field "instance"
Target : Loki
Kueri  : {instance="${instance}"} |= "error"
Label  : "Log dari instance ini"

Explore Split View โ€” Alat Investigasi Utama

Tekan Split di Explore untuk membuka dua panel bersebelahan dengan time range tersinkron: metrik di kiri, log di kanan. Menggeser rentang di satu sisi menggeser keduanya. Dalam praktik, inilah tempat sebagian besar debugging benar-benar terjadi โ€” bukan di dashboard.

Log Panel di Dashboard

panel logjson
{
  "type": "logs",
  "title": "Error terbaru โ€” $layanan",
  "datasource": { "type": "loki", "uid": "loki-utama" },
  "targets": [{
    "refId": "A",
    "expr": "{layanan=~\"$layanan\", env=\"$env\"} | json | level=\"error\"",
    "maxLines": 100
  }],
  "options": {
    "showTime": true,
    "wrapLogMessage": true,
    "prettifyLogMessage": true,
    "enableLogDetails": true,
    "dedupStrategy": "exact",
    "sortOrder": "Descending"
  }
}

dedupStrategy sangat berguna: exact melipat baris identik yang berulang, sehingga satu error yang terjadi 5.000 kali tidak menenggelamkan yang lain.