Metric

Counter

Counter adalah tipe metrik yang digunakan untuk mencatat nilai yang selalu bertambah (increment) seiring waktu. Counter biasanya digunakan untuk menghitung kejadian atau aktivitas yang bersifat kumulatif, seperti jumlah request yang dilayani, jumlah task yang diselesaikan, atau jumlah error yang terjadi.

Karakteristik utama Counter:

  • Hanya bertambah: Nilai Counter hanya akan meningkat (atau tetap sama jika tidak ada aktivitas baru).

  • Reset tergantung implementasi:

    • Dalam Prometheus, Counter direset ketika aplikasi atau proses di-restart.

  • Counter berguna untuk melacak frekuensi kejadian atau menghitung total akumulasi dari suatu peristiwa.

Berikut adalah beberapa contoh penggunaan Counter dalam observability:

  1. Jumlah Permintaan yang Dilayani (Number of Requests Served)

    • Konteks: Sebuah aplikasi web ingin mencatat berapa kali endpoint API-nya diakses oleh pengguna.

    • Metrik Counter: api_requests_total

    • Cara Kerja: Setiap kali ada permintaan HTTP ke endpoint, Counter bertambah 1. Misalnya, jika ada 100 permintaan dalam satu jam, nilai Counter menjadi 100. Jika ada 50 permintaan lagi, nilai Counter menjadi 150.

    • Reset: Dalam Prometheus, jika server aplikasi di-restart, nilai Counter kembali ke 0.

  2. Tugas yang Diselesaikan (Tasks Completed)

    • Konteks: Sebuah sistem pengolahan batch (misalnya, sistem pengiriman email) mencatat jumlah email yang berhasil dikirim.

    • Metrik Counter: emails_sent_total

    • Cara Kerja: Setiap kali sebuah email berhasil dikirim, Counter bertambah 1. Misalnya, setelah mengirim 500 email, Counter menunjukkan nilai 500.

    • Reset: Sama seperti sebelumnya, reset tergantung pada sistem (StatsD: setiap flush, Prometheus: saat restart).

  3. Kesalahan yang Dilaporkan (Errors Reported)

    • Konteks: Sebuah aplikasi mencatat jumlah error 500 (Internal Server Error) yang terjadi.

    • Metrik Counter: http_500_errors_total

    • Cara Kerja: Setiap kali server mengembalikan error 500, Counter bertambah 1. Jika ada 10 error dalam sehari, nilai Counter menjadi 10.

    • Reset: Bergantung pada implementasi (StatsD atau Prometheus).

Mari kita ilustrasikan dengan contoh kasus Jumlah Permintaan API menggunakan Counter dalam Prometheus.

Skenario

Sebuah aplikasi web memiliki endpoint /api/users yang sering diakses. Kita ingin memantau berapa banyak permintaan yang masuk ke endpoint ini. Kita menggunakan metrik Counter bernama api_requests_total.

Contoh

  • Pada pukul 08:00, aplikasi mulai berjalan (Counter = 0).

  • Dari pukul 08:00 hingga 08:30, ada 200 permintaan ke /api/users (Counter = 200).

  • Dari pukul 08:30 hingga 09:00, ada tambahan 300 permintaan (Counter = 500).

  • Pada pukul 09:05, server di-restart (Counter direset ke 0).

  • Dari pukul 09:05 hingga 09:30, ada 150 permintaan (Counter = 150).

Visualisasi

Untuk mengilustrasikan perubahan nilai Counter, berikut adalah grafik yang menunjukkan nilai api_requests_total dari waktu ke waktu.

Penjelasan Grafik

  • Sumbu X: Waktu (dari 08:00 hingga 09:30).

  • Sumbu Y: Nilai Counter (api_requests_total).

  • Grafik menunjukkan kenaikan nilai Counter dari 0 ke 200, lalu ke 500, diikuti oleh reset ke 0 saat server di-restart pada pukul 09:05, dan kemudian naik lagi ke 150.

  • Grafik ini menggambarkan sifat Counter yang hanya bertambah, kecuali saat direset oleh restart aplikasi (dalam kasus Prometheus).

  • Penggunaan Counter: Counter sangat cocok untuk menghitung kejadian yang bersifat kumulatif, tetapi untuk analisis lebih lanjut (misalnya, laju permintaan per detik), Anda perlu menghitung rate dari Counter (contoh: rate(api_requests_total[5m]) di Prometheus).

  • Perhatikan Reset: Karena Counter bisa direset, penting untuk memahami perilaku sistem observability yang digunakan (StatsD vs. Prometheus) agar tidak salah interpretasi data.

  • Konteks Bisnis: Counter sering digunakan untuk metrik seperti jumlah transaksi, jumlah login pengguna, atau jumlah pesan yang diproses, yang membantu tim memantau performa dan mendeteksi anomali.


Apa Itu rate pada Metrik Counter?

rate adalah fungsi yang digunakan (terutama pada sistem seperti Prometheus) untuk menghitung laju perubahan (rate of change) dari nilai Counter per satuan waktu, biasanya per detik. Dengan kata lain, rate mengambil data dari metrik Counter (yang bersifat kumulatif dan hanya bertambah) dan menghitung berapa banyak kenaikan nilai tersebut terjadi dalam periode waktu tertentu.

  • Tujuan rate:

    • Mengubah data kumulatif Counter menjadi informasi yang lebih bermakna, seperti frekuensi atau intensitas kejadian per detik.

    • Membantu analisis performa sistem, mendeteksi lonjakan aktivitas, atau mengidentifikasi anomali.

    • Contoh: Jika Counter mencatat jumlah permintaan API (api_requests_total), rate dapat menghitung berapa banyak permintaan per detik.

  • Contoh Konkrit:

    • Metrik Counter: api_requests_total (total permintaan API yang diterima).

    • Jika dalam 5 menit, Counter meningkat dari 1000 menjadi 1600, maka rate(api_requests_total[5m]) menghitung laju rata-rata: (1600 - 1000) / (5 * 60) = 2 permintaan per detik.

Bagaimana Cara Kerja rate?

