managarten/docs/OBSERVABILITY_GAPS.md
Till JS 22a73943e1 chore: complete ManaCore → Mana rename (docs, go modules, plists, images)
Final cleanup of references missed in previous rename commits:

- Dockerfiles: PUBLIC_MANA_CORE_AUTH_URL → PUBLIC_MANA_AUTH_URL
- Go modules: github.com/manacore/* → github.com/mana/* (7 go.mod files)
- launchd plists: com.manacore.* → com.mana.* (14 files renamed + content)
- Image assets: *_Manacore_AI_Credits* → *_Mana_AI_Credits* (11 files)
- .env.example files: ManaCore brand strings → Mana
- .prettierignore: stale apps/manacore/* paths → apps/mana/*
- Markdown docs (CLAUDE.md, /docs/*): mana-core-auth → mana-auth, etc.

Excluded from rename: .claude/, devlog/, manascore/ (historical content),
client testimonials, blueprints, npm package refs (@mana-core/*).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 12:26:10 +02:00

19 KiB

Observability Gaps: Was unserem Monitoring-Stack noch fehlt

Stand: März 2026 Bezug: Bestehendes Setup dokumentiert in MONITORING.md und ERROR_TRACKING.md

Übersicht: Was wir haben vs. was fehlt

Bereich Haben wir? Tool Status
Metriken (Infra + App) Ja VictoriaMetrics + Grafana Produktionsreif
Alerting Ja Alertmanager + Telegram/ntfy 70+ Regeln
Error Tracking (Backend) Ja GlitchTip 18 Backends angebunden
Web Analytics Ja Umami 15 Apps getracked
Distributed Tracing Nein Kein Tracing
Log-Aggregation Nein Nur docker logs
APM (Application Performance Monitoring) Nein Kein Runtime-Profiling
Frontend Error Monitoring Nein Nur Umami (kein Error Tracking)

1. Distributed Tracing

Was ist das?

Distributed Tracing verfolgt eine einzelne Benutzeranfrage über alle beteiligten Services hinweg — vom Frontend über den Backend-Service bis zur Datenbank und zurück. Jeder Schritt wird als Span erfasst, und alle Spans einer Anfrage bilden zusammen einen Trace.

Beispiel: Was passiert ohne Tracing

Ein User öffnet den Chat und die Seite lädt langsam. Im Grafana siehst du, dass die p95-Latenz bei 3 Sekunden liegt. Aber wo steckt das Problem?

  • Liegt es am Chat-Backend?
  • An der Authentifizierung bei mana-auth?
  • An einer langsamen PostgreSQL-Query?
  • Am Redis-Cache, der nicht greift?
  • An der Netzwerk-Latenz zwischen Containern?

Ohne Tracing ist das Debugging ein Ratespiel. Du musst docker logs von jedem Service einzeln durchsuchen und Timestamps manuell korrelieren.

Was Tracing liefern würde

Ein Trace für dieselbe Anfrage zeigt dir eine Wasserfall-Ansicht:

[Chat-Web → Chat-Backend]  ─── 3.2s total ───────────────────────────────
  ├─ [Auth Validation]      ── 45ms ──  POST mana-auth/validate
  ├─ [Redis Cache Lookup]   ── 2ms ──   GET conversation:123
  ├─ [PostgreSQL Query]     ── 2.8s ──  SELECT * FROM messages WHERE...  ← BOTTLENECK
  │    └─ [Index Scan]      ── 2.7s ──  seq scan on messages (missing index!)
  └─ [Response Serialize]   ── 12ms ──  JSON encoding

Sofort sichtbar: Die PostgreSQL-Query braucht 2.8s wegen eines fehlenden Index.

Relevanz für unser Setup

Unser Stack hat viele Service-zu-Service-Aufrufe:

Browser → SvelteKit Web → NestJS Backend → mana-auth (Auth-Validierung)
                                         → PostgreSQL (Daten)
                                         → Redis (Cache/Sessions)
                                         → mana-search (Suche)
                                         → mana-llm (KI-Anfragen)
                                         → MinIO (Datei-Uploads)

Bei 20+ Containern und mehreren Backends, die sich gegenseitig aufrufen, ist es ohne Tracing nahezu unmöglich, Performance-Probleme zu isolieren.

Tools die in Frage kommen

Tool Typ Passt zu uns? Aufwand
Grafana Tempo Self-hosted, Open Source Sehr gut — integriert sich direkt in bestehendes Grafana Mittel
Jaeger Self-hosted, Open Source Gut — eigenständige UI, bewährt Mittel
OpenTelemetry (OTel) SDK/Collector (kein Backend) Pflicht — universeller Standard für Instrumentation Mittel
SigNoz Self-hosted, Open Source All-in-One (Traces + Metriken + Logs) Hoch
Datadog/New Relic SaaS Overkill und teuer für unsere Größe Gering (aber $)

Empfehlung: OpenTelemetry SDK in allen NestJS-Backends + Grafana Tempo als Backend. Tempo ist speziell für Grafana gebaut und braucht keinen zusätzlichen Storage — es nutzt Object Storage (MinIO haben wir bereits).

Was die Integration bedeutet

NestJS-Backends (Instrumentation):

// tracing.ts — wird vor dem App-Start geladen
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

const sdk = new NodeSDK({
  serviceName: 'chat-backend',
  traceExporter: new OTLPTraceExporter({
    url: 'http://tempo:4318/v1/traces',
  }),
  instrumentations: [
    getNodeAutoInstrumentations({
      // Automatisch: HTTP, Express, PostgreSQL, Redis, fetch
    }),
  ],
});
sdk.start();

Die Auto-Instrumentation fängt automatisch alle HTTP-Requests, DB-Queries und Redis-Aufrufe ab — ohne dass man jeden Endpunkt manuell annotieren muss.

Docker-Compose Erweiterung:

mana-mon-tempo:
  image: grafana/tempo:latest
  container_name: mana-mon-tempo
  volumes:
    - /Volumes/ManaData/tempo:/var/tempo
  ports:
    - "4318:4318"   # OTLP HTTP
    - "3200:3200"   # Tempo Query API

Aufwand-Schätzung:

  • Tempo aufsetzen: ~1h
  • OTel SDK als Shared Package: ~2h
  • In alle Backends integrieren: ~30min pro Backend (größtenteils Copy-Paste)
  • Grafana Datasource + Dashboard: ~1h

2. Log-Aggregation

Was ist das?

Log-Aggregation sammelt die Logs aller Container an einem zentralen Ort, macht sie durchsuchbar, filterbar und korrelierbar. Statt docker logs mana-chat-backend | grep error auf dem Server zu tippen, hast du ein Web-UI, in dem du über alle Services gleichzeitig suchen kannst.

Aktueller Zustand

Unsere Logs sind aktuell:

  • Nur per SSH erreichbar — du musst dich auf den Mac Mini verbinden
  • Pro Container isoliertdocker logs <container-name>
  • Begrenzt — max 3 Dateien à 50MB pro Container (150MB), danach weg
  • Nicht durchsuchbar — kein Volltext-Index, kein Filtern nach Severity/Service
  • Nicht korrelierbar — wenn Auth und Chat-Backend gleichzeitig Fehler loggen, musst du manuell Timestamps vergleichen

Konkrete Probleme die das verursacht

Szenario 1: Fehlersuche über Service-Grenzen

Ein User meldet: "Ich kann keine Nachrichten senden." Du weißt nicht, ob das Problem im Chat-Backend, bei der Auth-Validierung oder in der Datenbank liegt. Aktuell:

ssh mana-server
docker logs mana-chat-backend --since 10m | grep -i error
docker logs mana-auth | grep -i error
docker logs mana-infra-postgres 2>&1 | grep -i error
# ... manuell Timestamps vergleichen

Mit Log-Aggregation:

# In Grafana/Loki UI:
{service=~"chat-backend|mana-auth|postgres"} |= "error" | last 10m

Ein Query, alle Services, sofort korreliert.

Szenario 2: Logs gehen verloren

Container-Restart → Docker rotiert Logs → ältere Logs verschwinden. Wenn ein Problem erst Stunden später gemeldet wird, sind die relevanten Logs möglicherweise schon weg.

Szenario 3: Post-Mortem Analyse

Nach einem Ausfall willst du nachvollziehen, was passiert ist. Ohne zentrale Logs rekonstruierst du die Timeline mühsam aus fragmentierten Container-Logs.

Tools die in Frage kommen

Tool Typ Passt zu uns? Ressourcenverbrauch
Grafana Loki Self-hosted, Open Source Ideal — Grafana-nativ, label-basiert, leichtgewichtig Gering (~256MB RAM)
Promtail Log-Shipper für Loki Gehört zu Loki Gering (~50MB RAM)
ELK Stack (Elasticsearch + Logstash + Kibana) Self-hosted Überdimensioniert — Elasticsearch braucht 2-4GB RAM minimum Hoch
Vector/Fluentd Log-Shipper Alternative zu Promtail, flexibler aber komplexer Mittel

Empfehlung: Loki + Promtail. Loki ist speziell als "Prometheus, aber für Logs" gebaut. Es indiziert nur Labels (Service-Name, Level), nicht den vollen Log-Text — dadurch extrem ressourcenschonend. Perfekt für einen Mac Mini mit begrenztem RAM.

Was die Integration bedeutet

Docker-Compose Erweiterung:

mana-mon-loki:
  image: grafana/loki:3.0.0
  container_name: mana-mon-loki
  volumes:
    - /Volumes/ManaData/loki:/loki
  ports:
    - "3100:3100"
  command: -config.file=/etc/loki/local-config.yaml

mana-mon-promtail:
  image: grafana/promtail:3.0.0
  container_name: mana-mon-promtail
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock:ro
    - /var/lib/docker/containers:/var/lib/docker/containers:ro
  command: -config.file=/etc/promtail/config.yml

Promtail liest automatisch alle Docker-Container-Logs und schickt sie an Loki. Kein Code-Change in den Apps nötig.

Structured Logging (optional, aber empfohlen):

Damit Loki die Logs besser parsen kann, sollten Backends JSON statt Plain-Text loggen:

// NestJS: JSON-Logger statt Default
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';

WinstonModule.forRoot({
  transports: [
    new winston.transports.Console({
      format: winston.format.json(),
    }),
  ],
});

Ergebnis: {"level":"error","message":"DB connection failed","service":"chat-backend","timestamp":"2026-03-22T10:00:00Z"} statt [Nest] ERROR DB connection failed.

Aufwand-Schätzung:

  • Loki + Promtail aufsetzen: ~1-2h
  • Grafana Datasource konfigurieren: ~15min
  • Log-Dashboard bauen: ~1h
  • (Optional) Structured Logging in Backends: ~30min pro Backend

3. APM (Application Performance Monitoring)

Was ist das?

APM geht über Metriken und Tracing hinaus: Es liefert Runtime-Profiling deiner Anwendungen. Während Metriken dir sagen "die CPU ist bei 80%" und Tracing dir zeigt "diese Query dauert 3s", sagt dir APM warum auf Code-Ebene:

  • Welche Funktionen verbrauchen die meiste CPU-Zeit?
  • Wo entstehen Memory Leaks?
  • Wie verhält sich der Garbage Collector?
  • Welche Datenbankverbindungen sind idle vs. aktiv?
  • Wie groß ist der Event-Loop-Lag in Node.js?

Was wir aktuell haben (und was fehlt)

Haben wir:

  • Request-Raten, Latenz-Percentile (p50/p95/p99) via Prometheus-Metriken
  • Container-Ressourcen (CPU/RAM) via cAdvisor
  • PostgreSQL-Connection-Counts via postgres-exporter
  • Alert bei SlowResponseTime (p95 > 2s)

Fehlt uns:

  • Code-Level Profiling — Welche Funktion in welchem Service ist der Bottleneck?
  • Node.js Event Loop Monitoring — Liegt die Latenz an blockierendem Code?
  • Memory Leak Detection — Wächst der Heap eines Backends über Zeit?
  • Dependency Maps — Automatische Visualisierung, welche Services voneinander abhängen
  • Transaction-Level Analyse — Nicht nur "p95 ist 2s", sondern "der /api/messages Endpunkt hat p95 von 4s wegen N+1 Query"

Konkretes Beispiel

Das Chat-Backend wird langsamer über die Zeit. In Grafana siehst du:

  • CPU: 40% (nicht auffällig)
  • RAM: 650MB → 800MB → 950MB über 3 Tage (steigend!)
  • p95 Latenz: 500ms → 1.2s → 2.5s

Ohne APM: Du vermutest ein Memory Leak, aber wo? Du müsstest manuell Heap-Snapshots machen, den Code durchlesen, und raten.

Mit APM: Du siehst direkt:

  • Heap wächst wegen nicht-freigegebener Event-Listener in der WebSocket-Implementierung
  • Die Funktion ChatService.getConversationHistory() allociert bei jedem Call ein neues Array statt den Cache zu nutzen
  • Der GC läuft alle 30s und pausiert den Event Loop für 200ms

Tools die in Frage kommen

Tool Typ Passt zu uns? Aufwand
Grafana Pyroscope Self-hosted, Continuous Profiling Gut — integriert sich in Grafana, leichtgewichtig Mittel
Clinic.js Open Source, Node.js-spezifisch Gut für einmalige Diagnose, nicht für Dauerbetrieb Gering
OpenTelemetry Metrics SDK (haben wir noch nicht) Liefert Event-Loop-Lag, GC-Metriken etc. Mittel
Datadog APM SaaS Sehr gut, aber $31/Host/Monat Gering (aber $)
Elastic APM Self-hosted Braucht Elasticsearch (zu viel RAM) Hoch

Empfehlung: Zweistufig vorgehen:

  1. Sofort (Low-Effort): OpenTelemetry Runtime-Metriken in NestJS-Backends aktivieren. Das liefert Event-Loop-Lag, GC-Pause-Dauer, Heap-Size etc. als Prometheus-Metriken → direkt in VictoriaMetrics + Grafana sichtbar.

  2. Später (wenn nötig): Grafana Pyroscope für Continuous Profiling. Ermöglicht Code-Level Flame Graphs direkt in Grafana — du kannst live sehen, welche Funktionen CPU/Memory fressen.

Was die Integration bedeutet (Stufe 1: OTel Runtime Metriken)

// In jedem NestJS-Backend: runtime-metrics.ts
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
import { MeterProvider } from '@opentelemetry/sdk-metrics';
import { RuntimeNodeInstrumentation } from '@opentelemetry/instrumentation-runtime-node';

const exporter = new PrometheusExporter({ port: 9464 });
const meterProvider = new MeterProvider({ readers: [exporter] });

new RuntimeNodeInstrumentation({ meterProvider });

Das liefert automatisch:

  • nodejs.eventloop.delay — Event-Loop-Lag
  • nodejs.gc.duration — GC-Pausenzeiten
  • nodejs.heap.size — Heap-Größe über Zeit
  • process.cpu.utilization — CPU pro Prozess

Diese Metriken werden von VictoriaMetrics gescraped (ein neuer Scrape-Job pro Backend) und können in Grafana visualisiert werden.

Aufwand-Schätzung:

  • Stufe 1 (OTel Runtime Metriken): ~2-3h für alle Backends
  • Stufe 2 (Pyroscope): ~2h Setup + 1h Grafana-Integration

4. Frontend Error Monitoring

Was ist das?

Frontend Error Monitoring fängt Fehler ab, die im Browser des Users passieren — JavaScript-Exceptions, unhandled Promise Rejections, Netzwerk-Fehler, und Performance-Probleme. Diese Fehler sieht dein Backend nie, weil sie client-seitig auftreten.

Aktueller Zustand

Umami trackt nur:

  • Seitenaufrufe (welche Seiten werden besucht)
  • Referrer (woher kommen Besucher)
  • Browser/OS/Gerät
  • Länder/Sprachen

Umami trackt NICHT:

  • JavaScript-Fehler
  • Netzwerk-Fehler (fehlgeschlagene API-Calls)
  • Performance-Metriken (Largest Contentful Paint, Time to Interactive)
  • User-Journeys die abbrechen (z.B. Registrierung startet aber wird nicht abgeschlossen)
  • Crashes / White Screens

GlitchTip ist nur in den Backends integriert — kein einziges Frontend hat einen Error-Tracking-Client.

Warum das relevant ist

Szenario 1: Stille Fehler

Ein User auf Safari iOS hat einen Fehler in der Todo-App — eine Svelte-Komponente wirft eine Exception und der Screen wird weiß. Der User schließt die App, kommt nicht wieder. Du erfährst davon nie, weil:

  • Kein Error wird an ein Backend gesendet
  • Umami zeigt nur "1 Seitenaufruf weniger als gestern"
  • GlitchTip hat nur Backend-Fehler

Szenario 2: API-Fehler aus Sicht des Users

Das Chat-Backend gibt sporadisch 500er zurück. Im GlitchTip siehst du den Backend-Error. Aber was passiert auf der Client-Seite? Sieht der User eine Fehlermeldung? Versucht die App automatisch ein Retry? Oder hängt die UI einfach? Ohne Frontend-Monitoring weißt du das nicht.

Szenario 3: Performance-Probleme

Die Calendar-App lädt in deinem Netzwerk in 1.5s. Aber für einen User mit langsamer Verbindung in 8s. Ohne Real User Monitoring (RUM) mit Web Vitals siehst du nur deine eigene Erfahrung.

Tools die in Frage kommen

Tool Typ Passt zu uns? Kosten
GlitchTip (JS SDK) Self-hosted (haben wir schon!) Ideal — Backend läuft bereits, nur Frontend-SDK fehlt Kostenlos
Sentry SaaS oder Self-hosted Marktführer, aber teuer in SaaS Free Tier: 5K Events/Mo
Highlight.io Self-hosted, Open Source All-in-One (Errors + Sessions + Logs) Kostenlos
TrackJS SaaS Nur JS-Errors, kein Performance $

Empfehlung: GlitchTip JS SDK in allen SvelteKit-Frontends. GlitchTip ist Sentry-kompatibel — der @sentry/svelte oder @sentry/browser SDK funktioniert direkt mit unserem bestehenden GlitchTip-Server. Kein neuer Service nötig.

Was die Integration bedeutet

SvelteKit Frontend (hooks.client.ts):

// src/hooks.client.ts
import * as Sentry from '@sentry/svelte';

Sentry.init({
  dsn: 'https://key@glitchtip.mana.how/PROJECT_ID',
  environment: import.meta.env.MODE,

  // Performance Monitoring
  tracesSampleRate: 0.1, // 10% der Requests tracen

  // Session Replay (optional)
  replaysSessionSampleRate: 0,
  replaysOnErrorSampleRate: 0.5, // 50% der Error-Sessions aufzeichnen
});

Svelte Error Boundary:

<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import * as Sentry from '@sentry/svelte';

  // Svelte 5: Fängt unhandled Errors in Child-Komponenten
  $effect(() => {
    window.addEventListener('unhandledrejection', (event) => {
      Sentry.captureException(event.reason);
    });
  });
</script>

Web Vitals Tracking:

// src/lib/vitals.ts
import { onCLS, onFID, onLCP, onTTFB } from 'web-vitals';
import * as Sentry from '@sentry/svelte';

export function trackWebVitals() {
  onLCP((metric) => Sentry.captureMessage(`LCP: ${metric.value}ms`, { level: 'info' }));
  onCLS((metric) => Sentry.captureMessage(`CLS: ${metric.value}`, { level: 'info' }));
  // ... etc
}

Aufwand-Schätzung:

  • @sentry/svelte in ein Frontend integrieren: ~30min
  • Auf alle SvelteKit-Apps ausrollen: ~30min pro App (größtenteils gleicher Code)
  • Web Vitals ergänzen: ~1h
  • GlitchTip-Projekte für Frontends anlegen: ~30min

Zusammenfassung: Prioritäten

Prio Maßnahme Aufwand Impact
1 Frontend Error Monitoring (GlitchTip JS SDK) ~3-4h Hoch — blinde Flecken bei Client-Fehlern schließen, nutzt bestehende Infra
2 Log-Aggregation (Loki + Promtail) ~3-4h Hoch — massiv schnelleres Debugging, kein SSH mehr nötig
3 Distributed Tracing (OTel + Tempo) ~5-6h Mittel-Hoch — unverzichtbar wenn Service-übergreifende Probleme auftreten
4 APM / Runtime Profiling (OTel Metriken → Pyroscope) ~3-5h Mittel — hilft bei Memory Leaks und Event-Loop-Problemen

Alle vier Maßnahmen nutzen das Grafana-Ökosystem, das wir bereits betreiben. Kein neues Dashboard-Tool, keine neue Infrastruktur — nur Erweiterungen des bestehenden Stacks.

Gesamtbild nach Umsetzung

                        ┌──────────────────────────┐
                        │     Grafana (haben wir)   │
                        │  Dashboards für alles      │
                        └─────┬───┬───┬───┬────────┘
                              │   │   │   │
              ┌───────────────┘   │   │   └───────────────┐
              ▼                   ▼   ▼                   ▼
     VictoriaMetrics          Tempo  Loki            Pyroscope
     (Metriken ✅)        (Traces)  (Logs)          (Profiling)
              ▲                   ▲   ▲                   ▲
              │                   │   │                   │
     ┌────────┴──────┐    OTel SDK  Promtail      OTel/Pyroscope
     │  Exporters    │    (in Apps)  (Docker)        Agent
     │  (haben wir)  │
     └───────────────┘

     GlitchTip (Backend-Errors ✅ + Frontend-Errors NEU)
     Umami (Web Analytics ✅)
     Alertmanager (Alerts ✅)