Advanced (Diperbarui: 2/6/2026)

Logging dan monitoring dengan Claude Code: panduan observability praktis

Rancang structured logs, OpenTelemetry, alert, health check, dan incident handoff dengan Claude Code.

Logging dan monitoring dengan Claude Code: panduan observability praktis

Mulai dari kontrak operasional

Jika Anda hanya meminta Claude Code “tambahkan logging”, biasanya hasilnya beberapa console.log baru. Itu membantu saat debugging lokal, tetapi tidak cukup untuk incident production. Fondasi logging dan monitoring yang benar harus menjawab aksi pengguna mana yang memicu request, requestId mana yang mengikuti flow, service mana yang melambat, dependency mana yang gagal, dan data apa yang aman diteruskan ke engineer berikutnya.

OpenTelemetry Observability primer menjelaskan observability sebagai kemampuan memahami sistem dari luar dan menjawab pertanyaan baru. What is OpenTelemetry? juga menegaskan bahwa OpenTelemetry adalah layer instrumentasi vendor-neutral, bukan backend observability itu sendiri. Artikel ini mengubah konsep tersebut menjadi prompt Claude Code, snippet siap salin, aturan alert, review dashboard, dan format incident handoff.

Masa pernah mencoba flow ini pada checkout API kecil. Prompt pertama hanya berbunyi “buat log lebih detail”. Claude Code menyarankan mencatat seluruh request body ketika pembayaran gagal. Tidak ada nomor kartu, tetapi email, kupon, alamat, dan catatan pelanggan bisa ikut bocor. Setelah prompt dimulai dari daftar field terlarang, requestId, traceparent, dan test redaction, review menjadi jauh lebih tajam.

SinyalPertanyaanBatasan untuk Claude Code
LogsApa yang terjadiJSON, field tetap, PII disamarkan
MetricsSeberapa burukrate, p95, error rate
TracesDi mana waktu habisteruskan traceparent, beri nama span
Health checksDependency bisa dipakaistatus dan latency per dependency

Prompt aman dan aturan CLAUDE.md

Dokumentasi memory Claude Code menjelaskan bahwa CLAUDE.md memberi instruksi project, user, atau organisasi di awal sesi. Untuk observability, masukkan level log, nama field, field terlarang, perintah test, dashboard, dan format handoff. Gunakan juga CLAUDE.md best practices serta dokumen resmi Claude Code permissions dan hooks.

Claude Code task:
- Add observability to the checkout API only.
- Keep all changes inside src/checkout and tests/checkout.
- Use structured JSON logs with requestId and traceparent.
- Never log passwords, tokens, cookies, email, phone, address,
  raw prompt text, or full request/response bodies.
- Add tests proving redaction and requestId propagation.
- Add a /healthz report with database and cache latency.
- Add alert rules for 5xx rate, p95 latency, and redaction failure.
- Show a diff summary and remaining manual checks at the end.

Prompt ini sengaja sempit. Claude Code tidak diminta memilih vendor monitoring baru, mengubah route yang tidak terkait, atau menerima raw production logs sebagai konteks. Targetnya adalah diff operasional yang bisa direview, diuji, dan di-rollback.

Structured logs dan request ID

OWASP Logging Cheat Sheet memperlakukan logging sebagai fitur keamanan. Logging perlu code review, test, access control, perlindungan log injection, dan perilaku yang jelas saat sistem log gagal. Karena itu, minta Claude Code menambahkan test redaction, propagation, dan failure mode, bukan hanya pesan teks.

Logger JSON berikut tidak memakai dependency. Simpan sebagai structured-logger.mjs dan jalankan dengan Node.js 18 atau lebih baru.

import { randomUUID } from "node:crypto";

const rank = { debug: 10, info: 20, warn: 30, error: 40 };
const current = process.env.LOG_LEVEL || "info";
const threshold = rank[current] ?? rank.info;

const secretKeys = [
  "password",
  "token",
  "authorization",
  "cookie",
  "set-cookie",
  "apikey",
];

function cleanText(value) {
  return String(value).replace(/[\r\n\t]/g, " ").slice(0, 500);
}

function redact(value) {
  if (Array.isArray(value)) return value.map(redact);
  if (!value || typeof value !== "object") return value;

  return Object.fromEntries(
    Object.entries(value).map(([key, item]) => {
      if (secretKeys.includes(key.toLowerCase())) {
        return [key, "[REDACTED]"];
      }
      return [key, redact(item)];
    }),
  );
}

export function log(level, message, fields = {}) {
  if ((rank[level] ?? 99) < threshold) return;

  const entry = {
    ts: new Date().toISOString(),
    level,
    service: process.env.SERVICE_NAME || "checkout-api",
    env: process.env.NODE_ENV || "development",
    requestId: fields.requestId || randomUUID(),
    msg: cleanText(message),
    ...redact(fields),
  };

  process.stdout.write(`${JSON.stringify(entry)}\n`);
}