Fungsi rate bekerja dengan cara:

  1. Mengambil Data Counter dalam Rentang Waktu: Anda menentukan jendela waktu (time window), misalnya [5m] (5 menit), untuk menganalisis data Counter.

  2. Menghitung Kenaikan Nilai: rate menghitung selisih antara nilai Counter di akhir jendela waktu dan nilai di awal jendela waktu.

  3. Menormalkan ke Satuan Waktu: Selisih tersebut dibagi dengan durasi jendela waktu (dalam detik) untuk mendapatkan laju per detik.

  4. Menangani Reset Counter: Karena Counter bisa direset (misalnya, saat aplikasi restart di Prometheus), fungsi rate secara otomatis mendeteksi dan mengakomodasi reset ini dengan menganggap nilai setelah reset sebagai kelanjutan dari kenaikan.

Rumus rate:

rate(counter[time_window]) = (nilai_akhir - nilai_awal) / (durasi_time_window_dalam_detik)
  • Jika ada reset dalam jendela waktu, Prometheus menjumlahkan kenaikan sebelum dan sesudah reset untuk menghitung laju yang akurat.

Mengapa Bisa Menggunakan rate pada Counter?

Counter cocok untuk fungsi rate karena sifatnya yang monotonik meningkat (hanya bertambah atau tetap sama, tidak pernah berkurang kecuali saat reset). Ini membuat Counter ideal untuk mengukur frekuensi kejadian, seperti:

  • Jumlah permintaan HTTP.

  • Jumlah error.

  • Jumlah pesan yang diproses.

Alasan rate digunakan:

  • Data Kumulatif Sulit Dibaca Langsung: Nilai Counter seperti api_requests_total = 10.000 tidak memberikan informasi tentang seberapa cepat permintaan terjadi. rate mengubahnya menjadi metrik yang lebih actionable, misalnya, "10 permintaan per detik".

  • Mendeteksi Pola dan Anomali: Dengan rate, Anda bisa melihat apakah laju permintaan meningkat tiba-tiba (misalnya, karena serangan DDoS) atau menurun (misalnya, karena masalah server).

  • Konsistensi dengan Reset: Karena rate memperhitungkan reset Counter (khususnya di Prometheus), hasilnya tetap akurat meskipun aplikasi di-restart.

Bagaimana Data Counter Disimpan?

Untuk memahami mengapa rate bisa digunakan, kita perlu tahu bagaimana data Counter disimpan dalam sistem observability seperti Prometheus:

  1. Struktur Penyimpanan:

    • Data Counter disimpan sebagai time series (deret waktu) dalam database seperti Prometheus.

    • Setiap titik data dalam time series mencakup:

      • Timestamp: Waktu saat nilai Counter dicatat.

      • Value: Nilai kumulatif Counter pada waktu tersebut.

      • Labels: Metadata tambahan (misalnya, endpoint="/api/users", method="GET").

    • Contoh:

      api_requests_total{endpoint="/api/users"} 1000 @ 2025-10-19 08:00:00
      api_requests_total{endpoint="/api/users"} 1200 @ 2025-10-19 08:01:00
      api_requests_total{endpoint="/api/users"} 1500 @ 2025-10-19 08:02:00
  2. Penyimpanan dan Reset:

    • Dalam Prometheus, data Counter disimpan sebagai nilai absolut yang terus bertambah. Ketika aplikasi restart, Counter direset ke 0, tetapi Prometheus mendeteksi reset ini dengan melihat penurunan nilai secara tiba-tiba (misalnya, dari 1500 ke 0) dan menganggapnya sebagai kelanjutan time series.

  3. Mengapa Cocok untuk rate:

    • Karena Counter disimpan sebagai deret waktu dengan timestamp, sistem seperti Prometheus dapat menghitung kenaikan nilai dalam jendela waktu tertentu.

    • Prometheus menggunakan algoritma untuk mendeteksi reset (disebut "counter reset") dan menyesuaikan perhitungan rate agar tetap akurat.

    • Data time series memungkinkan rate untuk menghitung laju dengan membandingkan dua titik waktu dan menormalkannya ke detik.

Contoh Penggunaan rate

Skenario: Anda menjalankan aplikasi web dan ingin memantau laju permintaan ke endpoint /api/users menggunakan Prometheus.

  • Metrik Counter: api_requests_total{endpoint="/api/users"}.

  • Data:

    • Pada 08:00:00, api_requests_total = 1000.

    • Pada 08:01:00, api_requests_total = 1300.

    • Pada 08:01:30, server restart, api_requests_total = 0.

    • Pada 08:02:00, api_requests_total = 200.

  • Perhitungan rate:

    • Anda menggunakan query: rate(api_requests_total[2m]) (jendela waktu 2 menit).

    • Dari 08:00:00 ke 08:01:00: Kenaikan = 1300 - 1000 = 300 dalam 60 detik → 300/60 = 5 permintaan/detik.

    • Dari 08:01:00 ke 08:02:00: Ada reset, jadi Prometheus menghitung:

      • Sebelum reset: 1300 - 1000 = 300.

      • Setelah reset: 200 - 0 = 200.

      • Total kenaikan: 300 + 200 = 500 dalam 120 detik → 500/120 ≈ 4,17 permintaan/detik.

  • Hasil:

    • Grafik rate(api_requests_total[2m]) akan menunjukkan laju permintaan per detik, yang membantu Anda melihat pola seperti lonjakan lalu lintas atau penurunan performa.

Ilustrasi Grafik

Untuk mengilustrasikan, berikut adalah grafik yang menunjukkan nilai Counter (api_requests_total) dan rate (rate(api_requests_total[2m])) dalam periode waktu.

Penjelasan Grafik:

  • Garis Biru (api_requests_total): Menunjukkan nilai Counter yang meningkat hingga reset pada 08:01:30, lalu naik lagi.

  • Garis Oranye (rate): Menunjukkan laju permintaan per detik, dihitung dari kenaikan Counter dalam jendela waktu 2 menit, dengan penyesuaian untuk reset.

