Structured Logging — Debugging

Structured Logging Structured logging adalah teknik menulis log dalam format yang terstruktur (biasanya JSON) agar mudah dicari, difilter, dan dianalisis —…

Structured Logging

Structured logging adalah teknik menulis log dalam format yang terstruktur (biasanya JSON) agar mudah dicari, difilter, dan dianalisis — terutama saat ada ribuan log dari banyak server.

Masalah dengan console.log Biasa

// ❌ Unstructured log — susah di-parse dan dicari
console.log("User login failed for [email protected] at 2024-01-15 10:30");
console.log("Retry attempt 3 for request to /api/orders");
console.log("Payment processed: 150000 IDR");

Dengan log seperti ini, jika ingin mencari semua kegagalan login user tertentu di production dengan ribuan request — hampir mustahil.

Structured Log Format

// ✅ Structured log — mudah dicari dan dianalisis
{
  "level": "warn",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "context": "auth",
  "message": "Login failed",
  "userId": null,
  "email": "[email protected]",
  "reason": "invalid_password",
  "attempt": 3,
  "ip": "192.168.1.1"
}

Membuat Logger Sederhana

const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3, fatal: 4 };

function createLogger(context, minLevel = "info") {
  function log(level, message, meta = {}) {
    if (LOG_LEVELS[level] < LOG_LEVELS[minLevel]) return;

    const entry = {
      level,
      timestamp: new Date().toISOString(),
      context,
      message,
      ...meta,
    };

    // Output JSON ke console
    const output = JSON.stringify(entry);

    if (level === "error" || level === "fatal") {
      console.error(output);
    } else if (level === "warn") {
      console.warn(output);
    } else {
      console.log(output);
    }
  }

  return {
    debug: (msg, meta) => log("debug", msg, meta),
    info: (msg, meta) => log("info", msg, meta),
    warn: (msg, meta) => log("warn", msg, meta),
    error: (msg, meta) => log("error", msg, meta),
    fatal: (msg, meta) => log("fatal", msg, meta),
  };
}

// Penggunaan
const logger = createLogger("payment-service");

logger.info("Payment initiated", { userId: 42, amount: 150000 });
logger.warn("Retry attempt", { requestId: "abc123", attempt: 2 });
logger.error("Payment failed", { orderId: "ORD-001", reason: "insufficient_funds" });

Log Levels

Gunakan level yang tepat untuk setiap situasi:

Level Kapan Digunakan
DEBUG Info detail untuk debugging (hanya di development)
INFO Event normal yang penting (user login, order created)
WARN Situasi tidak normal tapi tidak menyebabkan error
ERROR Kesalahan yang perlu ditangani
FATAL Kesalahan fatal — aplikasi tidak bisa lanjut

Filtering Logs

function filterLogs(logs, minLevel) {
  const minValue = LOG_LEVELS[minLevel] ?? 0;
  return logs.filter(log => (LOG_LEVELS[log.level] ?? 0) >= minValue);
}

// Ambil hanya warn ke atas di production
const productionLogs = filterLogs(allLogs, "warn");

// Ambil semua log di development
const devLogs = filterLogs(allLogs, "debug");

Context dan Correlation ID

Untuk melacak satu request di banyak service:

function createRequestLogger(requestId, userId) {
  const base = createLogger("api");

  return {
    info: (msg, meta) => base.info(msg, { requestId, userId, ...meta }),
    error: (msg, meta) => base.error(msg, { requestId, userId, ...meta }),
    warn: (msg, meta) => base.warn(msg, { requestId, userId, ...meta }),
  };
}

// Di setiap request
app.use((req, res, next) => {
  req.log = createRequestLogger(req.headers["x-request-id"] ?? crypto.randomUUID(), req.user?.id);
  next();
});

Tips Structured Logging

  1. Selalu sertakan timestamp dalam ISO 8601
  2. Gunakan level yang tepat — jangan semua pakai error
  3. Sertakan context — modul atau service mana yang log ini
  4. Tambahkan request ID untuk tracing antar service
  5. Jangan log data sensitif — password, token, nomor kartu
  6. Gunakan library seperti pino atau winston di Node.js untuk production