log("info", "payment accepted", {
  requestId: "req_demo_001",
  userId: "user_123",
  amount: 4980,
  token: "sk_live_should_not_leak",
});

Pada aplikasi web, kembalikan request ID di response header dan simpan di async context. Spesifikasi W3C Trace Context menjelaskan cara membuat atau meneruskan traceparent. Pakai x-request-id untuk korelasi aplikasi dan traceparent untuk distributed tracing.

import { AsyncLocalStorage } from "node:async_hooks";
import { randomUUID } from "node:crypto";
import type { Request, Response, NextFunction } from "express";
import { log } from "./structured-logger";

type RequestContext = {
  requestId: string;
  traceparent?: string;
  userId?: string;
};

const storage = new AsyncLocalStorage<RequestContext>();

export function getRequestContext() {
  return storage.getStore();
}

export function requestContext(
  req: Request,
  res: Response,
  next: NextFunction,
) {
  const started = performance.now();
  const user = (req as Request & { user?: { id?: string } }).user;
  const requestId =
    req.get("x-request-id") ||
    req.get("cf-ray") ||
    randomUUID();

  const context = {
    requestId,
    traceparent: req.get("traceparent"),
    userId: user?.id,
  };

  res.setHeader("x-request-id", requestId);

  storage.run(context, () => {
    res.on("finish", () => {
      const durationMs = Math.round(performance.now() - started);
      const level = res.statusCode >= 500
        ? "error"
        : res.statusCode >= 400
          ? "warn"
          : "info";

      log(level, "http request completed", {
        requestId,
        method: req.method,
        path: req.path,
        statusCode: res.statusCode,
        durationMs,
      });
    });

    next();
  });
}

Level log sebaiknya sederhana: debug untuk detail lokal, info untuk event normal penting, warn untuk risiko yang masih bisa pulih, dan error untuk kondisi yang perlu investigasi manusia.

Dasar OpenTelemetry

OpenTelemetry menstandarkan sinyal aplikasi sebelum dikirim ke backend yang sudah dipakai tim. Untuk Node.js, cek panduan resmi JavaScript Node.js dan JavaScript exporters.

npm install @opentelemetry/sdk-node \
  @opentelemetry/auto-instrumentations-node \
  @opentelemetry/exporter-trace-otlp-proto \
  @opentelemetry/exporter-metrics-otlp-proto \
  @opentelemetry/sdk-metrics
const opentelemetry = require("@opentelemetry/sdk-node");
const {
  getNodeAutoInstrumentations,
} = require("@opentelemetry/auto-instrumentations-node");
const {
  OTLPTraceExporter,
} = require("@opentelemetry/exporter-trace-otlp-proto");
const {
  OTLPMetricExporter,
} = require("@opentelemetry/exporter-metrics-otlp-proto");
const {
  PeriodicExportingMetricReader,
} = require("@opentelemetry/sdk-metrics");

process.env.OTEL_SERVICE_NAME ||= "checkout-api";

const endpoint =
  process.env.OTEL_EXPORTER_OTLP_ENDPOINT ||
  "http://localhost:4318";

const sdk = new opentelemetry.NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: `${endpoint}/v1/traces`,
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({
      url: `${endpoint}/v1/metrics`,
    }),
    exportIntervalMillis: 30000,
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

process.on("SIGTERM", () => {
  sdk.shutdown().finally(() => process.exit(0));
});
flowchart LR
  A["Aksi user"] --> B["Aplikasi"]
  B --> C["Structured log"]
  B --> D["Metric"]
  B --> E["Trace span"]
  C --> F["Log store"]
  D --> G["Alert rule"]
  E --> H["Trace backend"]
  F --> I["Incident handoff"]
  G --> I
  H --> I

Health checks dan alert

Health check yang baik bukan sekadar 200 OK. Ia memeriksa database, cache, queue, dan API eksternal secara terpisah, mengembalikan latency, dan tidak pernah membocorkan secret.

function timeout(ms) {
  return new Promise((_, reject) => {
    setTimeout(() => reject(new Error("timeout")), ms);
  });
}

export async function buildHealthReport(checks) {
  const started = Date.now();
  const results = {};

  for (const [name, check] of Object.entries(checks)) {
    const before = Date.now();
    try {
      await Promise.race([check(), timeout(800)]);
      results[name] = {
        status: "ok",
        latencyMs: Date.now() - before,
      };
    } catch (error) {
      const message =
        error instanceof Error ? error.message : String(error);
      results[name] = {
        status: "fail",
        latencyMs: Date.now() - before,
        reason: message.slice(0, 120),
      };
    }
  }

  const failed = Object.values(results)
    .filter((item) => item.status === "fail")
    .length;

  return {
    status: failed ? "degraded" : "ok",
    uptimeSec: Math.round(process.uptime()),
    totalLatencyMs: Date.now() - started,
    checks: results,
  };
}