Kesimpulan

  • Apa Itu rate: Fungsi untuk menghitung laju kenaikan Counter per detik dalam jendela waktu tertentu.

  • Cara Kerja: Mengambil selisih nilai Counter, menangani reset, dan menormalkan ke detik.

  • Mengapa Bisa: Counter monotonik meningkat dan disimpan sebagai time series, memungkinkan analisis laju.

  • Penyimpanan Data: Counter disimpan dengan timestamp dan nilai kumulatif, dengan mekanisme untuk mendeteksi reset.

  • Manfaat: Membantu memahami frekuensi kejadian, mendeteksi anomali, dan membuat metrik lebih actionable.

Praktek

Kita akan membuat RESTful sederhana menggunakan Go 1.22 dengan fitur routing baru (dari package net/http), mengintegrasikan Prometheus untuk metrik Counter, dan menunjukkan cara menjalankannya dengan Docker. Aplikasi ini akan memiliki endpoint sederhana dan mencatat metrik Counter untuk jumlah permintaan HTTP (http_requests_total). Saya juga akan menyediakan query Prometheus untuk memantau metrik tersebut serta konfigurasi Docker untuk menjalankan aplikasi dan Prometheus.

  1. Aplikasi Go:

    • Menggunakan net/http dengan fitur routing baru di Go 1.22.

    • Membuat dua endpoint: /api/users (GET) dan /api/orders (GET).

    • Mengintegrasikan library Prometheus (prometheus/client_golang) untuk mencatat metrik Counter http_requests_total dengan label method dan endpoint.

    • Menyediakan endpoint /metrics untuk Prometheus scraping.

  2. Docker:

    • Dockerfile untuk membuild dan menjalankan aplikasi Go.

    • File docker-compose.yml untuk menjalankan aplikasi Go dan Prometheus.

    • File konfigurasi Prometheus (prometheus.yml) untuk scraping metrik.

  3. Query Prometheus:

    • Contoh query untuk menghitung total permintaan dan laju permintaan (rate).

  4. Struktur Proyek:

    go-prometheus-example/
    ├── main.go
    ├── Dockerfile
    ├── docker-compose.yml
    ├── prometheus.yml

Aplikasi Go

Berikut adalah kode aplikasi Go yang membuat RESTful API dan mencatat metrik Counter.

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Define Counter metric for HTTP requests
var httpRequestsTotal = prometheus.NewCounterVec(
	prometheus.CounterOpts{
		Name: "http_requests_total",
		Help: "Total number of HTTP requests processed",
	},
	[]string{"method", "endpoint"},
)

func main() {
	// Register Prometheus metrics
	prometheus.MustRegister(httpRequestsTotal)

	// Create new router using Go 1.22 routing
	mux := http.NewServeMux()

	// Define routes
	mux.HandleFunc("GET /api/users", func(w http.ResponseWriter, r *http.Request) {
		// Increment Counter for /api/users
		httpRequestsTotal.WithLabelValues("GET", "/api/users").Inc()
		fmt.Fprintln(w, `[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]`)
	})

	mux.HandleFunc("GET /api/orders", func(w http.ResponseWriter, r *http.Request) {
		// Increment Counter for /api/orders
		httpRequestsTotal.WithLabelValues("GET", "/api/orders").Inc()
		fmt.Fprintln(w, `[{"order_id": 101, "item": "Book"}, {"order_id": 102, "item": "Pen"}]`)
	})

	// Prometheus metrics endpoint
	mux.Handle("/metrics", promhttp.Handler())

	// Start server
	log.Println("Starting server on :8080")
	if err := http.ListenAndServe(":8080", mux); err != nil {
		log.Fatalf("Server failed: %v", err)
	}
}

Penjelasan Kode:

  • Metrik Counter: http_requests_total didefinisikan sebagai CounterVec dengan label method dan endpoint untuk membedakan permintaan berdasarkan metode HTTP dan path.

  • Routing Go 1.22: Menggunakan http.NewServeMux() dan HandleFunc dengan pola "GET /path" untuk mendefinisikan rute.

  • Endpoint:

    • GET /api/users: Mengembalikan daftar pengguna dummy dan meningkatkan Counter.

    • GET /api/orders: Mengembalikan daftar pesanan dummy dan meningkatkan Counter.

    • /metrics: Endpoint untuk Prometheus scraping.

  • Prometheus: Menggunakan prometheus/client_golang untuk merekam dan mengekspos metrik.

Konfigurasi Docker

Dockerfile

File ini digunakan untuk membuild aplikasi Go.

# Use official Go 1.22 image
FROM golang:1.22-alpine

# Set working directory
WORKDIR /app

# Copy source code
COPY . .

# Install dependencies
RUN go mod tidy

# Build the application
RUN go build -o app main.go

# Expose port
EXPOSE 8080

# Run the application
CMD ["./app"]

docker-compose.yml

File ini mengatur container untuk aplikasi Go dan Prometheus.

version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    networks:
      - monitoring

  prometheus:
    image: prom/prometheus:v2.54.1
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    networks:
      - monitoring

networks:
  monitoring:
    driver: bridge

prometheus.yml

File konfigurasi untuk Prometheus agar mengambil metrik dari aplikasi Go.

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'go-app'
    static_configs:
      - targets: ['app:8080']

Cara Menjalankan

  1. Buat Direktori Proyek:

    mkdir go-prometheus-example
    cd go-prometheus-example

    Simpan file main.go, Dockerfile, docker-compose.yml, dan prometheus.yml di direktori ini.

  2. Jalankan dengan Docker Compose:

    docker-compose up -d --build
    • Aplikasi Go akan berjalan di http://localhost:8080.

    • Prometheus akan berjalan di http://localhost:9090.

  3. Uji Endpoint:

    • Buka browser atau gunakan curl:

      curl http://localhost:8080/api/users
      curl http://localhost:8080/api/orders
    • Setiap permintaan akan meningkatkan Counter http_requests_total.

  4. Lihat Metrik:

    • Buka http://localhost:8080/metrics untuk melihat data mentah metrik.

    • Contoh output metrik:

      http_requests_total{method="GET",endpoint="/api/users"} 5
      http_requests_total{method="GET",endpoint="/api/orders"} 3
  5. Akses Prometheus:

    • Buka http://localhost:9090 di browser.

    • Gunakan query Prometheus untuk memantau metrik.

