Observability mit kleinem Budget: Logging, Tracing und Metriken ohne Enterprise-Tooling

#Observability mit kleinem Budget
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Gründer & Lead Developer

Experte für Softwareentwicklung und Legacy-Code-Optimierung

Die Rechnung von Datadog kam an einem Mittwochmorgen, und alle im Engineering-Meeting wurden sehr still. Ein SaaS-Team, mit dem wir letztes Jahr arbeiteten, zahlte für Observability mehr als für ihre gesamte Staging-Infrastruktur, und jeder neue Dienst trieb die Zahl höher. Das Produkt lieferte gut aus, der Traffic wuchs, und das Monitoring, das sich bei der Series A wie ein Sicherheitsnetz angefühlt hatte, war zu einem wiederkehrenden Kostenpunkt geworden, der jedes Roadmap-Gespräch prägte. Die Frage auf dem Tisch war, ob dieselbe Sichtbarkeit für ein Zehntel des Preises geliefert werden könnte. Die Antwort, nach drei Monaten Migration, war ja.

Observability mit kleinem Budget ist kein Qualitätskompromiss. Sie ist eine bewusste Architekturentscheidung, die offene Standards und Open-Source-Tooling nutzt, um dieselben drei Signale, Logs, Metriken und Traces, zu bekommen, die die Enterprise-Plattformen verkaufen. OpenTelemetry für die Instrumentierung, Prometheus und Grafana für Metriken und Dashboards, Loki oder strukturierte Logs mit Monolog für Log-Aggregation und Tempo oder Jaeger für verteiltes Tracing. Jedes Stück ist produktionsreif, weit verbreitet und gut dokumentiert. Zusammen bilden sie einen Stack, der von einem Zwei-Personen-Startup bis zu einem mittelgroßen SaaS skaliert, ohne eine fünfstellige Monatsrechnung.

Dieser Beitrag behandelt die Architektur dieses Stacks, die Begründung hinter jeder Tool-Wahl und die Kompromisse, die du eingehst, wenn du die Pipeline selbst besitzt, statt sie auszulagern.

Die drei Säulen und was jede tatsächlich beantwortet

Observability wird oft auf "Logs, Metriken und Traces" reduziert, aber die Formulierung verschleiert, warum du alle drei brauchst. Jedes Signal beantwortet eine andere Art von Frage, und ein Monitoring-Stack ist nur so stark wie seine schwächste Säule.

Metriken beantworten Fragen zu Raten und Summen über die Zeit: wie viele Requests pro Sekunde, was ist die p95-Latenz des Checkout-Endpunkts, wie viele Hintergrundjobs schlagen fehl, wie voll ist die Queue. Metriken sind günstig zu speichern, weil sie aggregieren. Sie sind schnell abzufragen, weil sie vorindizierte Zeitreihen sind. Sie treiben Alerts und Dashboards an, und sie sind das Erste, worauf du schaust, wenn sich etwas langsam anfühlt.

Logs beantworten Fragen zu konkreten Ereignissen: was geschah, als Nutzer 4523 um 14:32 versuchte auszuchecken, was war der Stacktrace der Ausnahme, die in der letzten Stunde zweimal ausgelöst wurde, welche Parameter erhielt der fehlschlagende Webhook. Logs sind im großen Maßstab teuer, weil sie hochvolumig sind und Volltextindizierung benötigen, um nützlich zu sein. Die Qualität eines Logging-Systems hängt fast vollständig davon ab, ob Logs strukturiert und korreliert sind.

Traces beantworten Fragen zu Kausalität über Dienste hinweg: ein Request kam am API-Gateway an, fächerte zu drei Backend-Diensten und zwei externen Anbietern auf und brauchte 4,2 Sekunden, wo ging die Zeit hin. Traces sind das wertvollste Signal in einem verteilten System und dasjenige, das am häufigsten übersprungen wird, weil Teams unterschätzen, wie schwer es ist, sie allein aus Logs zu rekonstruieren.

