Skip to content

Angka di Dashboard Bisa Basi Tanpa Terlihat

Adityo Guni Waluyo

Nilai terakhir di dashboard bisa basi tanpa terlihat. Cara saya nambahin staleness badge fresh/aging/dead dan aggregate avg/min/max yang ngikutin tab range.

Ringkasan

Dashboard nunjukin CPU 3% padahal server udah mati tujuh jam, soalnya cuma render data terakhir tanpa umur datanya. Solusinya kasih badge fresh/aging/dead berdasarkan usia data dibanding poll interval, jadi kelihatan langsung datanya masih bisa dipercaya atau nggak. Pelajaran lain: parsing timestamp zona lokal kayak WIB rentan meledak, mending simpan dan bandingin dalam UTC.

Saya lagi lihat dashboard monitoring server lama, CPU nunjuk 3%. Angka kecil, artinya server sehat, pikir saya waktu itu. Tapi pas saya SSH ke server, systemctl status bilang service udah inactive (dead) dari jam 4 pagi. Sekarang jam 11. Tujuh jam server mati, dashboard tetap tenang menampilkan angka yang sama.

Yang terjadi: collector terakhir kali ngirim data jam 4 pagi, dan dashboard cuma render nilai terakhir yang diterima tanpa nandain kapan data itu dikumpulkan. Angka 3% memang benar pada jam 4 pagi. Tapi sekarang angka itu sudah basi, dan dashboard nggak kasih tahu.

Tiga Bucket: fresh, aging, dead

Problem ini bukan problem baru. Prometheus udah lama menyelesaikannya di lapisan query: time series cuma di-return kalau sample paling baru masih dalam rentang lookback period [1]. Nagios Core punya freshness check sejak versi lama: kalau umur last check result melewati freshness threshold, hasil itu dianggap stale dan Nagios memaksa active check ulang [3]. Dua sistem beda, satu ide yang sama: umur data lebih penting daripada nilai datanya.

Versi saya cukup tiga bucket berdasarkan umur data dibanding poll interval yang di-set 180 detik:

def staleness_status(checked_at, now, poll_interval=180):
    """fresh/aging/dead based on data age vs poll interval."""
    if not checked_at:
        return "dead"
    age = (now - _parse_ts(checked_at)).total_seconds()
    if age <= 2 * poll_interval:
        return "fresh"
    if age <= 5 * poll_interval:
        return "aging"
    return "dead"

Multiplier 2x dan 5x bukan konstanta universal, itu pilihan lokal saya: fresh sampai 6 menit, aging sampai 15 menit, selebihnya dead. Yang penting konsepnya: data punya usia, dan usia itu harus dibandingkan dengan seberapa sering data seharusnya datang. Nagios bahkan menyarankan threshold-nya di-set eksplisit, bukan dihitung otomatis dari interval monitoring [3].

Di frontend, hasil bucket ini jadi badge kecil di hero detail server lengkap dengan umur data dalam menit. Badge bilang fresh, saya nggak usah mikir. Badge bilang aging atau dead, saya langsung tahu datanya nggak bisa dipercaya, tanpa harus buka terminal dan cocokkan jam manual.

Rata-rata, Min, Maks yang Ikut Tab

Masalah kedua masih satu keluarga: angka yang tampil nggak nyambung dengan konteks yang dilihat. Kartu metrik dulu cuma nunjukin delta yang selalu dihitung dari rentang 24 jam, nggak peduli user lagi pilih tab 1 jam atau 7 hari. Label bilang 24 jam padahal layar lagi nunjukin rentang lain. Kontradiktif.

Sekarang kartu-kartu yang fluktuatif, yaitu cpu_used_pct, disk_read_kbps, disk_written_kbps, net_rx_kbps, dan net_tx_kbps, dapat aggregate avg/min/max dari window yang sedang dilihat. Delta label-nya juga ngikutin tab range yang dipilih. Tab 1 jam, delta dibanding jam sebelumnya. Tab 7 hari, delta dibanding minggu sebelumnya.

Avg doang sebenarnya bisa menyesatkan untuk metrik yang loncat-loncat. Peak 800 Mbps dengan rata-rata 120 Mbps itu dua cerita yang sangat beda, dan tanpa min/max cuma satu yang keliatan. Untuk network throughput, min/max bukan hiasan, dia justru angka yang paling sering saya cari pas ada keluhan lemot.

Timestamp WIB yang Nggak Portable

Satu jebakan lagi yang nyangkut di commit yang sama: parsing timestamp systemd. Format display systemd pake zona lokal, misalnya Fri 2012-11-23 23:02:15 CET sesuai dokumentasinya [2]. Kalau saya parse string itu di mesin yang zonanya juga sama, aman, karena parser default ke zona lokal. Tapi systemd sendiri ngingatkan: timestamp dari remote system dengan zona yang beda biasanya nggak bisa diparse di mesin lain, karena komponen zona nggak dikenali kecuali UTC [2]. WIB nggak ada di daftar zona yang dikenal parser standar.

Solusinya handle eksplisit: kenalin abbreviation zona lokal, map ke offset, convert ke UTC sebelum disimpan. Nggak elegan, tapi satu-satunya cara yang nggak meledak pas collector dan viewer jalan di mesin dengan zona berbeda. Pelajaran umumnya: jangan pernah parsing timestamp ber-zona lokal sebagai string mentah, simpan dan bandingkan dalam UTC, tampilkan dalam zona pembaca.

Nilai terakhir tanpa umur data itu dusta. Angka di dashboard harus tampil bareng umurnya, berapa menit sejak data itu dikumpulkan. Tanpa itu, 3% CPU bisa berarti server sehat, atau bisa berarti server udah mati tujuh jam dan saya baru sadar sekarang.

Sumber

  1. Querying basics — Prometheus Documentation. "Time series are only returned if their most recent sample is less than the lookback period ago." Diakses 2026-09-16.
  2. systemd.time(7) — Linux man-pages. "The timezone defaults to the current timezone if not specified explicitly." dan "timestamps displayed by remote systems with a non-matching timezone are usually not parsable locally". Diakses 2026-09-16.
  3. Host and Service Freshness Checks — Nagios Core Documentation. "If the age of the last check result is greater than the freshness threshold, the check result is considered stale." Diakses 2026-09-16.

Artikel terkait