Query Prometheus

Berikut adalah contoh query untuk memantau metrik Counter http_requests_total:

  1. Total Permintaan per Endpoint:

    http_requests_total
    • Menampilkan nilai Counter untuk setiap kombinasi method dan endpoint.

  2. Laju Permintaan per Detik (rate):

    rate(http_requests_total[5m])
    • Menghitung laju permintaan per detik dalam jendela waktu 5 menit.

    • Contoh: Jika http_requests_total{method="GET",endpoint="/api/users"} meningkat dari 100 ke 400 dalam 5 menit, maka:

      rate = (400 - 100) / (5 * 60) = 1 permintaan/detik
  3. Total Permintaan per Metode HTTP:

    sum by (method) (http_requests_total)
    • Mengelompokkan total permintaan berdasarkan metode HTTP (misalnya, GET).

  4. Laju Permintaan per Endpoint:

    sum by (endpoint) (rate(http_requests_total[5m]))
    • Menampilkan laju permintaan per detik untuk setiap endpoint.

Catatan Penting

  • Go 1.22 Routing: Fitur routing baru (HandleFunc dengan pola seperti "GET /path") mempermudah definisi rute tanpa library tambahan seperti gorilla/mux.

  • Prometheus Counter: Metrik http_requests_total menggunakan CounterVec untuk mendukung label, memungkinkan analisis berdasarkan method dan endpoint.

  • Docker: Pastikan Docker dan Docker Compose terinstal. Container app dan prometheus dihubungkan melalui jaringan monitoring.

  • Query Prometheus: Gunakan rate untuk analisis laju, dan sum untuk agregasi data berdasarkan label.


Gauge

Gauge adalah tipe metrik yang merepresentasikan snapshot dari suatu keadaan pada waktu tertentu. Berbeda dengan Counter yang hanya bertambah (monotonik meningkat), Gauge dapat bertambah atau berkurang sesuai dengan nilai yang diukur. Gauge cocok untuk mengukur nilai yang berfluktuasi, seperti suhu, jumlah item dalam antrean, penggunaan ruang disk, atau jumlah permintaan bersamaan (concurrent requests).

Karakteristik Utama Gauge:

  • Snapshot Nilai: Gauge mencerminkan nilai saat ini dari suatu keadaan, bukan akumulasi seperti Counter.

  • Bisa Naik atau Turun: Nilai Gauge dapat meningkat atau menurun tergantung pada keadaan yang diukur.

  • Agregasi Berguna: Gauge sering dianalisis dengan fungsi agregasi seperti sum, avg (rata-rata), min, atau max dalam periode waktu tertentu untuk memberikan wawasan yang lebih bermakna.

  • Reset atau Persistensi: Tergantung pada implementasi (misalnya, Prometheus), Gauge mungkin direset atau dipertahankan sesuai dengan siklus hidup aplikasi atau pengaturan sistem monitoring.

  • Contoh Penggunaan:

    • Suhu (Temperature): Mengukur suhu server saat ini (misalnya, 45°C).

    • Item dalam Antrean (Items in Queue): Jumlah tugas yang sedang menunggu diproses.

    • Ruang Disk Terpakai (Disk Space Used): Persentase atau jumlah GB disk yang digunakan.

    • Jumlah Permintaan Bersamaan (Concurrent Requests): Jumlah permintaan HTTP yang sedang diproses.

Perbedaan dengan Counter:

  • Counter hanya bertambah dan cocok untuk menghitung kejadian kumulatif (misalnya, total permintaan HTTP).

  • Gauge mencerminkan keadaan saat ini dan cocok untuk nilai yang berfluktuasi (misalnya, jumlah koneksi aktif).

Variasi Implementasi:

  • Dalam Prometheus, Gauge disimpan sebagai time series dan mencerminkan nilai terbaru yang dilaporkan oleh aplikasi. Prometheus tidak secara otomatis mereset Gauge kecuali aplikasi di-restart atau nilai diatur ulang secara eksplisit.

  • Penting untuk memverifikasi bagaimana sistem observability Anda menangani Gauge agar interpretasi data akurat.

Contoh

Berikut adalah beberapa contoh penggunaan Gauge dalam observability:

  1. Suhu Server (Temperature)

    • Konteks: Sebuah aplikasi memantau suhu CPU server untuk mendeteksi risiko overheating.

    • Metrik Gauge: server_cpu_temperature_celsius

    • Cara Kerja: Aplikasi membaca suhu CPU setiap 10 detik dan mengatur nilai Gauge. Misalnya, suhu berfluktuasi antara 40°C dan 50°C tergantung beban kerja.

    • Agregasi: Menggunakan avg(server_cpu_temperature_celsius[5m]) untuk melihat rata-rata suhu dalam 5 menit.

  2. Jumlah Item dalam Antrean (Items in Queue)

    • Konteks: Sistem pengolahan pesan (misalnya, RabbitMQ) mencatat jumlah pesan yang menunggu diproses.

    • Metrik Gauge: queue_items_count

    • Cara Kerja: Gauge diatur ke jumlah pesan saat ini. Jika antrean bertambah (misalnya, dari 10 ke 50 pesan) atau berkurang (misalnya, ke 20 pesan), Gauge mencerminkan nilai terbaru.

    • Agregasi: Menggunakan max(queue_items_count[1h]) untuk melihat puncak antrean dalam 1 jam.

  3. Ruang Disk Terpakai (Disk Space Used)

    • Konteks: Aplikasi memantau penggunaan disk pada server.

    • Metrik Gauge: disk_space_used_bytes

    • Cara Kerja: Gauge diperbarui dengan jumlah byte yang digunakan. Jika file ditambahkan atau dihapus, nilai Gauge naik atau turun.

    • Agregasi: Menggunakan avg(disk_space_used_bytes[1d]) untuk rata-rata penggunaan disk harian.

  4. Jumlah Permintaan Bersamaan (Concurrent Requests)

    • Konteks: Server web mencatat jumlah permintaan HTTP yang sedang diproses.

    • Metrik Gauge: http_concurrent_requests

    • Cara Kerja: Gauge diatur ke jumlah permintaan aktif. Misalnya, jika 10 permintaan sedang diproses, Gauge = 10; jika 3 selesai, Gauge turun ke 7.

    • Agregasi: Menggunakan max(http_concurrent_requests[5m]) untuk melihat puncak permintaan bersamaan.