Ein guter Stack liefert alle drei mit gemeinsamen Identifikatoren, sodass eine Trace-ID in einer Log-Zeile den entsprechenden Trace öffnet und ein langsamer Span in einem Trace auf die exakte Log-Ausgabe des Handlers verweist, der lief. Diese Korrelation ist es, die rohe Telemetrie in tatsächliche Observability verwandelt.

Strukturiertes Logging mit Monolog und einer gemeinsamen Correlation-ID

Für PHP/Symfony-Anwendungen ist Monolog das Logging-Fundament. Out of the box unterstützt es mehrere Handler, Log-Rotation und eine Handvoll eingebauter Prozessoren, aber die wesentliche Arbeit liegt darin, es für strukturierte Ausgabe und Trace-Korrelation zu konfigurieren.

Strukturiert bedeutet JSON. Eine Log-Zeile wie "User 4523 failed checkout" ist nur per Teilstring durchsuchbar; ein JSON-Objekt mit den Feldern user_id, event, order_id und error_code kann zur Abfragezeit gefiltert, aggregiert und korreliert werden. Der Wechsel zu JSON-Logging ist die einzelne Observability-Änderung mit dem höchsten Hebel, die die meisten Teams vornehmen können, und sie kostet nichts.

Ein minimales Monolog-Setup für einen Symfony-Dienst sieht so aus:

# config/packages/monolog.yaml
monolog:
  handlers:
    main:
      type: stream
      path: 'php://stdout'
      level: info
      formatter: monolog.formatter.json
      channels: ['!event']
  processors:
    - Monolog\Processor\UidProcessor
    - App\Logging\TraceIdProcessor

Der TraceIdProcessor ist die Stelle, an der die Korrelation geschieht. Er liest die aktuelle OpenTelemetry-Trace-ID aus dem Kontext und hängt sie an jeden Log-Datensatz an, sodass die Logs für einen einzelnen Request durch die Suche nach einer ID zurückgeholt werden können.

// src/Logging/TraceIdProcessor.php
final class TraceIdProcessor
{
    public function __invoke(array $record): array
    {
        $span = Span::getCurrent();
        $context = $span->getContext();
        if ($context->isValid()) {
            $record['extra']['trace_id'] = $context->getTraceId();
            $record['extra']['span_id'] = $context->getSpanId();
        }
        return $record;
    }
}

Logs, die als JSON nach stdout geschrieben werden, sind trivial zu sammeln. Promtail oder Vector verschiffen sie in Loki, Grafanas Log-Speicher-Engine, die einen Bruchteil von Elasticsearch oder einem SaaS-Log-Anbieter kostet, weil sie nur Labels indiziert und für den Rest auf günstigen Object Storage setzt. Für viele Teams bewältigt Loki auf einem VPS für 40 €/Monat das gesamte Log-Volumen eines wachsenden SaaS.

Metriken mit Prometheus und Grafana

Prometheus ist der De-facto-Standard für Metriken in Open-Source-Infrastruktur, und es gibt fast kein Szenario in einem mittelgroßen SaaS, in dem es die falsche Wahl ist. Es scrapt Metrik-Endpunkte nach Zeitplan, speichert sie als effiziente Zeitreihen und stellt eine Abfragesprache (PromQL) bereit, die ausdrucksstark genug für praktisch jeden Alert oder jedes Dashboard ist, das du bauen könntest.

Eine PHP-Anwendung für Prometheus zu instrumentieren erfordert einen Exporter, der den /metrics-Endpunkt im richtigen Format bereitstellt. promphp/prometheus_client_php erledigt das. Für Counter, Histogramme und Gauges verwendest du wenige Zeilen:

$registry = new CollectorRegistry(new Redis(['host' => 'redis']));

$counter = $registry->getOrRegisterCounter(
    'app',
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'route', 'status']
);
$counter->inc([$method, $route, (string) $status]);

$histogram = $registry->getOrRegisterHistogram(
    'app',
    'http_request_duration_seconds',
    'HTTP request duration',
    ['route'],
    [0.05, 0.1, 0.25, 0.5, 1, 2, 5]
);
$histogram->observe($durationSeconds, [$route]);

