Logging und Monitoring mit Claude Code: Observability in der Praxis
Strukturierte Logs, OpenTelemetry, Alerts und Incident-Handoff sicher mit Claude Code umsetzen.
Zuerst den Betriebsvertrag festlegen
Wenn man Claude Code nur bittet, “Logging einzubauen”, entstehen oft ein paar neue console.log-Zeilen. Das hilft lokal, aber nicht in einem Produktionsvorfall. Eine brauchbare Logging- und Monitoring-Basis muss beantworten: welche Nutzeraktion die Anfrage ausgelöst hat, welche requestId sie begleitet, welcher Service langsam wurde, welche Abhängigkeit ausfiel und welche Daten sicher an die nächste Person im Bereitschaftsdienst übergeben werden können.
Der OpenTelemetry Observability primer beschreibt Observability als Fähigkeit, ein System von außen zu verstehen und auch neue Fragen zu beantworten. What is OpenTelemetry? stellt außerdem klar, dass OpenTelemetry eine herstellerneutrale Instrumentierungsschicht ist, nicht das Observability-Backend selbst. Dieser Artikel macht daraus Claude-Code-Prompts, kopierbare Snippets, Alert-Regeln, Dashboard-Review und Incident-Handoff.
Masa hat das an einer kleinen Checkout-API ausprobiert. Der erste Prompt lautete nur “mach die Logs detaillierter”. Claude Code schlug vor, bei Zahlungsfehlern den ganzen Request Body zu loggen. Kartendaten waren nicht enthalten, aber E-Mail, Gutschein, Adresse oder Kundennotiz hätten durchrutschen können. Danach begann der Prompt mit verbotenen Feldern, requestId, traceparent und Redaction-Tests. Die Review wurde deutlich konkreter.
| Signal | Frage | Einschränkung für Claude Code |
|---|---|---|
| Logs | Was ist passiert | JSON, feste Felder, PII maskieren |
| Metriken | Wie schlimm ist es | rate, p95, Fehlerrate |
| Traces | Wo ging Zeit verloren | traceparent weitergeben, spans benennen |
| Health checks | Ist eine Abhängigkeit nutzbar | Status und latency pro Abhängigkeit |
Sicherer Prompt und CLAUDE.md
Die Memory-Dokumentation von Claude Code erklärt, dass CLAUDE.md Projekt-, Nutzer- oder Organisationsanweisungen zu Beginn jeder Sitzung liefert. Für Observability gehören dort Log-Level, Feldnamen, verbotene Felder, Testbefehle, Dashboard-Namen und Handoff-Format hinein. Ergänzend helfen die internen CLAUDE.md Best Practices sowie die offiziellen Seiten zu Claude Code permissions und 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.
Der Prompt ist absichtlich eng. Claude Code soll kein Monitoring-Tool auswählen, keine fremden Routen ändern und keine rohen Produktionslogs als Kontext erhalten. Das Ziel ist ein überprüfbarer operativer Diff.
Strukturierte Logs und Korrelation
Das OWASP Logging Cheat Sheet behandelt Logging als Sicherheitsfunktion: Review, Tests, Zugriffsschutz, Log-Injection, voller Datenträger und Ausfall des Logging-Mechanismus müssen berücksichtigt werden. Deshalb sollte Claude Code Redaction- und Propagation-Tests ergänzen, nicht nur Textmeldungen.
Dieser JSON-Logger kommt ohne Abhängigkeiten aus. Speichern Sie ihn als structured-logger.mjs und führen Sie ihn mit Node.js 18 oder neuer aus.
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",
});
In Webanwendungen sollte die Request-ID auch als Response-Header zurückgegeben und im Async Context gehalten werden. Der W3C-Standard Trace Context beschreibt, wie traceparent erzeugt oder weitergegeben wird. Nutzen Sie x-request-id für die Anwendungskorrelation und traceparent für 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();
});
}
Die Level sollten langweilig bleiben: debug für lokale Details, info für normale wichtige Ereignisse, warn für behebbare Risiken und error für Zustände, die untersucht werden müssen.
OpenTelemetry-Grundlagen
OpenTelemetry standardisiert Signale, bevor sie an das Backend Ihres Teams gehen. Prüfen Sie für Node.js die offiziellen Guides JavaScript Node.js und 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["Nutzeraktion"] --> B["Anwendung"]
B --> C["Strukturierter Log"]
B --> D["Metrik"]
B --> E["Trace span"]
C --> F["Log-Speicher"]
D --> G["Alert-Regel"]
E --> H["Trace-Backend"]
F --> I["Incident-Handoff"]
G --> I
H --> I
Health Checks und Alert-Regeln
Ein guter Health Check ist mehr als 200 OK. Er prüft Datenbank, Cache, Queue und externe APIs getrennt, liefert latency zurück und verrät keine Secrets.
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,
};
}
Alerting sollte auf Raten und Perzentilen basieren, nicht auf einer lauten einzelnen Logzeile.
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"
Drei konkrete Use Cases
Der erste Use Case ist eine Ecommerce-Checkout-API. Speichern Sie orderId, requestId, paymentProvider und amount, aber niemals Kartendaten, E-Mail, Adresse oder Token. Alerts trennen 5xx-Rate, Zahlungsfehler und p95-Latenz des Providers.
Der zweite Use Case ist ein SaaS-Adminbereich. Login, Berechtigungsänderungen, Einladungen und Planwechsel gehören in Audit Logs. E-Mail-Inhalte und private Notizen nicht. Claude Code sollte Audit Logs und App Logs trennen, Actor ID und Target User ID unterscheiden und RBAC-Tests ergänzen.
Der dritte Use Case ist ein Medien- oder Blog-CMS. Verfolgen Sie Veröffentlichung, CTA-Klicks, Formularerfolg, Bildgenerierungsfehler und fehlende Übersetzungen. Pageviews allein verbessern keinen Umsatz. Trennen Sie cta_click und generate_lead und lesen Sie dazu den Analytics-Guide.
Bei Microservices gehört der Microservices-Guide dazu. Wenn service.name oder Umgebungslabels driften, wird OpenTelemetry schwer durchsuchbar.
Fehlerbilder und Incident-Handoff
Typische Fehler sind vorhersehbar: ganzer Request Body im Log, Freitext ohne Suchbarkeit, zu viele Korrelations-IDs, ein /healthz, das keine Abhängigkeiten prüft, und Alerts ohne Besitzer. Mit Claude Code kommt hinzu, dass rohe Produktionslogs im Prompt landen können. Maskieren Sie Logs, teilen Sie aggregierte Metriken und geben Sie nur kurze Request- oder Trace-IDs weiter.
{
"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"]
}
Für sichere Untersuchung passt der Permissions Guide als nächster interner Link.
Review, CTA und Verifikation
Dashboards werden wöchentlich geprüft: fünf häufigste Fehler, p95, Logvolumen, Fehlalarme, Redaction-Probleme und offene Incidents. Einmal im Monat sollte ein echter Vorfall rückwärts gelesen werden: Hätten Logs, Metriken und Traces einer neuen Bereitschaftsperson schneller geholfen?
Einzelne Entwickler starten mit dem kostenlosen Cheatsheet. Wiederverwendbare Prompts und Setup-Material gibt es auf der Products page. Für Team-Rollout mit Logging-Standard, CLAUDE.md, Permissions, CI und Incident-Prozess ist Claude Code training and consultation der praktische nächste Schritt.
Beim Testen dieses Ablaufs brachte es am meisten, verbotene Felder vor dem Logger zu definieren. structured-logger.mjs maskierte den Token als [REDACTED] und normalisierte Zeilenumbrüche. Eine simulierte Cache-Störung ergab degraded, und ein Handoff mit requestId und traceId verkürzte die Review-Diskussion deutlich.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude-Code-Permission-Receipt: Scope, Beweis und Rollback festhalten
Permission-Receipt für Claude Code: erlaubte Aktionen, Freigabegrenzen, Prüfbefehle, Rollback und Umsatz-CTA-Prüfung.
Sicheres Agent Harness fur Claude Code und Codex: Rechte, Prufung und Rollback
Ein praktisches Agent Harness fur Claude Code und Codex mit Policy, Plan, Verifikation und Recovery.
Claude Code Subagents: Praxisleitfaden für sichere Agent-Delegation
Claude Code Subagents praktisch nutzen: Artikel- und Codearbeit sicher aufteilen, Prompts einsetzen, Fehler vermeiden.