Ilustrasi

Mari kita gunakan contoh Jumlah Permintaan Bersamaan (Concurrent Requests) untuk mengilustrasikan Gauge. Misalkan sebuah server web memproses permintaan HTTP, dan kita ingin memantau jumlah permintaan yang aktif pada setiap waktu.

Skenario

  • Aplikasi web berjalan dan mencatat metrik Gauge http_concurrent_requests.

  • Pada pukul 08:00, server memproses 5 permintaan bersamaan.

  • Pada pukul 08:05, permintaan meningkat menjadi 15 karena lonjakan lalu lintas.

  • Pada pukul 08:10, permintaan turun ke 8.

  • Pada pukul 08:15, permintaan naik lagi ke 12.

  • Pada pukul 08:20, server di-restart, dan Gauge kembali ke 0.

  • Pada pukul 08:25, permintaan naik ke 10.

Visualisasi

Berikut adalah grafik yang menunjukkan nilai Gauge http_concurrent_requests dari waktu ke waktu, menggunakan Chart.js dengan tipe line untuk menggambarkan fluktuasi nilai.

Penjelasan Grafik:

  • Sumbu X: Waktu (dari 08:00 hingga 08:25).

  • Sumbu Y: Nilai Gauge (http_concurrent_requests), yaitu jumlah permintaan HTTP aktif.

  • Grafik menunjukkan fluktuasi nilai Gauge: naik dari 5 ke 15, turun ke 8, naik lagi ke 12, direset ke 0 saat restart, lalu naik ke 10.

  • Ini menggambarkan sifat Gauge yang dapat naik dan turun, berbeda dengan Counter yang hanya bertambah.

Contoh Implementasi dalam Kode

Untuk memberikan gambaran lebih lanjut, berikut adalah contoh sederhana implementasi Gauge dalam aplikasi Go menggunakan Prometheus, mirip dengan contoh Counter sebelumnya. Saya akan fokus pada metrik http_concurrent_requests.

Kode Go

package main

