Subscriber fmt() pada Chapter sebelumnya kita pakai sebagai kotak hitam: rantai with_max_level(Level::INFO) membuat output tampil, tetapi level tersebut terkunci di dalam kode. Chapter penutup section ini membuka kotak tersebut dan mengatur dua kenop utama subscriber sesuai kebutuhan nyata. Kenop pertama adalah filter: EnvFilter membaca variabel lingkungan RUST_LOG dengan sintaks yang sama seperti env_logger, sehingga verbosity bisa diubah per eksekusi tanpa kompilasi ulang. Kenop kedua adalah format: builder fmt() menawarkan beberapa preset tampilan, dari baris padat untuk production sampai tampilan verbose yang menampilkan lokasi kode setiap event.
Peran Subscriber: Filter dan Format #
Mekanismenya bekerja dalam dua tahap yang berurutan. Setiap event dan span yang dibangkitkan instrumentasi dikirim ke dispatcher global, lalu EnvFilter memeriksa level dan target kejadian terhadap directive dari RUST_LOG: kejadian yang ditolak dibuang di tahap ini, sebelum menyentuh pemformat. Kejadian yang lolos diteruskan ke layer format milik fmt(), yang merender timestamp, level, konteks span, target, pesan, dan field menjadi baris teks di stderr. Karena penyaringan terjadi sebelum pemformatan, kejadian yang dibuang tidak menimbulkan biaya pencetakan sama sekali.
Diagram di atas juga menjelaskan pembagian kerja yang kita pakai sepanjang dua Chapter terakhir: instrumentasi hanya mengirim data, EnvFilter memutuskan nasibnya, dan layer format menentukan wujud akhirnya. Satu hal praktis sebelum menulis kode: EnvFilter tidak aktif secara bawaan. Kita perlu menyalakan feature env-filter pada deklarasi dependency.
[package]
name = "myapp"
version = "0.1.0"
edition = "2024"
[dependencies]
tracing = { version = "0.1", features = ["attributes"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }Filter RUST_LOG dengan EnvFilter #
Program uji kita memakai dua module supaya penyaringan target terlihat, dengan pola pemasangan subscriber yang menjadi resep standar aplikasi tracing:
use tracing::info;
use tracing_subscriber::EnvFilter;
mod gudang;
fn main() {
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
)
.init();
info!(pesanan_id = 42, "pesanan diterima");
tracing::debug!("membaca konfigurasi dari config.toml");
let stok = gudang::cek_stok("buku");
info!(stok, "pemeriksaan stok selesai");
}Module gudang menyumbang dua event dari dalam fungsinya:
pub fn cek_stok(barang: &str) -> u32 {
let stok = if barang == "buku" { 12 } else { 0 };
tracing::debug!(barang, "memeriksa stok");
tracing::info!(barang, stok, "stok diperiksa");
stok
}Mari kita bedah pemasangan subscribernya terlebih dahulu:
- Baris 7-11 (
tracing_subscriber::fmt() ... .init();): builderfmt()sekarang menerima filter lingkungan sebelum dipasang sebagai dispatcher global; urutan pemanggilan.with_env_filterbebas karena builder hanya mengumpulkan konfigurasi. - Baris 9 (
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"))):try_from_default_envmembacaRUST_LOGdan mengembalikanErrketika variabel tersebut tidak terisi atau berisi directive tidak valid;.unwrap_or_elsemengganti kegagalan itu denganEnvFilter::new("info")sehingga program tanpaRUST_LOGtetap menampilkan eventinfoke atas. - Baris 13-17: empat event dengan level dan target berbeda menjadi bahan uji filter — dua di module utama, dua di
myapp::gudang. - Baris 2-4 pada
gudang.rs: nilaistokdihitung lebih dulu, lalu eventdebug!daninfo!mencatat parameter beserta hasilnya sebagai field.
Jalankan tanpa RUST_LOG untuk melihat fallback bekerja:
cargo runOutput yang Dihasilkan:
2026-10-13T23:02:39.224528Z INFO myapp: pesanan diterima pesanan_id=42
2026-10-13T23:02:39.224568Z INFO myapp::gudang: stok diperiksa barang="buku" stok=12
2026-10-13T23:02:39.224574Z INFO myapp: pemeriksaan stok selesai stok=12Naikkan verbosity dengan sintaks level global yang sudah kita kenal dari env_logger:
RUST_LOG=debug cargo runOutput yang Dihasilkan:
2026-10-13T23:02:39.250204Z INFO myapp: pesanan diterima pesanan_id=42
2026-10-13T23:02:39.250240Z DEBUG myapp: membaca konfigurasi dari config.toml
2026-10-13T23:02:39.250245Z DEBUG myapp::gudang: memeriksa stok barang="buku"
2026-10-13T23:02:39.250249Z INFO myapp::gudang: stok diperiksa barang="buku" stok=12
2026-10-13T23:02:39.250252Z INFO myapp: pemeriksaan stok selesai stok=12Directive target=level dan kombinasi dengan koma juga bekerja dengan grammar yang sama:
RUST_LOG=myapp::gudang=debug cargo runOutput yang Dihasilkan:
2026-10-13T23:02:39.274977Z DEBUG myapp::gudang: memeriksa stok barang="buku"
2026-10-13T23:02:39.275000Z INFO myapp::gudang: stok diperiksa barang="buku" stok=12RUST_LOG=warn cargo runOutput yang Dihasilkan:
Empat eksekusi tersebut memperlihatkan kisah lengkapnya: fallback info menampilkan tiga baris saat RUST_LOG kosong, level global debug membuka seluruh detail, directive myapp::gudang=debug membatasi output pada module gudang saja, dan ambang warn memutuskan seluruh event karena tidak ada satu pun yang mencapai level tersebut. Nilai timestamp di setiap blok diambil dari eksekusi nyata di mesin penulis dan akan berbeda di mesin Anda — subscriber mencatat waktu kejadian dengan ketelitian mikrodetik pada saat setiap event dibangkitkan.
Anatomi Satu Baris Output #
Ambil satu baris dari eksekusi debug di atas dan bedah bagiannya:
2026-10-13T23:02:39.250245Z DEBUG myapp::gudang: memeriksa stok barang="buku"Tabel berikut memetakan setiap bagian:
| Bagian | Nilai pada contoh | Keterangan |
|---|---|---|
| Timestamp | 2026-10-13T23:02:39.250245Z | Waktu UTC berketelitian mikrodetik |
| Level | DEBUG | Ditata rata kanan dalam lima karakter |
| Konteks span | — | Muncul di antara level dan target bila event berada dalam span aktif |
| Target | myapp::gudang | Module path asal event |
| Pesan | memeriksa stok | Teks utama dari argumen terakhir macro |
| Field | barang="buku" | Pasangan kunci-nilai terstruktur dari event |
Perbandingan dengan env_logger pada Chapter sebelumnya menajamkan perbedaan keduanya. Format env_logger adalah [timestamp LEVEL target] pesan — tanpa field terstruktur dan dengan ketelitian detik — sedangkan format bawaan fmt() menempatkan field data setelah pesan dan mencatat mikrodetik. Ketika event dicatat di dalam span aktif, bagian konteks span seperti proses_pesanan{pesanan_id=42}: tampil di antara level dan target, persis seperti output Chapter Tracing Dasar.
Menyesuaikan Format Output #
Builder fmt() menerima penyesuaian berantai. Opsi pertama menyembunyikan target untuk baris yang lebih pendek — ganti rantai builder di awal main menjadi berikut:
tracing_subscriber::fmt()
.with_target(false)
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
)
.init();Output yang Dihasilkan (dengan RUST_LOG=debug cargo run):
2026-10-13T23:03:04.393557Z INFO pesanan diterima pesanan_id=42
2026-10-13T23:03:04.393584Z DEBUG membaca konfigurasi dari config.toml
2026-10-13T23:03:04.393588Z DEBUG memeriksa stok barang="buku"
2026-10-13T23:03:04.393592Z INFO stok diperiksa barang="buku" stok=12
2026-10-13T23:03:04.393595Z INFO pemeriksaan stok selesai stok=12Kolom myapp: dan myapp::gudang: lenyap dari setiap baris, sehingga baris lebih ringkas — kompromi yang pantas ketika satu aplikasi hanya punya sedikit module. Opsi kedua mengubah wujud total menjadi preset pretty():
tracing_subscriber::fmt()
.pretty()
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
)
.init();Output yang Dihasilkan (dengan RUST_LOG=debug cargo run):
2026-10-13T23:03:04.735006Z INFO myapp: pesanan diterima, pesanan_id: 42
at src/main.rs:14
2026-10-13T23:03:04.735039Z DEBUG myapp: membaca konfigurasi dari config.toml
at src/main.rs:15
2026-10-13T23:03:04.735044Z DEBUG myapp::gudang: memeriksa stok, barang: "buku"
at src/gudang.rs:3
2026-10-13T23:03:04.735049Z INFO myapp::gudang: stok diperiksa, barang: "buku", stok: 12
at src/gudang.rs:4
2026-10-13T23:03:04.735052Z INFO myapp: pemeriksaan stok selesai, stok: 12
at src/main.rs:18Format pretty() mengorbankan kerapatan demi keterbacaan: setiap event menempati blok tersendiri dengan indentasi, field dipisahkan koma, dan — bagian paling berharga saat menyelidiki masalah — baris at src/main.rs:14 yang menunjukkan lokasi kode persis tempat event ditulis. Nomor baris pada output di atas merujuk pada posisi event di berkas sumber setelah rantai builder diganti, dan nomor tersebut otomatis mengikuti kode kita selama kompilasi ulang.
Ringkasan #
tracing-subscriber melengkapi instrumentasi tracing dengan dua kenop yang siap produksi. EnvFilter membaca RUST_LOG dengan grammar level global, target=level, dan kombinasi koma — sintaks yang sama dengan env_logger — sementara pola try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")) menjamin fallback yang ramah ketika variabel lingkungan kosong. Layer format menentukan wujud baris: format bawaan menempatkan field terstruktur setelah pesan, with_target(false) memangkas kolom target, dan pretty() menukar kepadatan dengan blok verbose berisi lokasi kode. Dengan ini, perjalanan kita dari println! debug sampai sistem logging terkontrol lengkap: level yang terstandar, filter per module lewat variabel lingkungan, data terstruktur, dan konteks span — fondasi observability yang siap menopang aplikasi Rust skala berapa pun.