Mencegah log API membengkak bukan sekadar urusan menghemat storage. Masalah ini langsung memengaruhi kemampuan tim untuk melakukan debugging, menjaga biaya observability tetap terkendali, dan mencegah insiden kecil berubah menjadi gangguan operasional yang lebih besar. Dalam praktiknya, ledakan volume log sering muncul dari kombinasi yang tampak biasa: retry otomatis tanpa batas, body error yang terlalu besar, webhook yang dikirim berulang, dan idempotency yang lemah.
Sebagai konteks risiko, ada insiden bug logging pada Codex yang menunjukkan bagaimana perilaku logging yang tidak terkendali dapat memproduksi data berlebih di lingkungan lokal. Artikel ini tidak membahasnya sebagai berita, tetapi sebagai pengingat desain sistem: integrasi API yang tidak membatasi retry, tidak merangkum error, atau menyimpan payload mentah secara penuh dapat membuat log lokal maupun terpusat membengkak sangat cepat.
Mengapa log API bisa membengkak sangat cepat
Ledakan log biasanya bukan disebabkan satu faktor saja. Polanya sering berantai:
- Sebuah request gagal karena timeout atau respons downstream tidak stabil.
- Client melakukan retry agresif, kadang tanpa batas atau tanpa backoff.
- Setiap percobaan mencatat request dan response lengkap, termasuk body besar.
- Webhook juga ikut terkirim ulang karena pengirim tidak menerima ACK yang dianggap valid.
- Sisi penerima tidak memiliki idempotency yang kuat, sehingga duplikasi tetap diproses dan kembali menghasilkan log baru.
Jika log dikirim ke sistem terpusat, masalah membesar lagi: ingestion naik, indexing melambat, dashboard jadi bising, alert kehilangan sinyal penting, dan biaya penyimpanan ikut melonjak. Jika log disimpan lokal, disk bisa cepat habis dan mengganggu proses aplikasi itu sendiri.
Sumber pembengkakan yang paling umum
- Retry tanpa batas: loop retry di aplikasi, queue worker, atau gateway.
- Payload/error body besar: HTML error page, stack trace panjang, payload JSON penuh, atau file yang ikut terekam.
- Webhook duplikat: pengirim melakukan redelivery karena ACK terlambat atau tidak sesuai.
- Idempotency lemah: event yang sama diproses berulang karena tidak ada deduplikasi yang andal.
- Logging terlalu verbose di jalur panas: semua request sukses maupun gagal dicatat dengan tingkat detail yang sama.
- Tidak ada redaksi: token, cookie, Authorization header, atau PII dicatat mentah dan memperbesar ukuran serta risiko keamanan.
Desain kontrak API yang membantu mengendalikan log
Salah satu akar masalah ada di kontrak API itu sendiri. Jika API mengembalikan error yang terlalu besar, terlalu bebas, atau tidak konsisten, client cenderung menyimpan seluruh body untuk debugging. Hasilnya adalah log yang padat, mahal, dan sulit dicari.
Buat error ringkas dan terstruktur
Untuk kasus gagal, prioritaskan field yang stabil dan cukup untuk investigasi:
- code: kode error mesin, misalnya PAYMENT_TIMEOUT atau INVALID_SIGNATURE.
- message: pesan singkat yang aman ditampilkan ke operator.
- correlation_id: ID untuk menelusuri request lintas layanan.
- retryable: petunjuk apakah aman untuk dicoba ulang.
- details opsional: ringkas, tidak berisi dump objek penuh.
Hindari mengembalikan stack trace internal, query SQL, atau representasi objek lengkap dalam response API publik. Jika detail internal diperlukan, simpan di server dan hubungkan dengan correlation ID, bukan kirim semuanya ke client.
Gunakan correlation ID sejak awal
Correlation ID mengurangi kebutuhan menulis payload besar ke log. Dengan satu ID yang konsisten, tim bisa menelusuri request dari gateway, service, queue worker, sampai webhook consumer tanpa menduplikasi seluruh konteks di setiap titik.
Prinsipnya sederhana:
- Jika request masuk sudah memiliki correlation ID, teruskan ke downstream.
- Jika belum ada, buat ID baru di edge service.
- Masukkan ID itu ke response dan ke semua log yang relevan.
Tambahkan idempotency key untuk operasi yang bisa terulang
Untuk operasi yang bisa terkena retry, seperti pembayaran, pembuatan order, atau callback webhook, gunakan idempotency key. Tujuannya bukan hanya mencegah data ganda, tetapi juga mencegah log amplification dari operasi yang sama diproses berkali-kali.
Idempotency key yang baik:
- Dibuat per intent bisnis, bukan per koneksi TCP atau per attempt.
- Disimpan bersama hasil operasi pertama.
- Memiliki TTL yang sesuai dengan jendela retry realistis.
- Memvalidasi bahwa request dengan key yang sama memang request yang sama, bukan payload berbeda.
Alur buruk vs alur aman
Contoh alur yang buruk
- Client mengirim POST /payments dengan body besar.
- Downstream lambat, client timeout setelah 3 detik.
- Retry berjalan tanpa backoff dan tanpa batas attempt.
- Setiap gagal, aplikasi menulis seluruh request body, seluruh response body, dan stack trace panjang ke log.
- Penyedia webhook mengirim event pembayaran 5 kali karena ACK lambat.
- Consumer webhook tidak mengecek event ID atau idempotency key, sehingga event yang sama diproses berulang.
- Setiap duplikasi memicu request baru ke downstream dan log baru lagi.
Hasilnya: satu gangguan kecil berubah menjadi ledakan log, duplicate side effect, biaya observability naik, dan operator kesulitan mencari akar masalah karena sinyal tenggelam oleh noise.
Contoh alur yang aman
- Client mengirim POST /payments dengan Idempotency-Key dan X-Correlation-ID.
- API menyimpan status awal request berdasarkan idempotency key.
- Jika timeout terjadi, client retry dengan exponential backoff dan jumlah attempt maksimum.
- Setiap log hanya mencatat metadata penting: method, path, status, latency, correlation ID, idempotency key, dan hash/ringkasan payload.
- Response error hanya mengandung code, message, retryable, dan correlation_id.
- Webhook consumer memverifikasi signature, mengecek event ID yang sudah pernah diproses, lalu meng-ACK cepat.
- Jika pemrosesan gagal berulang, event dipindah ke dead-letter queue, bukan diulang tanpa henti.
Dengan pola ini, kegagalan masih bisa dilacak, tetapi tidak memperbanyak log secara tidak terkendali.
Implementasi praktis: apa yang sebaiknya dicatat, dibatasi, dan disembunyikan
Catat metadata, bukan payload mentah penuh
Default yang aman untuk log request/response:
- timestamp
- service name
- method dan route
- status code
- latency
- correlation ID
- idempotency key jika ada
- retry attempt
- ukuran request/response body
- hash payload atau ringkasan field penting
Jika benar-benar perlu menyimpan body, lakukan secara selektif dan terbatasi ukuran. Untuk kebanyakan endpoint produksi, menyimpan full body di setiap request adalah anti-pattern.
Terapkan batas ukuran log per event
Satu event log sebaiknya memiliki ukuran maksimum. Jika data melebihi batas:
- potong body dan tambahkan penanda truncated=true
- simpan hanya beberapa field yang diizinkan
- gantikan blob besar dengan hash, ukuran byte, atau lokasi artefak terpisah yang dilindungi
Ini penting untuk mencegah satu error page HTML, satu dump JSON besar, atau satu exception chain panjang memenuhi pipeline log.
Redaksi token, secret, dan PII
Redaksi bukan hanya kebutuhan keamanan, tetapi juga mengurangi ukuran log. Header Authorization, cookie sesi, access token, API key, nomor telepon, email, alamat, dan data identitas tidak seharusnya ditulis mentah ke log umum.
Prinsip redaksi yang praktis:
- deny by default untuk field sensitif yang dikenal
- masking parsial jika operator masih perlu konteks, misalnya 4 karakter terakhir
- jangan log file biner atau base64 penuh
- normalisasi error agar data pengguna tidak ikut terpantul ke log
// Contoh pseudocode middleware logging yang aman
function sanitize(headers, body) {
const redactedHeaders = { ...headers };
const sensitiveHeaders = ['authorization', 'cookie', 'set-cookie', 'x-api-key'];
for (const key of sensitiveHeaders) {
if (redactedHeaders[key]) redactedHeaders[key] = '[REDACTED]';
}
const allowlist = ['order_id', 'customer_id', 'event_type'];
const sanitizedBody = {};
for (const key of allowlist) {
if (body[key] !== undefined) sanitizedBody[key] = body[key];
}
return { headers: redactedHeaders, body: sanitizedBody };
}Pendekatan allowlist untuk body umumnya lebih aman daripada mencoba menutupi semua field sensitif dengan blocklist.
Gunakan sampling untuk jalur bervolume tinggi
Tidak semua request sukses perlu dicatat penuh. Untuk endpoint bertrafik tinggi:
- simpan metrik agregat untuk semua request
- log detail hanya untuk error, latency tinggi, atau sampel kecil request sukses
- naikkan sampling sementara saat investigasi insiden
Trade-off-nya jelas: sampling mengurangi detail historis, tetapi menjaga sistem observability tetap sehat. Kuncinya adalah memastikan error penting dan anomali tetap tidak tersampling keluar.
Retry yang aman: backoff, batas attempt, dan dead-letter queue
Jangan retry semua error
Kesalahan umum adalah menganggap semua kegagalan layak di-retry. Padahal:
- 4xx tertentu seperti validasi gagal biasanya tidak akan berhasil dengan retry.
- 401/403 perlu perbaikan kredensial atau otorisasi, bukan pengulangan buta.
- 5xx, timeout, atau koneksi terputus lebih masuk akal untuk retry terbatas.
Kontrak API yang menyediakan flag retryable membantu client membedakan kegagalan yang layak diulang dari yang harus gagal cepat.
Pakai exponential backoff dengan jitter
Exponential backoff mengurangi tekanan serentak ke layanan yang sedang bermasalah. Menambahkan jitter mencegah banyak client melakukan retry persis di waktu yang sama.
// Pseudocode retry sederhana
maxAttempts = 5
baseDelayMs = 500
for attempt in 1..maxAttempts:
result = callApi()
if result.success:
return result
if !result.retryable:
break
delay = randomBetween(0, baseDelayMs * (2 ^ (attempt - 1)))
sleep(delay)
sendToDeadLetterQueue(context)Batas attempt harus eksplisit. Retry tak terbatas hampir selalu berbahaya untuk log, biaya, dan stabilitas downstream.
Gunakan dead-letter queue untuk kegagalan persisten
Jika pesan queue atau event webhook gagal terus, pindahkan ke dead-letter queue setelah jumlah percobaan tertentu. Ini mencegah satu pesan rusak atau satu ketergantungan yang sedang down menghasilkan banjir log tanpa akhir.
DLQ efektif jika disertai:
- metadata alasan gagal terakhir
- jumlah attempt
- correlation ID
- timestamp pertama dan terakhir gagal
- prosedur replay yang aman dan idempotent
Idempotency untuk request API dan webhook
Request API masuk
Untuk endpoint yang menciptakan side effect, seperti POST /orders atau POST /payments, simpan hasil berdasarkan idempotency key. Jika key yang sama datang lagi:
- jika payload sama, kembalikan hasil yang sama tanpa mengeksekusi ulang
- jika payload berbeda, tolak sebagai konflik agar bug client terlihat jelas
Tanpa aturan ini, retry client bisa menimbulkan duplikasi transaksi sekaligus duplikasi log pemrosesan.
Webhook masuk
Webhook sering dirancang sebagai at-least-once delivery. Artinya, duplikasi adalah perilaku normal, bukan anomali. Karena itu, consumer wajib menyimpan identitas event yang sudah diproses.
Praktik yang aman:
- verifikasi signature sebelum memproses
- cek event ID di storage cepat
- jika sudah pernah diproses, kembalikan ACK sukses tanpa side effect baru
- pisahkan ACK dari pekerjaan berat bila memungkinkan
// Pseudocode webhook consumer
function handleWebhook(event) {
verifySignature(event)
if (hasProcessed(event.id)) {
return 200
}
markProcessing(event.id)
try {
processBusinessLogic(event)
markProcessed(event.id)
return 200
} catch (err) {
unmarkProcessingOrExpire(event.id)
throw err
}
}Detail implementasi markProcessing perlu hati-hati agar aman terhadap race condition. Biasanya ini dilakukan dengan operasi atomik di database atau cache yang mendukung set-if-not-exists.
Alert dan observability: awasi volume log, bukan hanya error rate
Banyak tim punya alert untuk CPU, latency, dan error rate, tetapi tidak untuk volume log. Padahal log yang membengkak sering menjadi gejala awal bug retry, loop queue, atau webhook duplikat.
Sinyal yang perlu dipantau
- jumlah event log per menit per service
- rata-rata dan persentil ukuran log event
- rasio retry terhadap request sukses
- jumlah pesan yang masuk DLQ
- jumlah webhook duplikat yang ditolak
- persentase log yang terpotong karena batas ukuran
Alert volume log membantu tim menemukan masalah lebih cepat, bahkan sebelum pengguna melaporkan gejala bisnis yang lebih serius.
Catatan operasional: jika sistem logging terpusat mulai menelan volume besar secara tiba-tiba, pertimbangkan rate limiting atau penurunan level log sementara pada komponen yang bermasalah. Ini bukan perbaikan akar masalah, tetapi bisa mencegah observability stack ikut tumbang saat insiden berlangsung.
Anti-pattern umum yang sebaiknya dihindari
- Log semua request dan response body di production tanpa pembatasan ukuran.
- Retry tanpa batas di HTTP client, worker queue, dan scheduler sekaligus.
- Menganggap webhook exactly-once lalu tidak menyimpan event ID.
- Menggunakan idempotency key tetapi tidak mengikatnya ke payload, sehingga request berbeda bisa dianggap sama.
- Menyimpan token, cookie, atau PII mentah demi debugging cepat.
- Menulis stack trace panjang untuk setiap retry alih-alih sekali per insiden atau per kelompok error.
- Tidak membedakan error retryable dan non-retryable.
- Tidak punya retention dan batas ukuran log lokal, sehingga disk host mudah penuh.
Checklist implementasi
- Tentukan field log minimum untuk semua request: method, route, status, latency, correlation ID, ukuran body, retry attempt.
- Terapkan redaksi header dan body dengan pendekatan allowlist.
- Batasi ukuran setiap event log, dan tandai jika dipotong.
- Gunakan kontrak error ringkas: code, message, retryable, correlation_id.
- Tambahkan idempotency key untuk endpoint yang punya side effect.
- Simpan hasil request pertama dan tolak konflik jika key dipakai dengan payload berbeda.
- Pastikan webhook consumer melakukan deduplikasi berbasis event ID.
- Terapkan exponential backoff, jitter, dan jumlah attempt maksimum.
- Kirim kegagalan persisten ke dead-letter queue, bukan retry tanpa henti.
- Buat alert untuk lonjakan volume log, ukuran log, retry ratio, dan pertumbuhan DLQ.
- Uji skenario timeout, redelivery webhook, dan partial failure di staging.
- Audit log untuk memastikan token, secret, dan PII tidak bocor.
Penutup
Mencegah log API membengkak membutuhkan kombinasi desain kontrak, kontrol retry, idempotency yang benar, dan kebijakan logging yang disiplin. Jika Anda hanya memperbaiki satu sisi saja, misalnya menambah kapasitas storage log, masalah inti tetap ada: sistem masih memperbanyak noise setiap kali ada kegagalan.
Pendekatan yang paling efektif adalah membuat kegagalan menjadi terlihat tetapi tidak berisik. Artinya, cukup data untuk investigasi, namun tidak berlebihan; retry yang membantu pemulihan, namun tidak menciptakan badai request; dan webhook yang toleran terhadap duplikasi, namun tidak menimbulkan side effect berulang. Dengan itu, insiden kecil tidak mudah berubah menjadi ledakan log dan gangguan operasional yang mahal.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!