Belajar Rust - Observability, Logging, dan Tracing
Series/Belajar Rust/Episode 14
Episode 14 of 19

Belajar Rust - Observability, Logging, dan Tracing

Episode ini membuat service Rust bisa diamati: logging terstruktur dengan tracing dan ekosistem subscriber, distributed tracing dengan tracing-opentelemetry, serta metrics Prometheus dengan crate prometheus atau opentelemetry.

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

Pendahuluan

Aplikasi yang berjalan di production hampir tidak mungkin dipahami tanpa observability: logging, tracing, dan metrics. Saat error terjadi di jam 3 pagi, log yang terstruktur dan trace yang lengkap adalah alat pertama yang menyelamatkan kalian.

Episode 14 membangun tiga pilar observability di Rust: logging terstruktur dengan tracing, distributed tracing dengan tracing-opentelemetry, dan metrics dengan Prometheus. Di akhir episode, kalian bisa menjawab tiga pertanyaan: apa yang terjadi, di mana terjadinya, dan seberapa sering.

Logging dengan Tracing

Konsep: Events dan Spans

tracing adalah framework logging modern: ia membedakan event (sesaat terjadi, misalnya "request selesai") dan span (periode, misalnya "memproses request id 42"). Span bisa bersarang, sehingga seluruh request bisa dihubungkan dalam satu konteks.

Event dan span
cat > src/main.rs <<'EOF'
use tracing::{debug, error, info, span, Level};
use tracing_subscriber;
 
fn proses_request(id: u64) {
    let span = span!(Level::INFO, "request", id);
    let _entered = span.enter();
    info!("memulai proses");
    debug!("membaca data dari cache");
    error!("timeout saat menghubungi upstream");
}
 
fn main() {
    tracing_subscriber::fmt()
        .with_max_level(Level::DEBUG)
        .init();
 
    proses_request(42);
}
EOF
cargo run

span!(Level::INFO, "request", id) membuka span berkonteks, dan _entered menutupnya di akhir scope. info!, debug!, dan error! mencatat event di dalam span. cargo add tracing-subscriber --features fmt,json menambah subscriber yang mencetak ke terminal.

Subscriber dan Format JSON

Subscriber adalah bagian yang menerima event dan span. Untuk produksi, ganti formatter ke JSON agar log bisa diparse kolektor seperti Loki atau OpenSearch: tracing_subscriber::fmt().json().with_current_span(true).init() menghasilkan satu objek JSON per baris dengan timestamp, level, field, dan span aktif. tracing::info!(user = "budi", ...) menyisipkan field terstruktur — bukan sekadar teks yang digabung.

Instrumentasi Handler dengan Attribute

Macro #[tracing::instrument]

Menambahkan span di setiap fungsi secara manual itu membosankan. Macro #[tracing::instrument] melakukannya otomatis:

Instrument function
cat > src/main.rs <<'EOF'
use tracing::instrument;
 
#[instrument]
fn hitung_biaya(items: u32, harga: f64) -> f64 {
    items as f64 * harga
}
 
fn main() {
    tracing_subscriber::fmt().init();
    let total = hitung_biaya(10, 5.5);
    println!("total: {}", total);
}
EOF
cargo run

#[instrument] membuat span yang mengikuti fungsi, mencatat parameter, dan menutup saat fungsi selesai. Untuk handler axum, #[instrument(skip(state))] mengecualikan state besar dari log. Ini cara paling cepat membuat aplikasi bisa dilacak.

Distributed Tracing dengan OpenTelemetry

Menghubungkan Antar Service

Di arsitektur microservices, satu request melewati banyak service. Distributed tracing menghubungkan semua segmen lewat trace ID yang disebarkan antar service. Ekosistem standarnya adalah OpenTelemetry.

Setup OTLP exporter
cat > src/main.rs <<'EOF'
use opentelemetry::global::set_tracer_provider;
use opentelemetry_otlp::new_exporter;
 
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let exporter = new_exporter()
        .tonic()
        .with_endpoint("http://otel-collector:4317")
        .build_export_pipeline()?;
    let provider = exporter.setup();
    set_tracer_provider(provider);
 
    tracing_subscriber::registry()
        .with(tracing_opentelemetry::layer())
        .init();
 
    tracing::info!("layanan observability siap");
    Ok(())
}
EOF
cargo run

Exporter OTLP mengirim trace ke OpenTelemetry Collector yang kemudian meneruskannya ke backend seperti Jaeger atau Tempo. Dengan ini, satu trace ID menghubungkan request yang melintasi banyak service — sumber daya utama debugging.

Metrics dengan Prometheus

Counter dan Histogram

Metrics adalah angka: jumlah request, durasi, error rate. Prometheus menarik angka ini lewat endpoint HTTP yang dibuka aplikasi. Mulai dari Registry dan IntCounter:

Registry dan counter
cat > src/main.rs <<'EOF'
use prometheus::{Encoder, IntCounter, Opts, Registry, TextEncoder};
 
fn main() {
    let registry = Registry::new();
    let counter = IntCounter::new(
        Opts::new("http_request_total", "total request http"),
    )
    .unwrap();
    registry.register(counter).unwrap();
 
    let mut buffer = Vec::new();
    let encoder = TextEncoder::new();
    encoder.encode(&registry.gather(), &mut buffer).unwrap();
    println!("{}", String::from_utf8(buffer).unwrap());
}
EOF
cargo run

Registry menampung seluruh metrics, dan TextEncoder menghasilkan format yang dipahami Prometheus. Di aplikasi server, pasang handler yang mengembalikan teks ini di endpoint /metrics seperti handler axum di episode 10 — Prometheus menariknya secara berkala, lalu Grafana memvisualisasikan. IntCounter menghitung event; untuk durasi, gunakan Histogram atau Summary.

Opsional: OpenTelemetry Metrics

Jika stack kalian sudah memakai OpenTelemetry, opentelemetry juga menyediakan metrics API yang bisa diekspor via OTLP. Pilihannya konsisten: pakai prometheus langsung untuk endpoint scrape, atau opentelemetry untuk pipeline terpadu dengan trace.

Praktik Observability

Beberapa kebiasaan yang membuat observability berguna, bukan sekadar berisik:

  • Log pada level yang tepat: error untuk kegagalan, warn untuk degradasi, info untuk lifecycle.
  • Selalu sertakan field terstruktur — jangan gabung teks dalam pesan.
  • Buat span di batas service dan biarkan #[instrument] menangani sisanya.
  • Satu trace ID per request user-facing, disebarkan ke semua panggilan downstream.
  • Tiga metrics wajib: request rate, error rate, dan latency percentile.

Penutup

Inti yang harus dibawa pulang:

  • tracing membedakan event dan span; subscriber menentukan format dan tujuan.
  • #[tracing::instrument] meng-instrument fungsi secara otomatis.
  • JSON logging agar log mudah diparse kolektor.
  • OpenTelemetry menghubungkan trace antar service dengan trace ID.
  • Prometheus menarik metrics dari endpoint /metrics.
  • Tiga metrik wajib: rate, error rate, dan latency.

Di episode 15 selanjutnya kita akan membahas resilience, graceful shutdown, dan health check — mematikan server dengan bersih lewat signal handling, menyediakan health check dan readiness probes untuk Kubernetes, serta menerapkan retry, timeout, dan circuit breaker dengan tower dan tower-http. Service kalian akan tahan banting.

Belajar Rust - Observability, Logging, dan Tracing | Belajar Rust