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:

KomponenPeranContoh populer
agent (shipper)membaca log di server sumber lalu mengirimnya lewat jaringanrsyslog, systemd-journal-remote, Filebeat, Fluentd
collectormenerima kiriman, menormalkan format, menambah konteks (hostname, service)rsyslog mode server, Logstash, Fluentd
storagemenyimpan dan mengindeks agar bisa dicari cepatElasticsearch, Loki
UItempat query dan dashboardKibana, 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:

bash
cat 20-forward.conf

Output yang Dihasilkan:

text
# /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:

  1. *.*: selector facility.priority — baca “semua facility, semua priority”. Seluruh log yang masuk ke rsyslog di server ini akan diteruskan; kita bisa mempersempit misalnya auth.* jika hanya ingin log autentikasi.
  2. action(type="omfwd" ...): mendeklarasikan action bertipe forward. Semua parameter di dalamnya mengatur ke mana dan bagaimana pengiriman dilakukan.
  3. target dan port: alamat collector — 192.0.2.10 pada port 514, konvensi syslog. Setiap server pengirim memasang berkas yang sama dengan target yang sama.
  4. 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.
  5. queue.*: antrian penyangga saat collector sedang tidak bisa dihubungi. linkedList menyimpan di memori, lalu meluber ke disk (queue.filename, dibatasi maxDiskSpace 100 MB) — server pengirim tidak kehilangan log hanya karena pusat sedang restart.
  6. 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 :

bash
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.jsonl

Output yang Dihasilkan:

text
== 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:

  1. select(.level == "error"): menyaring hanya objek yang field level-nya "error"; {msg, dur_ms} memproyeksikan dua field itu saja, dan -c mencetak hasil rapat satu baris per objek. Dua dari lima baris lolos saringan ini.
  2. jq -s: opsi slurp membaca seluruh baris menjadi satu array besar sehingga bisa diagregasi. group_by(.level) mengelompokkan dan sekaligus mengurutkan berdasarkan kunci.
  3. 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.

bash
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.log

Output yang Dihasilkan:

text
[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:

  1. 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.
  2. 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”.
  3. > pusat.log lalu >> 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:

AspekELK (Elasticsearch)Loki
Yang diindekssetiap field setiap barishanya label (host, service, level)
Kebutuhan sumber dayabesar (RAM dan CPU untuk index)kecil
Pencarian bebasya, di semua fieldpilih label dulu, baru isi di-scan
Cocok untukanalitik kompleks, tim besartim 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
bash
#!/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"