Einführung
„Einfach mehr Logging hinzufügen” ist die Standardantwort, wenn Produktionsprobleme schwer zu debuggen sind. Aber mehr Logs bedeuten oft mehr Rauschen, nicht mehr Erkenntnis.
Echte Observability erfordert einen anderen Ansatz: das System durch Metriken, Traces und strukturierte Logs zu verstehen, die zusammenwirken.
Die drei Säulen
Logs
Was zu einem bestimmten Zeitpunkt passiert ist.
{
"timestamp": "2024-01-15T10:23:45Z",
"level": "error",
"message": "Payment failed",
"userId": "user_123",
"paymentId": "pay_456",
"error": "Card declined",
"traceId": "abc123"
}
Gut geeignet für: Detailliertes Debugging, Audit-Trails, das Verständnis konkreter Ereignisse.
Schlecht geeignet für: Aggregation, Trends, das Verständnis systemweiten Verhaltens.
Metriken
Numerische Messungen über die Zeit.
http_requests_total{method="POST", path="/api/payments", status="500"} 42
http_request_duration_seconds{quantile="0.99"} 2.5
active_database_connections 45
Gut geeignet für: Alerting, Dashboards, das Verständnis von Trends und Mustern.
Schlecht geeignet für: Das Verständnis, warum etwas passiert ist, das Debuggen konkreter Anfragen.
Traces
Der Weg einer Anfrage durch Ihr System.
Trace: abc123
├── API Gateway (2ms)
├── Auth Service (15ms)
├── Payment Service (450ms)
│ ├── Validate Request (5ms)
│ ├── Check Fraud (200ms)
│ └── Process Payment (240ms)
│ └── External Provider (235ms) ← langsam!
└── Notification Service (25ms)
Gut geeignet für: Das Verständnis von Latenz, das Finden von Engpässen, das Debuggen verteilter Systeme.
Schlecht geeignet für: Aggregation (zu viele Daten), einfache Systeme.
Effektives Logging implementieren
Strukturieren Sie Ihre Logs
Unstrukturierte Logs sind im großen Maßstab nahezu nutzlos:
# Schlecht
console.log(`User ${userId} failed to pay: ${error}`);
# Gut
logger.error('Payment failed', {
userId,
paymentId,
amount,
currency,
errorCode: error.code,
errorMessage: error.message,
traceId: context.traceId
});
Log-Level sind wichtig
Verwenden Sie Level konsistent:
- ERROR: Etwas ist fehlgeschlagen, das nicht hätte fehlschlagen sollen
- WARN: Etwas Unerwartetes, aber Behandeltes
- INFO: Wichtige Geschäftsereignisse
- DEBUG: Detaillierte Informationen zum Debuggen (in Produktion deaktiviert)
Kontext einbeziehen
Jedes Log sollte beantworten: wer, was, wann, wo, warum?
function processOrder(order: Order, context: Context) {
const logContext = {
orderId: order.id,
userId: order.userId,
traceId: context.traceId,
spanId: context.spanId
};
logger.info('Processing order', { ...logContext, amount: order.total });
try {
// ... Bestellung verarbeiten
logger.info('Order processed successfully', logContext);
} catch (error) {
logger.error('Order processing failed', {
...logContext,
error: error.message,
stack: error.stack
});
throw error;
}
}
Metriken implementieren
Die vier goldenen Signale
Beginnen Sie mit diesen für jeden Service:
- Latenz: Wie lange Anfragen dauern
- Traffic: Wie viele Anfragen Sie bearbeiten
- Fehler: Wie viele Anfragen fehlschlagen
- Sättigung: Wie „voll” Ihr Service ist
// Beispiel mit Prometheus-Client
const httpRequestDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests',
labelNames: ['method', 'path', 'status'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5]
});
const httpRequestsTotal = new Counter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'path', 'status']
});
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
const labels = {
method: req.method,
path: req.route?.path || 'unknown',
status: res.statusCode
};
httpRequestDuration.observe(labels, duration);
httpRequestsTotal.inc(labels);
});
next();
});
Geschäftsmetriken
Überwachen Sie nicht nur die Infrastruktur – überwachen Sie, was für das Geschäft wichtig ist:
const ordersProcessed = new Counter({
name: 'orders_processed_total',
help: 'Total orders processed',
labelNames: ['status', 'payment_method']
});
const orderValue = new Histogram({
name: 'order_value_dollars',
help: 'Order value in dollars',
buckets: [10, 50, 100, 500, 1000, 5000]
});
Warnung zur Kardinalität
Seien Sie vorsichtig mit Label-Werten. Hohe Kardinalität bringt Metriksysteme zum Absturz:
// Schlecht - userId hat unbegrenzte Werte
httpRequests.inc({ userId: user.id });
// Gut - begrenzte Wertemenge
httpRequests.inc({ userType: user.type }); // 'free', 'premium', 'enterprise'
Verteiltes Tracing implementieren
Kontext weitergeben
Geben Sie den Trace-Kontext durch Ihren gesamten Anfragefluss weiter:
// HTTP-Client
async function callService(url: string, context: Context) {
return fetch(url, {
headers: {
'X-Trace-Id': context.traceId,
'X-Span-Id': context.spanId,
'X-Parent-Span-Id': context.parentSpanId
}
});
}
// Message Queue
async function publishMessage(queue: string, message: any, context: Context) {
await queue.publish({
...message,
_traceContext: {
traceId: context.traceId,
spanId: generateSpanId(),
parentSpanId: context.spanId
}
});
}
Schlüsseloperationen instrumentieren
Konzentrieren Sie das Tracing auf:
- Aufrufe externer Services
- Datenbankabfragen
- Cache-Operationen
- Message-Queue-Operationen
- Wichtige Geschäftslogik
async function processPayment(payment: Payment, context: Context) {
return tracer.startSpan('processPayment', { parent: context.span }, async (span) => {
span.setAttributes({
'payment.id': payment.id,
'payment.amount': payment.amount,
'payment.currency': payment.currency
});
try {
const result = await paymentProvider.charge(payment);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
span.recordException(error);
throw error;
}
});
}
Die Säulen verbinden
Die wahre Stärke entsteht durch die Verbindung von Logs, Metriken und Traces:
Trace-ID überall
Fügen Sie die Trace-ID in alle Logs und Metriken ein:
logger.info('Payment processed', {
traceId: context.traceId, // Verknüpft mit Trace
paymentId: payment.id
});
paymentDuration.observe(
{ traceId: context.traceId }, // Verknüpft mit Trace
duration
);
Exemplare
Verknüpfen Sie Metriken mit konkreten Traces:
// Wenn Sie einen Latenzanstieg sehen, klicken Sie durch, um
// die tatsächlichen Traces zu sehen, die ihn verursacht haben
httpLatency.observe(
{ method: 'POST', path: '/payments' },
duration,
{ traceId: context.traceId } // Exemplar
);
Alerting-Strategie
Bei Symptomen alarmieren, nicht bei Ursachen
# Schlecht - alarmiert bei der Ursache
- alert: HighCPU
expr: cpu_usage > 80%
# Gut - alarmiert beim Symptom
- alert: HighLatency
expr: http_request_duration_seconds{quantile="0.99"} > 2
Mehrere Signale nutzen
- alert: PaymentServiceDegraded
expr: |
(
rate(payment_errors_total[5m]) / rate(payment_requests_total[5m]) > 0.01
) and (
histogram_quantile(0.99, rate(payment_duration_seconds_bucket[5m])) > 5
)
annotations:
summary: "Payment service is degraded - high errors AND high latency"
Praktische Tipps
Einfach anfangen
Versuchen Sie nicht, alles auf einmal zu instrumentieren:
- Die vier goldenen Signale zu jedem Service hinzufügen
- Strukturiertes Logging mit Trace-IDs hinzufügen
- Tracing für externe Aufrufe hinzufügen
- Basierend auf dem tatsächlichen Debugging-Bedarf erweitern
Dashboards nützlich gestalten
Ein gutes Dashboard beantwortet: „Ist das System gerade gesund?”
Enthalten Sie:
- Anfragerate und Fehlerrate
- Latenz-Perzentile (p50, p95, p99)
- Sättigungsmetriken (Queue-Tiefe, Connection Pool)
- Wichtige Geschäftsmetriken
Ihre Observability üben
Führen Sie Game Days durch, bei denen Sie:
- Ausfälle künstlich herbeiführen
- Versuchen, nur mit Observability-Tools zu diagnostizieren
- Lücken in der Instrumentierung identifizieren
Fazit
Gute Observability bedeutet nicht, mehr Daten zu sammeln – es bedeutet, die richtigen Daten zu sammeln und sie leicht nutzbar zu machen.
Konzentrieren Sie sich auf:
- Strukturierte, kontextreiche Logs
- Die vier goldenen Signale für Metriken
- Traces für das Verständnis des Anfrageflusses
- Die Verbindung aller drei mit Trace-IDs
Wenn in der Produktion etwas schiefgeht, sollten Sie in Minuten, nicht Stunden, vom Alarm zur Grundursache gelangen können.