import (
	"fmt"
	"log"
	"net/http"
	"time" // Import time untuk simulasi latency

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Define Gauge metric for concurrent requests
var concurrentRequests = prometheus.NewGauge(
	prometheus.GaugeOpts{
		Name: "http_concurrent_requests",
		Help: "Number of concurrent HTTP requests being processed",
	},
)

func main() {
	// Register Prometheus metrics
	prometheus.MustRegister(concurrentRequests)

	// Membuat router baru
	mux := http.NewServeMux()

	// Handler untuk /api/users dengan instrumentasi Gauge
	apiUsersHandler := func(w http.ResponseWriter, r *http.Request) {
		// Increment concurrent requests
		concurrentRequests.Inc()

		// Pastikan untuk decrement saat selesai, menggunakan defer
		defer concurrentRequests.Dec()

		// Simulasi pekerjaan/latency (misalnya 2 detik) agar metrik teramati
		time.Sleep(2 * time.Second)

		// Logika bisnis utama
		fmt.Fprintln(w, `[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]`)
	}

	// Tentukan route
	mux.HandleFunc("GET /api/users", apiUsersHandler)

	// Prometheus metrics endpoint
	mux.Handle("/metrics", promhttp.Handler())

	// Start server
	log.Println("Starting server on :8080")
	if err := http.ListenAndServe(":8080", mux); err != nil {
		log.Fatalf("Server failed: %v", err)
	}
}

Penjelasan Kode:

  • Metrik Gauge: http_concurrent_requests didefinisikan sebagai Gauge untuk mencatat jumlah permintaan bersamaan.

  • Penghitungan Concurrent Requests: Menggunakan atomic.Int64 untuk menghitung jumlah permintaan aktif secara thread-safe. Gauge diperbarui saat permintaan masuk (naik) dan selesai (turun).

  • Endpoint: GET /api/users mengembalikan data dummy dan memperbarui Gauge.

  • Prometheus: Endpoint /metrics mengekspos metrik Gauge untuk scraping.

Docker dan Prometheus: Anda dapat menggunakan konfigurasi Docker dan prometheus.yml yang sama seperti pada contoh Counter sebelumnya, hanya mengganti nama metrik di query Prometheus.

Query Prometheus untuk Gauge

Berikut adalah contoh query untuk memantau metrik Gauge http_concurrent_requests:

  1. Nilai Saat Ini:

    http_concurrent_requests
    • Menampilkan jumlah permintaan bersamaan saat ini.

  2. Rata-rata dalam 5 Menit:

    avg_over_time(http_concurrent_requests[5m])
    • Menghitung rata-rata jumlah permintaan bersamaan dalam jendela waktu 5 menit.

  3. Puncak dalam 1 Jam:

    max_over_time(http_concurrent_requests[1h])
    • Menampilkan jumlah maksimum permintaan bersamaan dalam 1 jam.

  4. Minimum dalam 1 Jam:

    min_over_time(http_concurrent_requests[1h])
    • Menampilkan jumlah minimum permintaan bersamaan dalam 1 jam.

Perhatian Implementasi

  • Prometheus: Gauge disimpan sebagai time series, dan nilai terbaru selalu digunakan. Tidak ada mekanisme reset otomatis kecuali aplikasi di-restart atau Gauge diatur ulang secara eksplisit.

  • StatsD: Gauge di StatsD biasanya dilaporkan sebagai nilai absolut per flush (misalnya, setiap 10 detik). Jika aplikasi tidak mengirim nilai baru, StatsD mungkin mempertahankan nilai terakhir atau menganggapnya 0, tergantung konfigurasi.

  • Konsistensi: Pastikan aplikasi Anda secara konsisten mengirim nilai Gauge untuk menghindari data yang menyesatkan. Misalnya, jika server mati, Gauge mungkin tidak diperbarui, dan sistem monitoring mungkin menampilkan nilai lama.

Perbandingan dengan Counter

  • Counter: Cocok untuk kejadian kumulatif (misalnya, total permintaan HTTP). Digunakan dengan rate untuk menghitung laju (contoh: permintaan per detik).

  • Gauge: Cocok untuk keadaan yang berfluktuasi (misalnya, jumlah permintaan aktif). Digunakan dengan avg, max, atau min untuk analisis tren.

  • Contoh Kombinasi: Dalam aplikasi web, Anda bisa menggunakan Counter http_requests_total untuk menghitung total permintaan dan Gauge http_concurrent_requests untuk memantau beban saat ini.

Kesimpulan

  • Gauge: Metrik untuk snapshot keadaan yang bisa naik atau turun, cocok untuk suhu, antrean, penggunaan sumber daya, atau permintaan bersamaan.

  • Agregasi: Gunakan avg, max, atau min untuk analisis lebih lanjut.

  • Implementasi: Perhatikan perbedaan antara Prometheus (time series, nilai terbaru) dan StatsD (nilai absolut per flush).

  • Ilustrasi: Grafik menunjukkan fluktuasi Gauge, berbeda dengan Counter yang hanya naik.

  • Kode: Contoh Go menunjukkan cara melacak Gauge dengan thread-safe dan mengeksposnya ke Prometheus.


Histogram

Histogram adalah tipe metrik yang digunakan untuk mengukur distribusi nilai dari suatu kejadian, seperti latensi permintaan, ukuran respons, atau durasi proses. Histogram mengelompokkan data ke dalam bucket (rentang nilai) dan menghitung berapa banyak kejadian yang masuk ke setiap bucket, serta menyediakan jumlah total dan total nilai pengamatan.

Karakteristik Utama Histogram:

  • Bucket: Histogram membagi pengamatan ke dalam rentang nilai yang telah ditentukan (misalnya, latensi <0.1s, 0.1-0.2s, 0.2-0.5s, dll.).

  • Counter per Bucket: Setiap bucket adalah Counter yang mencatat jumlah kejadian yang jatuh dalam rentang tersebut.

  • Metrik Tambahan:

    • <nama_metrik>_sum: Total nilai semua pengamatan (misalnya, total latensi dalam detik).

    • <nama_metrik>_count: Total jumlah pengamatan.

  • Agregasi: Histogram memungkinkan analisis distribusi, seperti persentil (dengan fungsi seperti histogram_quantile di Prometheus) atau rata-rata.

  • Implementasi:

    • Dalam Prometheus, Histogram menghasilkan beberapa time series: satu untuk setiap bucket (<nama_metrik>_bucket{le="..."}), satu untuk _sum, dan satu untuk _count.

    • Dalam StatsD, Histogram sering diimplementasikan dengan cara yang lebih sederhana, mengirimkan distribusi ke server tanpa bucket eksplisit, tergantung pada backend.

Contoh Penggunaan:

  • Latensi Permintaan HTTP: Mengukur berapa lama server memproses permintaan (misalnya, 50ms, 200ms).

  • Ukuran Respons: Mengukur ukuran data yang dikembalikan (misalnya, dalam byte).

  • Durasi Proses: Mengukur waktu pemrosesan tugas (misalnya, waktu eksekusi job batch).

Perbedaan dengan Counter dan Gauge:

  • Counter: Hanya menghitung kejadian kumulatif (total permintaan).

  • Gauge: Mengukur nilai saat ini yang bisa naik/turun (jumlah koneksi aktif).

  • Histogram: Mengukur distribusi nilai dengan bucket untuk analisis statistik.

Contoh

Skenario: Aplikasi web mencatat latensi permintaan HTTP ke endpoint /api/users.

  • Metrik Histogram: http_request_duration_seconds

  • Bucket: [0.1, 0.2, 0.5, 1.0, 2.0] detik (artinya, latensi dikelompokkan ke dalam rentang <0.1s, 0.1-0.2s, 0.2-0.5s, dll.).

  • Cara Kerja:

    • Jika sebuah permintaan memakan waktu 0.15 detik, bucket le="0.2" bertambah 1, dan _sum bertambah 0.15, _count bertambah 1.

    • Jika permintaan lain memakan waktu 0.3 detik, bucket le="0.5" bertambah 1, dan _sum bertambah 0.3, _count bertambah 1.

  • Agregasi: Gunakan histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) untuk menghitung persentil ke-95 latensi.

Ilustrasi

Misalkan kita mencatat latensi permintaan HTTP selama 5 menit:

  • Latensi: 0.05s, 0.12s, 0.18s, 0.25s, 0.4s, 0.7s, 1.2s.

  • Bucket: [0.1, 0.2, 0.5, 1.0, 2.0].

Distribusi:

  • <0.1s: 1 permintaan (0.05s).

  • 0.1-0.2s: 2 permintaan (0.12s, 0.18s).

  • 0.2-0.5s: 2 permintaan (0.25s, 0.4s).

  • 0.5-1.0s: 1 permintaan (0.7s).

  • 1.0-2.0s: 1 permintaan (1.2s).

  • _sum: 0.05 + 0.12 + 0.18 + 0.25 + 0.4 + 0.7 + 1.2 = 2.9 detik.

  • _count: 7 permintaan.

Visualisasi: Berikut adalah grafik distribusi Histogram menggunakan Chart.js (tipe bar) untuk menunjukkan jumlah permintaan per bucket.

Penjelasan Grafik:

  • Sumbu X: Bucket latensi (rentang waktu dalam detik).

  • Sumbu Y: Jumlah permintaan yang jatuh ke dalam setiap bucket.

  • Grafik menunjukkan distribusi latensi, yang membantu mengidentifikasi pola (misalnya, sebagian besar permintaan berada di bucket 0.1-0.5s).


Summary

Summary adalah tipe metrik yang mirip dengan Histogram, tetapi fokus pada menghitung persentil langsung di sisi klien (aplikasi) dan menyimpan data ringkasan seperti jumlah total dan total nilai pengamatan. Summary tidak menggunakan bucket, melainkan melacak kuantil tertentu (misalnya, p50, p90, p99) secara langsung.