Die Metriken, die du standardmäßig bereitstellen solltest, sind Request-Rate und Latenz-Histogramme pro Route, Datenbank-Query-Dauer, Queue-Tiefe, Job-Dauer, Fehlerraten und eine oder zwei Geschäftsmetriken, die für dein Produkt zählen (Anmeldungen pro Minute, Bestellungen pro Minute, aktive WebSocket-Verbindungen). Grafana-Dashboards, die auf diesen fünf oder sechs Metrik-Familien aufbauen, decken 90 % der operativen Fragen ab.

Grafana selbst ist kostenlos, läuft auf jedem kleinen Server und hat ein ausgereiftes Alerting-System, das an Slack, PagerDuty, Opsgenie oder E-Mail feuert. Alert-Regeln leben als Code in Grafanas Provisioning-Dateien, sodass sie genauso wie Anwendungscode überprüft und versioniert werden. Das ist oft ein besserer Workflow als SaaS-Alerting-UIs, bei denen Alert-Definitionen von den Annahmen des Teams, das sie geschrieben hat, abdriften.

Verteiltes Tracing mit OpenTelemetry und Tempo

OpenTelemetry ist das kritische Stück des Budget-Stacks, weil es Vendor-Lock-in beseitigt. Das OpenTelemetry-SDK instrumentiert deine Anwendung mit herstellerneutraler Semantik, und der OpenTelemetry Collector routet die resultierende Telemetrie zu jedem kompatiblen Backend. Beginne mit Tempo (Grafanas Trace-Backend), und wenn du später zu einem kommerziellen Anbieter migrierst, ändert sich die Instrumentierung nicht, nur die Export-Konfiguration des Collectors.

Eine Symfony-Anwendung instrumentiert sich selbst mit open-telemetry/opentelemetry-auto-symfony und einem im Service-Container konfigurierten Tracer-Provider. Die Auto-Instrumentierung umschließt den Kernel, Doctrine-Queries, HTTP-Clients und Cache-Operationen und erzeugt für jeden eingehenden Request einen Trace-Baum. Für manuelle Spans um bestimmte Operationen macht ein dünner Helfer den Code ergonomisch:

public function processPayment(Order $order): PaymentResult
{
    return $this->tracer->inSpan('payment.process', function () use ($order) {
        $span = Span::getCurrent();
        $span->setAttribute('order.id', $order->getId());
        $span->setAttribute('order.amount_cents', $order->getAmountCents());

        $result = $this->paymentProvider->charge($order);

        $span->setAttribute('payment.provider_ref', $result->getReference());
        return $result;
    });
}

Traces fließen durch den OpenTelemetry Collector in Tempo, das sie in S3-kompatiblem Object Storage speichert. Weil Tempo den Trace-Inhalt nicht indiziert, es indiziert nur die Trace-ID und nutzt externe Systeme (Loki, Prometheus) für die Suche, ist es bei gleichem Volumen dramatisch günstiger als Jaeger mit Elasticsearch. Der typische Budget-Stack speichert Wochen von Traces für den Preis von ein oder zwei Tagen auf einer kommerziellen Plattform.

Der praktische Wert zeigt sich, wenn etwas schiefgeht: Ein langsamer Checkout-Trace zeigt genau, welcher Span den Request dominierte, die Logs für diesen Span sind über die gemeinsame Trace-ID einen Klick entfernt, und die relevanten Prometheus-Metriken sind in derselben Grafana-Instanz sichtbar. Diese Integration ist es, die Observability funktionieren lässt, und sie kommt kostenlos, wenn alle drei Signale am selben Ort leben.

Die ehrlichen Kompromisse

Ein Budget-Stack ist nicht identisch mit Datadog oder New Relic. Es gibt echte Unterschiede, die es wert sind, benannt zu werden.

