Monitoring¶
Der Shop verwendet OpenTelemetry für Tracing und Metriken, Loki für Log-Aggregation und Grafana für Dashboards.
OpenTelemetry¶
Die monitoring-App (src/snake_shop/monitoring/) konfiguriert:
Instrumentierung¶
Beim App-Start werden automatisch instrumentiert:
| Komponente | Instrumentiert durch |
|---|---|
| Django-Requests | DjangoInstrumentor |
| PostgreSQL-Queries | Psycopg2Instrumentor |
| Redis-Zugriffe | RedisInstrumentor |
| HTTP-Clients | RequestsInstrumentor |
| Celery-Tasks | CeleryInstrumentor |
| Template-Rendering | Django-Middleware |
Konfiguration¶
# monitoring/apps.py
from opentelemetry.instrumentation.django import DjangoInstrumentor
from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor
from opentelemetry.instrumentation.redis import RedisInstrumentor
DjangoInstrumentor().instrument()
Psycopg2Instrumentor().instrument()
RedisInstrumentor().instrument()
Exporter¶
- OTLP-Collector: Traces und Metriken an zentralen Collector
- Umgebungsvariablen:
OTEL_EXPORTER_OTLP_ENDPOINT– Collector-AdresseOTEL_SERVICE_NAME– Service-Name („shop“)OTEL_RESOURCE_ATTRIBUTES– Deployment-Umgebung
Logs¶
Strukturiertes Logging¶
# Verwendet django-structlog
import structlog
logger = structlog.get_logger(__name__)
logger.info("order_placed", order_id=42, user_id=7)
Log-Ausgabe¶
- Container schreibt strukturierte JSON-Logs nach stdout
- Docker/Containerd sammeln und leiten an Loki weiter
- Log-Level:
INFO(Produktion),DEBUG(Entwicklung)
Log-Streams¶
| Stream | Inhalt |
|---|---|
| django | Request/Response, Middleware |
| django.db | SQL-Queries (nur DEBUG) |
| celery | Task-Ausführung, Retries |
| gunicorn | HTTP-Server |
| opentelemetry | Export-Status |
Fehlerbenachrichtigungen ohne E-Mail-Rückkopplung¶
Django-Requestfehler und fehlgeschlagene Celery-Tasks werden über Console,
OpenTelemetry und die zentralen Grafana-/Loki-Ansichten beobachtet. Sie lösen
keine automatische mail_admins()-Benachrichtigung aus.
Insbesondere darf kein globaler Celery-task_failure-Handler eine Admin-Mail
versenden: Der E-Mail-Versand läuft selbst über Celery. Schlägt dieser Task fehl,
würde eine erneute Admin-Mail wieder einen E-Mail-Task erzeugen und damit eine
Rückkopplungsschleife aufbauen. Fachliche Benachrichtigungen bleiben davon
unberührt.
Metriken¶
Request-Metriken¶
| Metrik | Typ | Beschreibung |
|---|---|---|
http.server.duration |
Histogram | Request-Dauer |
http.server.request_count |
Counter | Anzahl Requests |
http.server.error_count |
Counter | Fehler (5xx) |
Application-Metriken¶
| Metrik | Beschreibung |
|---|---|
orders.created |
Neue Bestellungen |
orders.value |
Bestellwert |
checkout.duration |
Checkout-Durchlaufzeit |
email.sent |
Versendete E-Mails |
celery.tasks.succeeded |
Erfolgreiche Tasks |
celery.tasks.failed |
Fehlgeschlagene Tasks |
Datenbank-Metriken¶
- Query-Dauer (p50, p95, p99)
- Query-Anzahl pro Request
- Connection-Pool-Auslastung
Health Checks¶
| Endpoint | Zweck | Erwartete Antwort |
|---|---|---|
/health/ |
Liveness | 200 OK |
/ready/ |
Readiness | 200 OK (DB/Cache erreichbar) |
/metrics/ |
Prometheus-Metriken | Text/Plain |
Kubernetes-Probes¶
livenessProbe:
httpGet:
path: /health/
port: 8000
readinessProbe:
httpGet:
path: /ready/
port: 8000
Grafana-Dashboards¶
Vorkonfigurierte Dashboards:
| Dashboard | Inhalt |
|---|---|
| Shop-Übersicht | Requests, Fehler, Latenz |
| Bestellungen | Anzahl, Wert, Status-Verteilung |
| Celery | Task-Durchsatz, Fehler, Queue-Länge |
| Datenbank | Queries, Connections, Slow-Queries |
Alerting¶
Eingerichtete Alerts:
| Alert | Bedingung | Schweregrad |
|---|---|---|
| Fehlerrate > 5 % | 5xx > 5 % in 5 Min. | Warning |
| Fehlerrate > 10 % | 5xx > 10 % in 5 Min. | Critical |
| Latenz p99 > 5s | p99 > 5s in 5 Min. | Warning |
| Health-Check down | /health/ ≠ 200 |
Critical |
| DB-Connections > 80 % | Pool-Auslastung | Warning |
| Celery-Queue > 1000 | Pending Tasks | Warning |
Fehler-Tracking (Sentry)¶
Optionale Sentry-Integration für Exception-Tracking:
# Aktiviert über Umgebungsvariable
SENTRY_DSN = os.environ.get("SENTRY_DSN")
if SENTRY_DSN:
import sentry_sdk
sentry_sdk.init(dsn=SENTRY_DSN)