console.log di Production: Performa, Risiko Data, dan Pengukuran

Dampak logging terhadap latency, CPU, data sensitif, dan biaya observability—beserta cara benchmark dan perbaikannya.

· · 8 min read

console.log di Production: Dampak Performa, Risiko Data, dan Cara Mengukurnya

console.log() dapat menjadi bottleneck, tetapi klaim “selalu synchronous” atau “selalu asynchronous” tidak akurat. Dokumentasi Node.js secara eksplisit mengingatkan bahwa metode global console tidak konsisten sinkron atau asynchronous: perilakunya bergantung pada platform dan stream tujuan.

Masalah production biasanya merupakan kombinasi volume log, biaya serialisasi, backpressure output, dan pipeline container—bukan nama fungsi console.log semata.

---

Biaya Terjadi Sebelum dan Saat Menulis

Contoh berikut melakukan lebih dari satu pekerjaan:

console.log('request body', JSON.stringify(req.body));

Biayanya dapat mencakup:

1. traversal dan serialisasi object;
2. formatting argumen oleh console;
3. alokasi string dan garbage collection;
4. penulisan ke stdout/stderr;
5. backpressure ketika consumer log lebih lambat;
6. pengiriman, parsing, indexing, dan penyimpanan di platform observability.

Jika body besar, serialisasi dapat mahal walaupun level log akhirnya tidak dibutuhkan. Log mentah juga berisiko memuat password, token, cookie, data pribadi, atau payment information.

---

Jangan Memakai Angka Benchmark Orang Lain

Efek log berbeda antara terminal lokal, file, pipe, Docker logging driver, systemd journal, dan platform serverless. Ukur konfigurasi yang mendekati production.

Contoh server uji:

import http from 'node:http';

const verbose = process.env.VERBOSE_LOG === '1';

const server = http.createServer((request, response) => {
if (verbose) {
console.log('request', {
method: request.method,
url: request.url,
requestId: request.headers['x-request-id'],
});
}

response.writeHead(200, { 'content-type': 'application/json' });
response.end('{"ok":true}');
});

server.listen(3000);

Jalankan benchmark dua kali dengan kondisi identik:

VERBOSE_LOG=0 node server.mjs
npx autocannon -c 50 -d 30 http://127.0.0.1:3000/

VERBOSE_LOG=1 node server.mjs
npx autocannon -c 50 -d 30 http://127.0.0.1:3000/

Catat throughput, p50/p95/p99 latency, CPU, event-loop delay, memory, byte log per detik, dan apakah pipeline log membatasi output. Warm-up dan ulangi beberapa kali agar hasil tidak bergantung pada satu run.

Benchmark ini bukan angka production, tetapi dapat membuktikan arah dan besarnya pengaruh pada environment Anda.

---

Structured Logging Bukan Otomatis Cepat

Logger terstruktur memudahkan filtering dan query:

logger.info(
{
requestId,
route: '/api/orders/:id',
statusCode: response.statusCode,
durationMs,
},
'request completed',
);

Tetapi mengganti console.log dengan library tidak otomatis menyelesaikan semuanya. Konfigurasi transport, destination, redaction, serializer, dan level tetap menentukan biaya.

Prinsip yang berguna:

• pilih level (debug, info, warn, error) secara konsisten;
• matikan debug di production kecuali sedang investigasi terkontrol;
• log identifier dan hasil, bukan seluruh object;
• gunakan redaction untuk field sensitif;
• hindari membuat payload mahal sebelum memeriksa level;
• kirim log melalui pipeline yang memiliki buffering dan backpressure yang dipahami.

Contoh lazy guard:

if (logger.isLevelEnabled('debug')) {
logger.debug(
{ orderSummary: buildOrderSummary(order) },
'order calculation details',
);
}

---

Data yang Tidak Boleh Masuk Log

Hindari logging langsung:

console.log(req.headers);
console.log(req.body);
console.log(user);
console.log(process.env);

Field yang sering bocor:

• authorization, cookie, session ID, dan API key;
• password dan reset token;
• nomor identitas, alamat, telepon, dan email lengkap;
• connection string;
• payload pembayaran;
• prompt atau file pengguna yang bersifat privat.

Gunakan allowlist:

logger.info({
requestId,
method: request.method,
route: matchedRoute,
statusCode,
durationMs,
});

Untuk nilai yang perlu korelasi tetapi tidak perlu dibaca operator, pertimbangkan pseudonymous stable ID atau hash dengan desain keamanan yang benar. Jangan menganggap hash tanpa salt otomatis anonim.

---

Sampling untuk Traffic Tinggi

Tidak semua request sukses perlu satu baris log. Pada traffic tinggi:

• simpan seluruh error dan warning yang relevan;
• sample sebagian request sukses;
• tingkatkan sampling sementara saat insiden;
• gunakan metrics untuk agregat seperti request count dan latency;
• gunakan tracing untuk alur lintas service.

Sampling harus menjaga konteks. Jika satu trace dipilih, pertahankan keputusan sampling secara konsisten agar potongan alurnya tidak hilang.

---

Backpressure dan Kegagalan Pipeline

Pertanyaan penting bukan hanya “berapa cepat logger?”, tetapi:

• apa yang terjadi jika stdout consumer macet;
• apakah aplikasi menunggu, membuang log, atau menumpuk memory;
• apakah disk lokal dapat penuh;
• apakah shutdown memberi waktu untuk flush;
• berapa batas ukuran dan retensi;
• bagaimana operator mengetahui log sedang terbuang.

Tidak ada pilihan tanpa trade-off. Logging sinkron lebih mudah mempertahankan urutan tetapi dapat menahan request. Buffer asynchronous mengurangi blocking di hot path, tetapi membutuhkan batas buffer, kebijakan drop, dan flush saat shutdown.

---

Bedakan Log, Metrics, dan Trace

Memaksa semua observability masuk ke log biasanya menghasilkan volume besar tetapi konteks kecil. Gunakan metrics untuk angka agregat berulang seperti request count, error rate, queue depth, dan latency histogram. Gunakan trace untuk melihat hubungan antar-service dan span yang lambat. Gunakan log untuk peristiwa diskret yang membutuhkan detail manusia.

Sebagai contoh, jangan menulis satu log setiap kali cache hit. Tambahkan counter cache_hits_total. Ketika upstream gagal, log satu event terstruktur dengan request ID, target yang telah disanitasi, durasi, dan error code; trace dapat menyimpan span request terkait.

Pendekatan ini mengurangi biaya tanpa membuat sistem buta. Dashboard dan alert juga menjadi lebih stabil karena tidak bergantung pada parsing kalimat log yang dapat berubah.

---

Checklist Perbaikan

1. Ukur volume dan byte log per route/level.
2. Bandingkan performa dengan logging verbose aktif dan nonaktif.
3. Hapus payload mentah dan terapkan allowlist/redaction.
4. Gunakan level, sampling, metrics, dan tracing sesuai fungsi.
5. Periksa backpressure pada environment deployment yang sebenarnya.
6. Uji shutdown dan kegagalan destination.
7. Tetapkan budget retensi dan biaya ingestion.

Tujuannya bukan menghapus semua log. Production tetap membutuhkan bukti untuk debugging dan audit. Tujuannya adalah menghasilkan log yang sedikit, aman, terstruktur, dan berguna.

Referensi

• Node.js: Console
• Node.js: Process I/O
• Pino: Redaction
• OpenTelemetry: Logs