Der erste ist operativer Aufwand. Prometheus, Grafana, Loki, Tempo und den OpenTelemetry Collector zu betreiben erfordert jemanden im Team, der versteht, wie man sie aktualisiert, die Aufbewahrung konfiguriert, Ressourcenlimits abstimmt und sich von Speicherdruck erholt. Teams ohne Platform Engineer unterschätzen diese Kosten oft. In der Praxis braucht ein gut konfigurierter Stack auf Docker oder Kubernetes ein paar Stunden Wartung pro Monat, viel weniger als die Enterprise-Rechnung, aber nicht null.

Der zweite sind fortgeschrittene Funktionen. Kommerzielle Plattformen enthalten Anomalie-Erkennung, automatisches Dependency-Mapping, Session Replay, Real User Monitoring und KI-gestützte Root-Cause-Analyse. Die meisten dieser Funktionen sind im großen Maßstab nützlich, aber für ein Team unter ein paar hundert Diensten unnötig. Wenn du sie später brauchst, portiert die OpenTelemetry-Instrumentierung sauber auf kommerzielle Backends.

Der dritte ist die Aufbewahrung bei sehr hohen Volumina. Bei zehn Milliarden Log-Zeilen pro Tag wird der Eigenbetrieb von Loki nicht-trivial. Für die meisten SaaS-Unternehmen ist das ein Problem für später; für sehr hochvolumige Produkte lohnt es sich, es früh zu validieren.

Für mittelgroße europäische SaaS-Unternehmen ist der Budget-Stack meist die richtige Wahl, nicht weil er günstiger ist, sondern weil er inspizierbar, portabel und auf Standards gebaut ist, die jeden einzelnen Anbieter überdauern werden. Er ist auch der Stack, der am besten zu Legacy-Code-Optimierung und breiterer individueller Softwareentwicklung passt, weil er Engineering-Teams die Sichtbarkeit gibt, die sie brauchen, um Systeme mit Zuversicht zu ändern.

Eine Referenzarchitektur, die du dieses Quartal ausliefern kannst

Ein konkretes End-to-End-Setup, das wir erfolgreich über mehrere Berliner SaaS-Produkte hinweg deployt haben, sieht so aus: Symfony- oder Next.js-Anwendungen, instrumentiert mit OpenTelemetry und Monolog; ein OpenTelemetry Collector, der als Sidecar oder als einzelne Instanz pro Umgebung läuft; Prometheus, das /metrics von jedem Dienst und von Infrastruktur-Exportern (node_exporter, mysqld_exporter, redis_exporter) scrapt; Loki, das JSON-Logs aus stdout per Promtail sammelt; Tempo, das Traces in S3 speichert; und Grafana als die einzige Glasscheibe für Dashboards, Logs und Traces, mit Alerting, das an Slack und PagerDuty geroutet wird.

Der gesamte Stack läuft für die große Mehrheit der SaaS-Produkte auf zwei oder drei bescheidenen Hetzner- oder OVH-Servern, einschließlich einiger mit Millionen Requests pro Tag. Die Monatskosten liegen typischerweise unter 200 €, und die Sichtbarkeit ist von dem, was eine kommerzielle Plattform für den operativen Alltag bietet, nicht zu unterscheiden.

Observability ist kein Problem, das man einmal löst. Sie ist eine Praxis, die mit dem Produkt wächst, und die frühen Entscheidungen summieren sich. Teams, die sich früh zu OpenTelemetry-Instrumentierung und strukturiertem Logging verpflichten, können Backends frei wechseln, während sich ihre Bedürfnisse entwickeln; Teams, die in einem proprietären Agenten gefangen sind, zahlen für diese Entscheidung bei jeder zukünftigen Migration.

Wenn dein Team eine Observability-Migration evaluiert, den ersten Instrumentierungsdurchlauf absteckt oder versucht, eine Datadog-Rechnung daran zu hindern, das Quartal zu definieren, helfen wir mittelgroßen europäischen SaaS-Unternehmen, produktionsreife Open-Source-Observability-Stacks zu entwerfen und zu deployen. Kontaktiere uns unter hello@wolf-tech.io oder besuche wolf-tech.io für eine kostenlose Beratung.