Karakteristik Utama Summary:

  • Kuantil: Summary menghitung persentil (misalnya, 50%, 90%, 99%) dari pengamatan secara langsung di aplikasi.

  • Metrik Tambahan:

    • <nama_metrik>_sum: Total nilai semua pengamatan.

    • <nama_metrik>_count: Total jumlah pengamatan.

    • <nama_metrik>{quantile="X"}: Nilai persentil (misalnya, p90 = latensi di mana 90% permintaan lebih cepat).

  • Agregasi: Summary kurang fleksibel dibandingkan Histogram karena persentil dihitung di klien, sehingga tidak bisa diubah di server (misalnya, Prometheus tidak bisa menghitung persentil baru dari Summary).

  • Implementasi:

    • Dalam Prometheus, Summary menghasilkan time series untuk setiap kuantil, _sum, dan _count.

    • Dalam StatsD, Summary sering diimplementasikan dengan pengiriman nilai rata-rata atau persentil sederhana, tergantung backend.

Contoh Penggunaan:

  • Latensi Permintaan HTTP: Mengukur persentil latensi (misalnya, p95 = 0.5s berarti 95% permintaan lebih cepat dari 0.5s).

  • Waktu Eksekusi Tugas: Mengukur waktu pemrosesan dengan fokus pada persentil.

  • Ukuran Data: Mengukur distribusi ukuran respons API.

Perbedaan dengan Histogram:

  • Histogram: Menggunakan bucket, fleksibel untuk menghitung persentil di server, tetapi membutuhkan lebih banyak penyimpanan.

  • Summary: Menghitung persentil di klien, lebih hemat penyimpanan, tetapi kurang fleksibel untuk analisis tambahan.

Contoh

Skenario: Aplikasi web mencatat latensi permintaan HTTP ke endpoint /api/orders.

  • Metrik Summary: http_request_duration_seconds

  • Kuantil: [0.5, 0.9, 0.99] (persentil ke-50, ke-90, ke-99).

  • Cara Kerja:

    • Aplikasi mencatat latensi setiap permintaan dan menghitung persentil secara langsung.

    • Misalnya, jika latensi adalah 0.1s, 0.2s, 0.3s, Summary memperbarui:

      • _sum: Total latensi (misalnya, 0.6s).

      • _count: Jumlah permintaan (3).

      • quantile="0.9": Latensi di mana 90% permintaan lebih cepat (misalnya, 0.28s).

  • Agregasi: Gunakan http_request_duration_seconds{quantile="0.9"} untuk melihat latensi p90.

Ilustrasi

Misalkan kita mencatat latensi permintaan HTTP selama 5 menit:

  • Latensi: 0.05s, 0.12s, 0.18s, 0.25s, 0.4s, 0.7s, 1.2s.

  • Kuantil: [0.5, 0.9, 0.99].

  • Hasil (dihitung di klien):

    • p50 (median): ~0.18s.

    • p90: ~0.7s.

    • p99: ~1.2s.

    • _sum: 2.9 detik.

    • _count: 7.

Visualisasi: Berikut adalah grafik yang menunjukkan nilai persentil dari Summary menggunakan Chart.js (tipe line).

Penjelasan Grafik:

  • Sumbu X: Waktu pengamatan.

  • Sumbu Y: Nilai latensi untuk persentil p50, p90, dan p99.

  • Grafik menunjukkan bagaimana persentil berubah seiring waktu, memberikan wawasan tentang performa aplikasi (misalnya, p99 tinggi menunjukkan adanya permintaan lambat).


Implementasi Aplikasi Go

Kita akan membuat aplikasi RESTful sederhana menggunakan Go 1.22 dengan dua endpoint:

  • /api/users: Mengukur latensi dengan Histogram.

  • /api/orders: Mengukur latensi dengan Summary. Aplikasi akan dijalankan dengan Docker dan terintegrasi dengan Prometheus.

package main

import (
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Define Histogram for request duration (users endpoint)
var httpRequestDurationHistogram = prometheus.NewHistogram(
	prometheus.HistogramOpts{
		Name:    "http_request_duration_seconds_histogram",
		Help:    "Histogram of HTTP request durations for /api/users",
		Buckets: []float64{0.1, 0.2, 0.5, 1.0, 2.0},
	},
)

// Define Summary for request duration (orders endpoint)
var httpRequestDurationSummary = prometheus.NewSummary(
	prometheus.SummaryOpts{
		Name:       "http_request_duration_seconds_summary",
		Help:       "Summary of HTTP request durations for /api/orders",
		Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
	},
)

func main() {
	// Register Prometheus metrics
	prometheus.MustRegister(httpRequestDurationHistogram)
	prometheus.MustRegister(httpRequestDurationSummary)

	// Seed random number generator for simulating latency
	rand.Seed(time.Now().UnixNano())

	// Create new router
	mux := http.NewServeMux()

	// Handler for /api/users (Histogram)
	mux.HandleFunc("GET /api/users", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		// Simulate varying latency (50ms to 1500ms)
		time.Sleep(time.Duration(rand.Intn(1450)+50) * time.Millisecond)

		// Record duration in Histogram
		duration := time.Since(start).Seconds()
		httpRequestDurationHistogram.Observe(duration)

		fmt.Fprintln(w, `[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]`)
	})

	// Handler for /api/orders (Summary)
	mux.HandleFunc("GET /api/orders", func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		// Simulate varying latency (50ms to 1500ms)
		time.Sleep(time.Duration(rand.Intn(1450)+50) * time.Millisecond)

		// Record duration in Summary
		duration := time.Since(start).Seconds()
		httpRequestDurationSummary.Observe(duration)

		fmt.Fprintln(w, `[{"order_id": 101, "item": "Book"}, {"order_id": 102, "item": "Pen"}]`)
	})

	// Prometheus metrics endpoint
	mux.Handle("/metrics", promhttp.Handler())

	// Start server
	log.Println("Starting server on :8080")
	if err := http.ListenAndServe(":8080", mux); err != nil {
		log.Fatalf("Server failed: %v", err)
	}
}

