Logging & Observability mit Claude Code: Pino, Winston, OpenTelemetry 2026

Wenn ein Production-Bug um 3 Uhr nachts auftritt, entscheiden deine Logs ob du ihn in 5 Minuten oder 5 Stunden findest. Claude Code baut strukturierte Logging-Pipelines, konfiguriert OpenTelemetry und erklärt welches Log-Level wann passt.

Log-Levels: Was wann loggen?

Level Wann Beispiel Alert?
ERROR Fehler der User-Experience beeinflusst DB-Verbindung fehlgeschlagen, API 500 Ja — sofort
WARN Unerwartetes aber verkraftbares Ereignis Rate Limit nähert sich, Retry nötig Bei Häufung
INFO Normaler Business-Flow User eingeloggt, Order erstellt Nein
DEBUG Entwicklung + gezieltes Debugging SQL-Query, Request-Details Nein — Production aus!
Claude Code Prompt: "Review diese Datei auf falsche Log-Levels. Prüfe: werden sensible Daten geloggt? Sind DEBUG-Logs in Production aktiv? Sind alle ERROR-Logs structured mit request_id und user_id?"

Pino: Schnelles strukturiertes Logging

PinoProduction-Setup mit Request-Context

# Prompt: "Erstelle Pino-Setup mit Request-ID-Tracking und Production/Development-Config" # logger.js const pino = require('pino'); const logger = pino({ level: process.env.LOG_LEVEL || 'info', // Development: lesbare Ausgabe transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty', options: { colorize: true } } : undefined, // Production: JSON (Loki/CloudWatch verarbeitet JSON) // Sensible Felder maskieren redact: { paths: ['password', 'token', '*.creditCard', '*.ssn'], censor: '[REDACTED]' }, // Basis-Felder für jedes Log base: { service: process.env.SERVICE_NAME || 'api', env: process.env.NODE_ENV } }); module.exports = logger; # Express Middleware: Request-ID + Timing const { randomUUID } = require('crypto'); app.use((req, res, next) => { const requestId = req.headers['x-request-id'] || randomUUID(); const start = Date.now(); // Child-Logger mit Request-Context req.log = logger.child({ requestId, method: req.method, url: req.url, userId: req.user?.id }); res.on('finish', () => { req.log.info({ status: res.statusCode, duration: Date.now() - start }, 'request completed'); }); next(); });

Structured Logging: Was ins JSON gehört

StructuredKonsistente Log-Felder

# FALSCH — schwer zu filtern: logger.error(`User ${userId} kann Bestellung ${orderId} nicht stornieren: ${error.message}`); # RICHTIG — strukturiert, filterbar: logger.error({ event: 'order.cancel.failed', userId, orderId, errorCode: error.code, errorMessage: error.message, stack: error.stack }, 'Order cancellation failed'); # Warum: In Grafana/Kibana/Loki kannst du filtern: # {event="order.cancel.failed", userId="123"} # Freitext-Suche in Strings ist langsam und unzuverlässig

OpenTelemetry: Distributed Tracing

OpenTelemetryTraces durch Microservices verfolgen

# Prompt: "Richte OpenTelemetry für Node.js ein — Traces zu Grafana Tempo" # instrumentation.js (vor allem anderen laden!) const { NodeSDK } = require('@opentelemetry/sdk-node'); const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express'); const { PgInstrumentation } = require('@opentelemetry/instrumentation-pg'); const sdk = new NodeSDK({ serviceName: 'order-service', traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT // Grafana Tempo / Jaeger }), instrumentations: [ new HttpInstrumentation(), // Alle HTTP-Requests new ExpressInstrumentation(), // Express Routen new PgInstrumentation() // PostgreSQL Queries ] }); sdk.start(); // node --require ./instrumentation.js server.js
# Manueller Span für Business-Logik: const { trace } = require('@opentelemetry/api'); const tracer = trace.getTracer('order-service'); async function processOrder(orderId) { return tracer.startActiveSpan('process-order', async (span) => { span.setAttribute('order.id', orderId); try { const result = await doProcessing(orderId); span.setAttribute('order.status', 'success'); return result; } catch (err) { span.recordException(err); span.setStatus({ code: SpanStatusCode.ERROR }); throw err; } finally { span.end(); } }); }

Metrics mit Prometheus

MetricsCustom Business Metrics

# prom-client für Node.js Metrics const { Counter, Histogram, register } = require('prom-client'); const httpRequests = new Counter({ name: 'http_requests_total', help: 'Total HTTP requests', labelNames: ['method', 'route', 'status'] }); const responseTime = new Histogram({ name: 'http_response_duration_seconds', help: 'HTTP response times', buckets: [0.05, 0.1, 0.3, 0.5, 1, 2, 5] }); // Business Metric: Orders pro Minute const ordersProcessed = new Counter({ name: 'orders_processed_total', help: 'Total orders processed', labelNames: ['status', 'payment_method'] }); // Metrics-Endpoint für Prometheus Scraping: app.get('/metrics', async (req, res) => { res.set('Content-Type', register.contentType); res.end(await register.metrics()); });

Log-Aggregation: Grafana Loki

# docker-compose für lokalen Observability-Stack # Prompt: "Erstelle docker-compose mit Grafana, Loki, Prometheus, Tempo" services: grafana: image: grafana/grafana:latest ports: ["3000:3000"] loki: # Log-Aggregation image: grafana/loki:latest ports: ["3100:3100"] prometheus: # Metrics image: prom/prometheus:latest ports: ["9090:9090"] tempo: # Distributed Tracing image: grafana/tempo:latest ports: ["4317:4317"] # OTLP gRPC # Pino → Loki: pino-loki Transport const transport = pino.transport({ target: 'pino-loki', options: { host: 'http://localhost:3100', labels: { app: 'order-service', env: 'production' } } });
DSGVO-Achtung bei Logs: Niemals E-Mail-Adressen, IPs (ohne Rechtsgrundlage), Passwörter oder Zahlungsdaten in Logs. Claude Code kann einen Audit deiner Logging-Felder machen — frage: "Prüfe alle log.* Aufrufe auf DSGVO-kritische Daten."

Observability im Kurs

Im Claude Code Mastery Kurs: vollständiger Observability-Stack mit Pino, OpenTelemetry, Grafana Loki und Prometheus — von der ersten Log-Zeile bis zum Production-Dashboard mit automatischen Alerts.

14 Tage kostenlos testen →