Alert sebaiknya berbasis rate dan percentile, bukan satu baris log yang bising.

groups:
  - name: checkout-api
    rules:
      - alert: CheckoutHigh5xxRate
        expr: |
          sum(rate(http_requests_total{
            service="checkout-api",
            status_code=~"5.."
          }[5m]))
          /
          sum(rate(http_requests_total{
            service="checkout-api"
          }[5m])) > 0.02
        for: 10m
        labels:
          severity: page
        annotations:
          summary: "Checkout 5xx rate is above 2%"

      - alert: CheckoutP95LatencyHigh
        expr: |
          histogram_quantile(
            0.95,
            sum by (le) (
              rate(http_request_duration_seconds_bucket{
                service="checkout-api"
              }[5m])
            )
          ) > 1.5
        for: 15m
        labels:
          severity: ticket
        annotations:
          summary: "Checkout p95 latency is above 1.5s"

Tiga use case konkret

Use case pertama adalah checkout API ecommerce. Simpan orderId, requestId, paymentProvider, dan amount, tetapi jangan pernah simpan data kartu, email, alamat, atau token. Pisahkan alert untuk 5xx rate, payment failure rate, dan p95 latency provider.

Use case kedua adalah dashboard admin SaaS. Login, perubahan permission, undangan member, dan perubahan plan masuk ke audit logs. Isi email undangan dan catatan pribadi tidak perlu masuk. Minta Claude Code memisahkan audit logs dari app logs, membedakan actor ID dan target user ID, serta menambahkan test RBAC.

Use case ketiga adalah media site atau blog CMS. Lacak publish event, CTA click, lead form success, image generation failure, dan missing translation. Page views saja tidak memperbaiki revenue. Pisahkan cta_click dan generate_lead, lalu hubungkan dengan analytics implementation guide.

Jika sistem memakai microservices, baca juga microservices guide. service.name dan environment label yang tidak konsisten membuat data OpenTelemetry sulit dicari.

Failure mode dan incident handoff

Kegagalan umum mudah ditebak: mencatat seluruh request body, memakai pesan bebas yang sulit di-query, membuat terlalu banyak correlation ID, endpoint /healthz tidak mengecek dependency, dan alert tidak punya owner. Dengan Claude Code ada risiko tambahan: raw production logs masuk ke prompt. Masking dulu logs, share metrics agregat, lalu kirim hanya request ID atau trace ID pendek.

{
  "incident_id": "INC-2026-06-02-001",
  "severity": "SEV2",
  "owner": "oncall-api",
  "customer_impact": "Checkout errors for some card payments",
  "first_seen": "2026-06-02T09:15:00+09:00",
  "request_ids": ["req_7f3a", "req_8b21"],
  "trace_ids": ["7bba9f33312b3dbb8b2c2c62bb7abe2d"],
  "dashboards": ["Checkout API overview"],
  "current_hypothesis": "Payment provider latency spike",
  "actions_taken": ["Disabled checkout_v2 feature flag"],
  "next_checks": ["Compare p95 latency by region"],
  "do_not_do": ["Do not paste raw customer data into prompts"]
}

Untuk investigasi read-only yang aman, gunakan juga permissions guide.

Review dashboard, CTA, dan verifikasi

Review dashboard setiap minggu: lima error teratas, p95 latency, pertumbuhan volume log, false alert, kegagalan redaction, dan incident terbuka. Setiap bulan, ambil satu incident nyata dan cek apakah logs, metrics, dan traces akan membantu on-call baru menemukan root cause lebih cepat.

Untuk mulai sendiri, gunakan free Claude Code cheatsheet agar daily check konsisten. Untuk prompt dan material setup yang bisa dipakai ulang, lihat products page. Jika tim perlu standar logging, CLAUDE.md, permissions, CI, dan incident workflow di repository nyata, Claude Code training and consultation adalah langkah berikutnya.

Saat flow ini dicoba, hasil terbesar datang dari menulis field terlarang sebelum menulis logger. structured-logger.mjs mengubah token menjadi [REDACTED] dan menormalisasi newline menjadi satu baris. Simulasi cache failure membuat health report menjadi degraded; handoff berisi requestId dan traceId membuat review jauh lebih singkat.

#Claude Code #logging #monitoring #OpenTelemetry #observability
Gratis

PDF gratis: cheatsheet Claude Code

Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.

Kami menjaga datamu dan tidak mengirim spam.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.