Penjelasan Kode:

  • Histogram: http_request_duration_seconds_histogram mengukur latensi untuk /api/users dengan bucket [0.1, 0.2, 0.5, 1.0, 2.0].

  • Summary: http_request_duration_seconds_summary mengukur latensi untuk /api/orders dengan kuantil p50, p90, dan p99.

  • Simulasi Latensi: Menggunakan rand untuk mensimulasikan latensi acak antara 50ms hingga 1500ms.

  • Endpoint:

    • GET /api/users: Mengembalikan data pengguna dummy dan mencatat latensi ke Histogram.

    • GET /api/orders: Mengembalikan data pesanan dummy dan mencatat latensi ke Summary.

    • /metrics: Endpoint untuk Prometheus scraping.

Konfigurasi Docker

Dockerfile

# Use official Go 1.22 image
FROM golang:1.22-alpine

# Set working directory
WORKDIR /app

# Copy source code
COPY . .

# Install dependencies
RUN go mod tidy

# Build the application
RUN go build -o app main.go

# Expose port
EXPOSE 8080

# Run the application
CMD ["./app"]

docker-compose.yml

version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    networks:
      - monitoring

  prometheus:
    image: prom/prometheus:v2.54.1
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    networks:
      - monitoring

networks:
  monitoring:
    driver: bridge

prometheus.yml

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'go-app'
    static_configs:
      - targets: ['app:8080']

Cara Menjalankan

  1. Buat Direktori Proyek:

    mkdir go-prometheus-histogram-summary
    cd go-prometheus-histogram-summary

    Simpan file main.go, Dockerfile, docker-compose.yml, dan prometheus.yml.

  2. Jalankan dengan Docker Compose:

    docker-compose up --build
    • Aplikasi Go berjalan di http://localhost:8080.

    • Prometheus berjalan di http://localhost:9090.

  3. Uji Endpoint:

    curl http://localhost:8080/api/users
    curl http://localhost:8080/api/orders
    • Setiap permintaan akan mencatat latensi ke Histogram atau Summary.

  4. Lihat Metrik:

    • Buka http://localhost:8080/metrics untuk melihat data mentah.

    • Contoh output:

      http_request_duration_seconds_histogram_bucket{le="0.1"} 2
      http_request_duration_seconds_histogram_bucket{le="0.2"} 5
      ...
      http_request_duration_seconds_histogram_sum 2.9
      http_request_duration_seconds_histogram_count 7
      http_request_duration_seconds_summary{quantile="0.5"} 0.18
      http_request_duration_seconds_summary{quantile="0.9"} 0.7
      ...
  5. Akses Prometheus:

    • Buka http://localhost:9090 untuk menjalankan query.

Query Prometheus

Untuk Histogram

  1. Jumlah Permintaan per Bucket:

    http_request_duration_seconds_histogram_bucket
    • Menampilkan jumlah permintaan untuk setiap bucket latensi.

  2. Laju Permintaan per Bucket:

    rate(http_request_duration_seconds_histogram_bucket[5m])
    • Menghitung laju permintaan per detik untuk setiap bucket.

  3. Persentil ke-95:

    histogram_quantile(0.95, rate(http_request_duration_seconds_histogram_bucket[5m]))
    • Menghitung latensi di mana 95% permintaan lebih cepat.

  4. Rata-rata Latensi:

    rate(http_request_duration_seconds_histogram_sum[5m]) / rate(http_request_duration_seconds_histogram_count[5m])
    • Menghitung rata-rata latensi dalam 5 menit.

Untuk Summary

  1. Nilai Persentil:

    http_request_duration_seconds_summary{quantile="0.9"}
    • Menampilkan latensi p90 (90% permintaan lebih cepat dari nilai ini).

  2. Total Permintaan:

    http_request_duration_seconds_summary_count
    • Menampilkan jumlah total permintaan.

  3. Rata-rata Latensi:

    rate(http_request_duration_seconds_summary_sum[5m]) / rate(http_request_duration_seconds_summary_count[5m])
    • Menghitung rata-rata latensi dari Summary.


Perbandingan Histogram dan Summary

  • Histogram:

    • Kelebihan: Fleksibel untuk menghitung persentil di server, cocok untuk analisis distribusi mendalam.

    • Kekurangan: Menghasilkan banyak time series (satu per bucket), meningkatkan kebutuhan penyimpanan.

    • Gunakan Ketika: Anda perlu fleksibilitas untuk menghitung persentil berbeda atau menganalisis distribusi.

  • Summary:

    • Kelebihan: Lebih hemat penyimpanan karena hanya menyimpan kuantil tertentu.

    • Kekurangan: Persentil dihitung di klien, sehingga tidak fleksibel untuk analisis tambahan.

    • Gunakan Ketika: Anda tahu persentil yang dibutuhkan (misalnya, p90, p99) dan ingin hemat sumber daya.


Berikut adalah perbandingan mendetail antara Histogram dan Summary:

Aspek

Histogram

Summary

Struktur Data

Mengelompokkan data ke dalam bucket (rentang nilai).

Menghitung persentil langsung di klien tanpa bucket.

Metrik yang Dihasilkan

_bucket{le="X"}, _sum, _count.

{quantile="X"}, _sum, _count.

Perhitungan Persentil

Persentil dihitung di server (misalnya, dengan histogram_quantile).

Persentil dihitung di klien, ditentukan saat konfigurasi metrik.

Fleksibilitas Analisis

Fleksibel: Persentil baru (misalnya, p95, p99) dapat dihitung dari bucket.

Tidak fleksibel: Hanya persentil yang dikonfigurasi (misalnya, p90) tersedia.

Kebutuhan Penyimpanan

Membutuhkan lebih banyak penyimpanan karena setiap bucket adalah time series.

Lebih hemat penyimpanan karena hanya menyimpan kuantil tertentu.

Overhead Klien

Rendah: Hanya mencatat ke bucket yang sesuai.

Tinggi: Harus menghitung persentil secara real-time di aplikasi.

Contoh Penggunaan

Latensi HTTP, ukuran respons, durasi proses dengan analisis distribusi.

Latensi HTTP, waktu eksekusi dengan fokus pada persentil spesifik.

Last updated