PHP Monitoring in Produktion: Was Sie bei PHP-FPM, OPcache, Laravel, Symfony und WordPress messen sollten
Welche Signale zeigen, ob eine PHP-Anwendung gesund ist, wie Sie PHP-FPM und OPcache richtig lesen, wie Sie PHP mit OpenTelemetry und Monolog instrumentieren und welche Alerts sich lohnen.
Zusammenfassung
- PHP Monitoring in Produktion stützt sich auf vier Ebenen: Latenz und Fehlerrate pro Endpunkt, Auslastung des PHP-FPM-Pools, OPcache und Laufzeitlimits sowie die Datenbank-, HTTP- und Queue-Aufrufe Ihres Codes.
- PHP-FPM stellt den Zustand jedes Pools über pm.status_path bereit; entscheidend sind aktive und freie Prozesse, max children reached und die Listen Queue.
- Die OpenTelemetry-Erweiterung für PHP liefert zusammen mit Auto-Instrumentierungspaketen für Laravel, Symfony, WordPress, PDO und HTTP-Clients Traces ohne Codeänderungen, und Monolog-JSON-Logs mit Trace-IDs verbinden diese mit Ihren Logs.
PHP Monitoring in Produktion funktioniert anders als die Überwachung einer langlebigen JVM- oder Go-Anwendung. Jede Anfrage läuft in einem kurzlebigen Worker, den PHP-FPM verwaltet, der Zustand wird bei jedem Request neu aufgebaut, und viele Performance-Probleme sind im PHP-Code gar nicht sichtbar: Sie zeigen sich als voller Worker-Pool, als kalter OPcache oder als Datenbank, die plötzlich 300 ms statt 3 ms braucht.
Dieser Leitfaden zeigt Ebene für Ebene, was Sie in einer PHP-Anwendung überwachen sollten: Latenz und Fehler pro Endpunkt, PHP-FPM-Pool-Metriken, OPcache, Laufzeitlimits, Datenbank- und externe Aufrufe sowie Queue-Worker. Dazu kommen die Besonderheiten von Laravel, Symfony und WordPress, die Instrumentierung mit OpenTelemetry und Monolog, Vorschläge für Alerts, eine Metrik-Tabelle und eine Checkliste zum Kopieren.
Am Anfang steht der Request: Latenz und Fehlerrate pro Endpunkt
Das nützlichste Signal im PHP Performance Monitoring ist zugleich das einfachste: Wie viele Anfragen kommen pro Route oder Controller-Action an, wie viele schlagen fehl und wie lange dauern sie? Das sind die klassischen RED-Metriken (Rate, Errors, Duration), und sie beschreiben genau das, was Ihre Nutzer erleben.
- Pro Endpunkt messen, nicht pro Host. Ein Durchschnitt über alle Routen verdeckt den Checkout-Endpunkt, der seit letzter Woche dreimal so langsam ist. Gruppieren Sie nach Routen-Template (
/orders/{id}) statt nach roher URL, damit die Kardinalität beherrschbar bleibt. - Perzentile statt Mittelwerte. Beobachten Sie p50, p95 und p99 der Latenz. Ein unauffälliger Mittelwert kann einen Ausreißerbereich verbergen, in dem jede hundertste Anfrage auf einen Lock wartet. Warum meist p99 die entscheidende Zahl ist, erklärt unser Lexikonartikel zu Perzentilen.
- Fehlerklassen trennen. HTTP-5xx-Antworten, nicht abgefangene Exceptions, Fatal Errors und Timeouts am Webserver sind unterschiedliche Fehlerbilder. Ein 502 oder 504 von Nginx bedeutet oft, dass PHP-FPM gar nicht geantwortet hat; im PHP-Fehlerlog taucht das nicht auf.
- Apdex als zusammenfassende Kennzahl. Wenn Stakeholder eine einzige Zahl pro Service wünschen, ist ein Apdex-Wert mit passendem Schwellenwert pro Endpunkttyp leichter zu kommunizieren als drei Perzentile.
Korrelieren Sie die Request-Metriken mit dem Webserver. Nginx-Access-Logs mit $request_time und $upstream_response_time zeigen, wie viel der Latenz auf das Warten auf PHP-FPM entfällt; die Nginx-Integration erfasst diese Werte zusammen mit den PHP-Signalen.
PHP-FPM überwachen: Pool-Metriken, die Ausfälle ankündigen
PHP-FPM betreibt einen Pool von Worker-Prozessen, und jeder Worker bearbeitet genau eine Anfrage zur selben Zeit. Sind alle Worker belegt, warten neue Anfragen in der Listen Queue; läuft diese voll, liefert der Webserver Fehler. Deshalb ist der Pool-Zustand der wichtigste Frühindikator, wenn Sie PHP-FPM überwachen.
Aktivieren Sie pro Pool die Statusseite über pm.status_path. Sie liefert unter anderem diese Felder:
- active processes / idle processes / total processes: wie viele Worker gerade beschäftigt sind. Liegt der Wert dauerhaft nahe
pm.max_children, sind Sie an der Kapazitätsgrenze. - max children reached: ein Zähler dafür, wie oft der Pool
pm.max_childrenerreicht hat und keinen weiteren Worker starten konnte. Jeder Anstieg verdient einen Blick. - listen queue / max listen queue / listen queue len: Anfragen, die auf einen freien Worker warten. In einem gesunden Pool ist die Listen Queue fast immer null.
- accepted conn und slow requests: Durchsatz sowie die Zahl der Anfragen, die
request_slowlog_timeoutüberschritten haben.
Besondere Aufmerksamkeit verdient das Slow Log. Sind request_slowlog_timeout und slowlog gesetzt, schreibt PHP-FPM für jede Anfrage, die länger als der Timeout läuft, einen Stacktrace. So erfahren Sie günstig und dauerhaft, in welcher Funktion eine langsame Anfrage hing, auch ganz ohne Tracing. Unabhängig davon beendet request_terminate_timeout Worker nach einer festen Wanduhrzeit und schützt den Pool vor Anfragen, die nie enden.
Eine minimale Pool-Konfiguration für das Monitoring sieht so aus:
; /etc/php/8.3/fpm/pool.d/www.conf (Beispiel)
pm = dynamic
pm.max_children = 40
pm.status_path = /fpm-status
ping.path = /fpm-ping
request_slowlog_timeout = 2s
slowlog = /var/log/php-fpm/www-slow.log
request_terminate_timeout = 60s
Stellen Sie den Statuspfad nur intern bereit oder schränken Sie ihn in der Webserver-Konfiguration ein, denn er verrät Details über Ihre Last. Mit ?json oder ?full erhalten Sie maschinenlesbare Ausgaben bzw. Werte pro Prozess.
OPcache, memory_limit und max_execution_time
OPcache
OPcache hält kompilierten PHP-Bytecode im Shared Memory. Funktioniert er, entfallen Parsen und Kompilieren bei jeder Anfrage. Ist er zu klein, verdrängt er still Skripte oder startet neu, und die Latenz steigt auf allen Endpunkten gleichzeitig. opcache_get_status() liefert die nötigen Werte:
- Trefferquote (
opcache_hit_rate): sollte nach dem Aufwärmen sehr nahe an 100 % liegen. Sinkt sie, ist der Cache meist voll oder wird zurückgesetzt. - Speicherbelegung: belegter, freier und verschwendeter Speicher im Verhältnis zu
opcache.memory_consumption. - Gecachte Schlüssel im Vergleich zu
max_cached_keys(gesteuert überopcache.max_accelerated_files) sowie der Zähler für Neustarts wegen Speichermangels (oom_restarts).
Da OPcache im Shared Memory des FPM-Masters liegt, lesen Sie die Werte über einen HTTP-Endpunkt aus, den FPM ausliefert. Die CLI hat einen eigenen, getrennten Cache und zeigt deshalb nicht die Werte Ihrer Webanfragen.
Laufzeitlimits
Zwei Fatal Errors verdienen eigene Zähler, weil sie sonst wie gewöhnliche 500er aussehen:
- „Allowed memory size of … bytes exhausted“: Eine Anfrage hat
memory_limiterreicht. Typische Ursachen sind große Ergebnismengen in Arrays, unbegrenzte Collections oder Bildverarbeitung im Request-Pfad. - „Maximum execution time of … seconds exceeded“: Eine Anfrage hat
max_execution_timeerreicht. Unter Linux zählt dieses Limit nur die Ausführungszeit des Skripts, nicht die Wartezeit auf Datenbankabfragen oder Stream-Operationen. Eine Anfrage, die an einer langsamen API hängt, kann also deutlich länger laufen. Das eigentliche Sicherheitsnetz für die Wanduhrzeit ist daherrequest_terminate_timeoutin FPM.
Zählen Sie beide pro Endpunkt.
Datenbankabfragen, externe HTTP-Aufrufe und Queue-Worker
In den meisten PHP-Anwendungen entfällt der Großteil der Antwortzeit gar nicht auf PHP, sondern auf MySQL oder PostgreSQL, Redis und Aufrufe an Zahlungsanbieter, Suchdienste oder interne APIs. Überwachen Sie diese Abhängigkeiten aus Sicht der Anwendung, pro Anfrage:
- Anzahl und Dauer der Abfragen pro Request. Springt ein Endpunkt plötzlich von 15 auf 400 Abfragen, steckt fast immer eine N+1-Abfrage dahinter, etwa eine ORM-Relation, die in einer Schleife nachgeladen wird.
- Langsamste Statements. Kombinieren Sie Spans aus der Anwendung mit der Serversicht der MySQL-Integration (Slow Query Log, Verbindungen, Lock-Wartezeiten). So erkennen Sie, ob eine Abfrage generell langsam ist oder nur unter Konkurrenz.
- Latenz und Fehler externer HTTP-Aufrufe pro Ziel-Host. Erfassen Sie Timeouts und 5xx je Abhängigkeit. Ohne explizite Timeouts im HTTP-Client kann eine einzige langsame Abhängigkeit FPM-Worker blockieren und den Pool leeren.
- Queue-Worker. Queue-Länge, Wartezeit, Laufzeit, Fehlschläge und Wiederholungen sowie Neustarts der Worker. Langlaufende PHP-CLI-Worker verlieren zudem mit der Zeit Speicher; überwachen Sie deren Speicherverbrauch und starten Sie sie regelmäßig neu.
Besonderheiten von Laravel, Symfony und WordPress
Laravel
Laravel-Queues werden meist mit php artisan queue:work abgearbeitet; fehlgeschlagene Jobs landen in der Tabelle failed_jobs, die sich gut als Zähler eignet. Horizon bietet Dashboard und Kennzahlen für Redis-Queues, und php artisan queue:monitor löst ein Event aus, sobald eine Queue eine festgelegte Anzahl von Jobs überschreitet. Behalten Sie auch geplante Aufgaben im Blick: Ein schedule:run, der unbemerkt aufhört zu laufen, ist ein häufiger blinder Fleck. Lazy Loading in Eloquent ist die übliche Quelle für N+1-Abfragen; Model::preventLazyLoading() außerhalb der Produktion hilft, sie früh zu finden.
Symfony
Symfony-Anwendungen loggen typischerweise über das MonologBundle und verarbeiten asynchrone Aufgaben mit Messenger (messenger:consume). Überwachen Sie den Failure Transport auf Nachrichten, deren Wiederholungen erschöpft sind, und die Wartezeit der Nachrichten in jedem Transport. Die Anzahl der Doctrine-Abfragen pro Request ist das Symfony-Gegenstück zur N+1-Prüfung in Eloquent.
WordPress
Die Performance von WordPress hängt vor allem von Plugins, Datenbankabfragen über $wpdb und ungecachten Seiten ab. Überwachen Sie admin-ajax.php und /wp-json/ getrennt von normalen Seitenaufrufen, da sie sich völlig anders verhalten. WP-Cron läuft standardmäßig bei Seitenaufrufen mit; auf stark besuchten Websites wird er häufig deaktiviert und über einen System-Cron angestoßen, den Sie dann wie jeden anderen geplanten Job überwachen sollten.
PHP mit OpenTelemetry instrumentieren
OpenTelemetry ist der herstellerneutrale Weg, Traces, Metriken und Logs aus PHP zu gewinnen. Laut offizieller Dokumentation auf opentelemetry.io besteht die Zero-Code-Instrumentierung für PHP aus zwei Teilen:
- Die PHP-Erweiterung
opentelemetry(ab PHP 8.0), installierbar über PECL (pecl install opentelemetry), PIE, den Docker-Helferinstall-php-extensionsoder Betriebssystempakete. Sie stellt die Hooks bereit, mit denen Bibliotheken ohne Codeänderungen instrumentiert werden. Allein erzeugt sie noch keine Telemetrie. - Composer-Pakete:
open-telemetry/sdk, ein Exporter wieopen-telemetry/exporter-otlpund ein oder mehrere Auto-Instrumentierungspakete aus dem Repository opentelemetry-php-contrib, zum Beispielopen-telemetry/opentelemetry-auto-laravel,open-telemetry/opentelemetry-auto-symfony,open-telemetry/opentelemetry-auto-wordpress,open-telemetry/opentelemetry-auto-pdo,open-telemetry/opentelemetry-auto-guzzleundopen-telemetry/opentelemetry-auto-curl.
Konfiguriert wird über die standardisierten OpenTelemetry-Umgebungsvariablen. In PHP-FPM setzen Sie diese in der Pool-Konfiguration mit env[...] oder in der php.ini. Beachten Sie, dass FPM die Umgebung standardmäßig leert (clear_env = yes):
# Umgebung für PHP-FPM-Worker (Beispiel)
OTEL_PHP_AUTOLOAD_ENABLED=true
OTEL_SERVICE_NAME=shop-api
OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=production,service.version=2026.10.3
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
OTEL_PROPAGATORS=baggage,tracecontext
Senden Sie die Daten an einen lokalen OpenTelemetry Collector statt direkt an ein Backend. Weil PHP am Ende jeder Anfrage exportiert, hält ein nahegelegener Collector die Exportlatenz niedrig und ermöglicht zentrales Batching, Filtern und Sampling. Mit Distributed Tracing sehen Sie bei einer langsamen Anfrage genau, welche Abfrage, welcher HTTP-Aufruf oder welcher Cache-Zugriff die Zeit gekostet hat; über den W3C-Header traceparent setzt sich der Trace in nachgelagerten Services fort.
Prüfen Sie vor dem Rollout den aktuellen Stand und die vollständige Paketliste in der OpenTelemetry Registry. Das PHP-Ökosystem entwickelt sich schnell, und die Pakete sind unterschiedlich ausgereift.
Strukturierte Logs mit Monolog und Trace-IDs
Die meisten PHP-Frameworks loggen über Monolog. Stellen Sie in Produktion von zeilenbasiertem Text auf strukturiertes Logging in JSON mit dem JsonFormatter um, schreiben Sie nach stdout oder in eine lokale Datei und ergänzen Sie jeden Eintrag um die aktive Trace- und Span-ID. Dann springen Sie mit einem Klick von der Logzeile zum Trace und zurück.
use Monolog\Formatter\JsonFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\LogRecord;
use OpenTelemetry\API\Trace\Span;
$handler = new StreamHandler('php://stdout');
$handler->setFormatter(new JsonFormatter());
$logger = new Logger('app');
$logger->pushHandler($handler);
$logger->pushProcessor(function (LogRecord $record): LogRecord {
$ctx = Span::getCurrent()->getContext();
if (!$ctx->isValid()) {
return $record;
}
return $record->with(extra: $record->extra + [
'trace_id' => $ctx->getTraceId(),
'span_id' => $ctx->getSpanId(),
]);
});
Alternativ stellt das Paket open-telemetry/opentelemetry-logger-monolog einen Monolog-Handler bereit, der Einträge samt Trace-Kontext über die OpenTelemetry-Logs-Pipeline versendet. In beiden Fällen gilt: Personenbezogene Daten gehören nicht in Logmeldungen oder den Log-Kontext. Maskieren Sie sie an der Quelle oder in Ihrer Telemetrie-Pipeline, bevor sie gespeichert werden. Kombinieren Sie Logs mit Fehler-Tracking, damit nicht abgefangene Exceptions nach Fingerprint gruppiert werden, statt im Logvolumen unterzugehen.
Metrik-Tabelle, Alert-Vorschläge und Checkliste für PHP Monitoring
Die folgenden Schwellenwerte sind Beispiele als Ausgangspunkt, keine Empfehlung für jede Last. Kalibrieren Sie sie anhand einiger Wochen Ihrer eigenen Baseline.
| Metrik | Warum sie wichtig ist | Typischer Schwellenwert / Alert (Beispiel) |
|---|---|---|
| p95-Latenz pro Endpunkt | Das, was Nutzer spüren; zeigt Regressionen pro Route | p95 über dem Doppelten der 7-Tage-Baseline für 10 Min. |
| 5xx- bzw. Fehlerrate pro Endpunkt | Direkte Auswirkung auf Nutzer, inklusive 502/504 durch FPM | Über 2 % für 5 Min. auf einer kritischen Route |
| FPM aktive Prozesse / max_children | Pool-Sättigung kündigt Ausfälle an | Über 85 % für 10 Min. |
| FPM max children reached | Anfragen bekamen keinen neuen Worker | Jeder Anstieg innerhalb von 15 Min. (Warnung) |
| FPM Listen Queue | Anfragen warten; die Latenz steigt gleich | Größer 0 für 2 Min. |
| FPM Slow Requests | Hinweis auf blockierende Codepfade | Rate über Baseline; Slow Log prüfen |
| OPcache-Trefferquote | Kalter oder voller Cache bremst jede Anfrage | Unter 99 % nach dem Aufwärmen |
| OPcache freier Speicher / oom_restarts | Verdrängung und Neustarts erzeugen Latenzwellen | Frei unter 10 % oder jeder OOM-Neustart |
| Fehler durch memory_limit / max_execution_time | Sonst hinter allgemeinen 500ern versteckt | Jedes neue Auftreten nach einem Deployment |
| Abfragen pro Request | Erkennt N+1-Muster | Doppelte Baseline auf einem Endpunkt |
| Latenz/Fehler externer Aufrufe pro Host | Langsame Abhängigkeiten blockieren Worker | Timeouts über 1 % für 5 Min. |
| Queue-Länge und Wartezeit der Jobs | Rückstau verzögert E-Mails, Bestellungen, Exporte | Wartezeit über 5 Min. für 10 Min. |
Alarmieren Sie per Pager nur bei Symptomen, die Nutzer treffen (Fehlerrate, Latenz-SLOs, gesättigter Pool mit wachsender Queue), und leiten Sie den Rest in Tickets oder den Chat. Wie Sie das strukturieren, beschreibt unser Artikel zum Reduzieren von Alert Fatigue.
Checkliste für PHP Monitoring
- Request-Rate, Fehlerrate und p50/p95/p99-Latenz pro Routen-Template
- Upstream-Zeit von Nginx oder Apache neben der PHP-Request-Zeit geloggt
pm.status_pathundping.pathpro Pool aktiviert und nur intern erreichbar- Aktive/freie Prozesse, max children reached und Listen Queue alle 10–30 s erfasst
request_slowlog_timeout,slowlogundrequest_terminate_timeoutgesetzt- OPcache-Trefferquote, freier Speicher, gecachte Schlüssel und OOM-Neustarts aus FPM gelesen, nicht aus der CLI
- Eigene Zähler für Fatal Errors durch memory_limit und max_execution_time
- Anzahl, Dauer und langsamste Statements der Abfragen pro Request
- Explizite Timeouts und Metriken pro Ziel-Host für jeden HTTP-Client
- Queue-Länge, Wartezeit, Fehlschläge und Worker-Speicher für jede Queue
- Geplante Jobs (Laravel Scheduler, WP-Cron, System-Cron) auf ausgefallene Läufe überwacht
- OpenTelemetry-Erweiterung, SDK, OTLP-Exporter und Framework-Auto-Instrumentierung installiert
service.name, Umgebung und Version als Resource-Attribute gesetzt- Monolog schreibt JSON mit trace_id und span_id; keine personenbezogenen Daten in Logs
- Alerts auf nutzerrelevante Symptome; Deployment-Marker in allen Dashboards
Wie Obseria beim PHP Monitoring unterstützt
Obseria ist eine OpenTelemetry-native Observability-Plattform. Die beschriebene PHP-Erweiterung, die Auto-Instrumentierungspakete und Monolog-JSON-Logs funktionieren daher ohne proprietären Agent. Die PHP-Integration führt PHP-FPM- und OPcache-Metriken mit Traces und Logs derselben Anfrage zusammen, und Datenbank-Monitoring verbindet langsame Endpunkte mit den Abfragen dahinter.
- Obseria Pipeline filtert, sampelt und maskiert personenbezogene Daten in der PHP-Telemetrie, bevor sie in der EU gespeichert wird.
- Obseria TopoGraph zeigt, wie PHP-Services mit Datenbanken, Caches, Queues und externen APIs zusammenhängen.
- Obseria Intelligence verknüpft einen gesättigten FPM-Pool, eine langsame Abfrage und ein aktuelles Deployment zu einer Ursache mit Belegen.
- SLOs mit Burn-Rate-Alerts ersetzen statische Schwellenwerte für Ihre wichtigsten Endpunkte.
Ihren ersten PHP-Service verbinden Sie in wenigen Minuten mit einem kostenlosen Obseria-Test.