Insiden pukul tiga pagi selalu berujung pada satu pertanyaan: ke mana kita harus melihat? Aplikasi api-toko berjalan di app-01 sampai app-50, dan pesan error di aplikasi biasanya hanya separuh cerita — akar masalahnya sering berada di log database atau load balancer di mesin lain. Membuka lima puluh sesi SSH dan mengetik grep satu per satu bukan jawaban; itu cara membuang satu jam di navigasi ketika yang dibutuhkan adalah satu menit analisis. Chapter ini membangun arsitektur pengumpulan log terpusat: agent di tiap server, collector yang menampung, storage yang bisa dicari, dan UI untuk membacanya. Karena rsyslog dan Elasticsearch tidak berjalan di container latihan ini, kita bedah konfigurasinya lewat fixture dan menguji pola kuncinya — format JSON serta agregasi jq — secara sungguhan.
Masalah: Mencari Jejak di Lima Puluh Server #
Log yang tersebar menciptakan tiga masalah yang saling menguatkan. Pertama, korelasi lintas server hampir mustahil — timestamp tiap mesin bisa bergeser beberapa detik dan format log tiap aplikasi berbeda-beda, sehingga menyusun ulang urutan kejadian menjadi pekerjaan detektif. Kedua, retensi tidak merata: /var/log di satu server sudah dirotasi setiap hari, di server lain menumpuk sampai disk penuh. Ketiga, begitu server mati atau disk-nya diganti, bukti forensiknya ikut hilang. Solusi ketiganya sama: log dikirim keluar dari server asalnya seketika, menyatu di satu tempat dengan format yang seragam.
Arsitektur: Agent, Collector, Storage, UI #
Semua sistem log agregasi — dari rsyslog sederhana sampai ELK — tersusun dari empat peran yang sama:
| Komponen | Peran | Contoh populer |
|---|---|---|
| agent (shipper) | membaca log di server sumber lalu mengirimnya lewat jaringan | rsyslog, systemd-journal-remote, Filebeat, Fluentd |
| collector | menerima kiriman, menormalkan format, menambah konteks (hostname, service) | rsyslog mode server, Logstash, Fluentd |
| storage | menyimpan dan mengindeks agar bisa dicari cepat | Elasticsearch, Loki |
| UI | tempat query dan dashboard | Kibana, Grafana |
rsyslog adalah pilihan paling ringan untuk Debian karena sudah terpasang secara default dan bisa berperan sebagai agent sekaligus collector. Jika log utama Anda ada di journald, ada jalur khusus systemd-journal-remote yang mengirim binary journal — lengkap dengan metadata unit — ke server pusat (lihat materi journalctl
). Filebeat dan Fluentd adalah shipper pihak ketiga yang biasa dipakai untuk membaca file log aplikasi milik framework web yang tidak menulis ke syslog.
rsyslog: Meneruskan Log dengan omfwd #
Di sisi pengirim, rsyslog memakai action omfwd (output module forward) untuk mengirim log ke jauh. Berikut fixture konfigurasi yang di produksi disimpan di /etc/rsyslog.d/20-forward.conf:
cat 20-forward.confOutput yang Dihasilkan:
# /etc/rsyslog.d/20-forward.conf — teruskan log ke collector terpusat.
# Dipasang di SETIAP server pengirim (app-01 s.d. app-50).
*.* action(type="omfwd"
target="192.0.2.10"
port="514"
protocol="tcp"
queue.type="linkedList"
queue.filename="fwd-queue"
queue.maxDiskSpace="100m"
queue.saveOnShutdown="on"
action.resumeRetryCount="-1")Mari kita bedah bagian demi bagiannya:
*.*: selector facility.priority — baca “semua facility, semua priority”. Seluruh log yang masuk ke rsyslog di server ini akan diteruskan; kita bisa mempersempit misalnyaauth.*jika hanya ingin log autentikasi.action(type="omfwd" ...): mendeklarasikan action bertipe forward. Semua parameter di dalamnya mengatur ke mana dan bagaimana pengiriman dilakukan.targetdanport: alamat collector —192.0.2.10pada port 514, konvensi syslog. Setiap server pengirim memasang berkas yang sama dengan target yang sama.protocol="tcp": TCP menjamin urutan dan ada konfirmasi pengiriman. UDP lebih cepat tetapi diam-diam bisa menjatuhkan pesan saat jaringan ramai — untuk audit keamanan, TCP adalah pilihan yang benar.queue.*: antrian penyangga saat collector sedang tidak bisa dihubungi.linkedListmenyimpan di memori, lalu meluber ke disk (queue.filename, dibatasimaxDiskSpace100 MB) — server pengirim tidak kehilangan log hanya karena pusat sedang restart.action.resumeRetryCount="-1": coba kirim ulang selamanya, jangan pernah membuang log karena pusat lama tak respons.
Setelah berkas dipasang, sudo systemctl restart rsyslog mengaktifkannya — pola pasang-lalu-restart dari Chapter Managing Services
. Di sisi collector, rsyslog dikonfigurasi kebalikan (menerima di port 514 dengan modul imtcp) dan menulis ke file per-host seperti /var/log/remote/app-01/. Halaman manual rsyslogd(8) merinci seluruh modul input-output yang tersedia.
Format JSON: Log untuk Mesin #
Log terpusat hanya berguna jika bisa ditanyai cepat, dan itu menuntut struktur. Satu baris log aplikasi dalam format JSON — dengan field ts, level, svc, msg — bisa disaring, dihitung, dan diagregasi tanpa menebak-nebak pola teks. Fixture app-toko.jsonl berisi lima baris contoh (JSON Lines: satu objek JSON per baris). Kita olah dengan jq dari Chapter jq dan JSON Processing
:
echo "== baris level error =="
jq -c 'select(.level == "error") | {msg, dur_ms}' app-toko.jsonl
echo "== jumlah baris per level =="
jq -s 'group_by(.level) | map({level: .[0].level, jumlah: length})' app-toko.jsonlOutput yang Dihasilkan:
== baris level error ==
{"msg":"timeout ke database","dur_ms":3001}
{"msg":"timeout ke database","dur_ms":2998}
== jumlah baris per level ==
[
{
"level": "error",
"jumlah": 2
},
{
"level": "info",
"jumlah": 2
},
{
"level": "warn",
"jumlah": 1
}
]Bedah dua query di atas:
select(.level == "error"): menyaring hanya objek yang fieldlevel-nya"error";{msg, dur_ms}memproyeksikan dua field itu saja, dan-cmencetak hasil rapat satu baris per objek. Dua dari lima baris lolos saringan ini.jq -s: opsi slurp membaca seluruh baris menjadi satu array besar sehingga bisa diagregasi.group_by(.level)mengelompokkan dan sekaligus mengurutkan berdasarkan kunci.map({level: .[0].level, jumlah: length}): tiap kelompok diubah menjadi objek kecil berisi nama level dan jumlah anggotanya. Hasil akhirnya adalah jawaban “berapa banyak error dibanding info” — pertanyaan pertama di setiap investigasi insiden — dalam satu query, bukan lima puluh sesi SSH.
Forwarder Mini di Sandbox #
Karena daemon rsyslog tidak berjalan di container latihan, kita tiru pola agent → collector dengan skrip mini forward-log.sh: baca sebuah file log, tambahkan label nama server, cetak ke stdout yang nanti diarahkan ke satu file pusat.
mkdir -p server-a server-b
printf 'api-toko: request selesai (200)\napi-toko: timeout ke database\n' > server-a/api.log
printf 'api-toko: request selesai (200)\n' > server-b/api.log
bash forward-log.sh server-a server-a/api.log > pusat.log
bash forward-log.sh server-b server-b/api.log >> pusat.log
cat pusat.logOutput yang Dihasilkan:
[server-a] api-toko: request selesai (200)
[server-a] api-toko: timeout ke database
[server-b] api-toko: request selesai (200)Bedah skrip dan bloknya:
while IFS= read -r baris; do ... done < "$berkas": membaca file baris demi baris tanpa memotong spasi — pola yang sama dengan membaca input apa pun di skrip shell.printf '[%s] %s\n' "$server" "$baris": menempelkan label[server-a]di depan setiap baris. Begitu log dari banyak mesin menyatu, label asal inilah yang menyelamatkan kita dari teka-teki “kejadian ini terjadi di mana”.> pusat.loglalu>> pusat.log: berkas pusat dibuat sekali, lalu ditambah dari setiap “server”. Ini miniatur dari collector yang menerima kiriman banyak agent ke satu penyimpanan.
Di produksi, penambahan label, timestamp seragam, dan penguraian format dilakukan otomatis oleh collector — rsyslog menyisipkan hostname dan waktu penerimaan, Logstash bisa mengurai field tambahan dari teks mentah.
ELK dan Alternatifnya #
Ketika tim dan volume log tumbuh, file pusat tidak lagi memadai — di situlah stack pencari seperti ELK masuk. Elasticsearch adalah search engine yang mengindeks setiap field sehingga query “semua error di api-toko satu jam terakhir” kembali dalam hitungan detik; Logstash adalah pipeline collector-nya (parse, enrich, route); Kibana adalah UI untuk query dan dashboard. Retensi diatur per index: log ditulis ke index harian seperti app-toko-2026.02.10, dan kebijakan lifecycle menghapus index yang lebih tua dari 30 hari — log keamanan bisa disimpan lebih lama, log debug lebih pendek. Alternatif yang jauh lebih ringan adalah Loki: hanya label (host, service, level) yang diindeks, sementara isi log disimpan terkompresi dan di-scan saat query. Perbandingannya:
| Aspek | ELK (Elasticsearch) | Loki |
|---|---|---|
| Yang diindeks | setiap field setiap baris | hanya label (host, service, level) |
| Kebutuhan sumber daya | besar (RAM dan CPU untuk index) | kecil |
| Pencarian bebas | ya, di semua field | pilih label dulu, baru isi di-scan |
| Cocok untuk | analitik kompleks, tim besar | tim kecil, volume sedang, sudah pakai Grafana |
Pilihan di antara keduanya adalah soal skala dan anggaran, bukan kebenaran — keduanya menuntut log terstruktur dan agent yang andal di sumbernya, dua hal yang sudah kita bahas di atas.
Log terpusat menjawab pertanyaan “apa yang sudah terjadi”. Bab berikutnya menjawab pertanyaan yang lebih tajam: “apakah layanan ini sehat sekarang, sebelum pengguna yang memberi tahu kita?” — lewat health check dan alerting di Chapter Uptime dan Health Checks .
Referensi lanjut: halaman manual rsyslogd
untuk modul input-output, serta situs jq
untuk bahasa query lengkapnya.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
forward-log.sh
Unduh berkas#!/usr/bin/env bash
# forward-log.sh — kirim isi file log ke collector terpusat (simulasi lokal).
# Pemakaian: bash forward-log.sh <nama-server> <file-log>
server="${1:-server-kantor}"
berkas="${2:-app.log}"
if [ ! -f "$berkas" ]; then
echo "tidak ada log untuk diteruskan: $berkas"
exit 0
fi
while IFS= read -r baris; do
printf '[%s] %s\n' "$server" "$baris"
done < "$berkas"