PHP-Logging richtig einrichten mit error_log() und Monolog

16. Juni 2026

PHP-Konfiguration zeigt den Pfad zur error_log-Datei. Die Einstellung für error_log ist c:/wamp64/logs/php_error.log.

Inhaltsverzeichnis

Ein fehlender Log-Eintrag kann die Fehlersuche in einer PHP-Anwendung unnötig verlängern, während ein unstrukturierter Log schnell zur Datenhalde wird. Ich zeige, wie das native PHP-Logging mit error_log(), php.ini, Dateien und Systemdiensten funktioniert und wann sich eine Bibliothek wie Monolog lohnt. Dazu kommen praxistaugliche Beispiele, typische Fehler und Regeln für sichere Logs in produktiven Umgebungen.

Mit wenigen Einstellungen wird aus PHP-Logging ein verlässliches Diagnosewerkzeug

  • Produktion: Fehler protokollieren, aber niemals direkt im Browser anzeigen.
  • error_log(): Für einzelne Meldungen genügt oft die eingebaute PHP-Funktion.
  • Ziel: Logs gehören in eine Datei, an stderr oder in einen zentralen Syslog-Dienst.
  • Struktur: Zeitstempel, Schweregrad, Kontext und eine Request-ID machen Einträge auswertbar.
  • Sicherheit: Passwörter, Tokens und vollständige Zahlungsdaten dürfen niemals im Log landen.

PHP-Code generiert Log-Dateien, die in der Cloud analysiert werden.

Was PHP-Logging praktisch leisten muss

Ein Log beschreibt, was in einer Anwendung passiert ist, wann es passiert ist und unter welchen Bedingungen. Das reicht von einem erfolgreichen Zahlungsvorgang bis zu einer nicht erreichbaren Datenbank. Ein guter Eintrag hilft mir nicht nur zu erkennen, dass etwas schiefgegangen ist, sondern auch, wo und warum.

Die Suchintention hinter „php log“ ist deshalb vor allem informativ und beratend. Entwickler möchten meist eine konkrete Meldung schreiben, PHP-Fehler in einer Datei finden oder verstehen, warum trotz aktivierter Protokollierung kein Eintrag erscheint. Diese drei Fälle brauchen unterschiedliche Einstellungen.

PHP unterscheidet zwischen Fehleranzeige und Fehlerprotokollierung. display_errors betrifft die Ausgabe an den Besucher, während log_errors festlegt, ob Fehler intern gespeichert werden. Im Livebetrieb sollten Fehlermeldungen nicht auf der Webseite erscheinen, weil sie Dateipfade, SQL-Details oder sensible Konfigurationswerte verraten können.

Für die technische Grundlage sind die Einstellungen error_reporting, display_errors, log_errors und error_log entscheidend. Das offizielle PHP Manual führt diese Optionen als Teil der PHP-Laufzeitkonfiguration.

Die wichtigsten Bordmittel für Meldungen und Fehler

Einzelne Ereignisse mit error_log schreiben

Für einen einfachen Hinweis brauche ich keine zusätzliche Bibliothek. Die Funktion error_log() schreibt eine Nachricht in den konfigurierten System- oder PHP-Log. Eine kurze Meldung kann bereits genügen, sollte aber möglichst einen konkreten Vorgang beschreiben.

Der zweite Parameter 0 ist der Standard und verwendet den von PHP konfigurierten Logger. Ich setze bei solchen Meldungen lieber einen verständlichen Text ein, statt einfach den Inhalt eines ganzen Arrays zu serialisieren. Das hält die Ausgabe lesbar und verhindert, dass ungewollt vertrauliche Daten auftauchen.

Eine Datei lässt sich mit dem Nachrichtentyp 3 direkt anhängen:

Bei diesem Modus fügt PHP keinen Zeilenumbruch automatisch hinzu. Genau das wird häufig übersehen. Für eine kleine lokale Anwendung funktioniert die Methode gut, für mehrere Server oder Container ist ein zentraler Ausgabekanal meistens die bessere Wahl.

Fehler in Entwicklung und Produktion konfigurieren

In einer Entwicklungsumgebung möchte ich möglichst viel sehen. In Produktion soll die Anwendung dagegen stabil und diskret bleiben. Eine typische Trennung sieht so aus:

; Entwicklung
error_reporting = E_ALL
display_errors = On
log_errors = On

; Produktion
error_reporting = E_ALL
display_errors = Off
log_errors = On
error_log = /var/log/php/application.log

E_ALL ist beim Entwickeln sinnvoll, weil auch Warnungen und veraltete Funktionen früh sichtbar werden. In produktiven Systemen ist es nicht automatisch falsch, ebenfalls alles zu protokollieren. Entscheidend sind ein brauchbarer Filter, eine Aufbewahrungsregel und ein Monitoring, das aus vielen unkritischen Meldungen keinen Alarmsturm macht.

Wenn der Fehler bereits vor dem Start des eigentlichen Skripts entsteht, kann eine Einstellung im Code zu spät kommen. Deshalb gehören die zentralen Optionen in die passende php.ini, in die PHP-FPM-Konfiguration oder in die Umgebung des jeweiligen Containers.

Exceptions zentral auffangen

Unbehandelte Exceptions sollten nicht an verschiedenen Stellen unterschiedlich behandelt werden. Ein zentraler Handler kann die Exception protokollieren und dem Nutzer gleichzeitig eine neutrale Fehlerseite liefern.

getMessage(),
        $exception->getFile(),
        $exception->getLine()
    ));

    http_response_code(500);
    echo 'Ein interner Fehler ist aufgetreten.';
});

Ich protokolliere dabei den Stacktrace nur dort, wo er geschützt ist. Eine vollständige Exception-Ausgabe im Browser gehört nicht in die Produktion. Außerdem sollte ein Handler nicht selbst einen zweiten Fehler auslösen, etwa durch einen nicht beschreibbaren Log-Pfad.

Wohin die Meldungen geschrieben werden

Der Code kann korrekt sein und trotzdem scheinbar keinen Log erzeugen. Der häufigste Grund ist nicht error_log(), sondern ein falscher Blick auf das Ziel. Je nach Betriebsart landen Einträge in einer Datei, im Webserver-Log, bei PHP-FPM, im Systemjournal oder auf stderr.

Ziel Geeignet für Typische Stolperfalle
Eigene Datei Kleine Anwendungen und lokale Entwicklung Fehlende Schreibrechte oder ungebremstes Wachstum
Webserver-Log Klassische Apache- oder Nginx-Installationen PHP- und Zugriffslogs werden verwechselt
PHP-FPM oder stderr Container, Plattformbetrieb und Prozessmanager Der Log liegt außerhalb des Projektverzeichnisses
Syslog oder Journald Server mit zentraler Administration Filter oder Berechtigungen unterdrücken die Meldung

Bei einer Linux-Installation prüfe ich zuerst die effektive PHP-Konfiguration, nicht nur irgendeine Datei im Projekt. Mit php --ini sehe ich die CLI-Konfiguration. Für Webanfragen kann jedoch eine andere PHP-SAPI aktiv sein, etwa PHP-FPM.

Eine solche Diagnose darf nur kurzzeitig und geschützt erreichbar sein, weil phpinfo() viele interne Details offenlegt. Alternativ kann eine kleine Testseite mit ini_get('log_errors') und ini_get('error_log') die relevante Konfiguration gezielter anzeigen.

In Containern schreibe ich Logs bevorzugt nach stderr oder stdout. Docker, Kubernetes und viele Hostingplattformen sammeln diese Streams bereits ein. Eine lokale Datei im Container kann dagegen beim Neustart verschwinden und ist dann für die Analyse wertlos.

Wann error_log genügt und wann Monolog besser passt

Für ein kleines Skript, einen Cronjob oder eine einzelne Warnung ist die native Funktion völlig ausreichend. Sobald mehrere Module, Log-Level, Ausgabekanäle oder strukturierte Zusatzdaten ins Spiel kommen, wird handgebautes Logging schnell unübersichtlich.

Monolog bietet eine einheitliche Schnittstelle nach PSR-3. Das ist ein verbreiteter Standard für Logger-Methoden wie debug(), info(), warning() und error(). Die Bibliothek kann unter anderem Dateien, Syslog, Sockets und externe Dienste bedienen. Die offizielle Monolog-Dokumentation beschreibt dieses Zusammenspiel aus Logger, Handlern, Formatierern und Prozessoren.

composer require monolog/monolog
pushHandler(
    new StreamHandler(__DIR__ . '/var/shop.log', Level::Info)
);

$logger->info('Bestellung angelegt', [
    'order_id' => 4815,
    'customer_id' => 73,
]);

Der Vorteil liegt nicht nur in schöneren Methoden. Ein Handler kann beispielsweise alle Meldungen in eine Datei schreiben, während ein zweiter nur bei kritischen Fehlern eine Benachrichtigung auslöst. Dadurch bleibt der Anwendungscode von der späteren Infrastruktur weitgehend unabhängig.

Für produktive Systeme bevorzuge ich strukturierte Logs im JSON-Format. Sie lassen sich von Logplattformen zuverlässig durchsuchen und nach Feldern filtern. Ein Eintrag mit order_id, request_id und duration_ms ist in der Praxis deutlich wertvoller als ein langer Freitext mit denselben Informationen.

Level Beispiel Reaktion
debug SQL-Parameter in einer Testumgebung Nur bei gezielter Fehlersuche aktivieren
info Import erfolgreich abgeschlossen Für Ablauf und Kennzahlen nutzen
warning Externer Dienst antwortet ungewöhnlich langsam Beobachten und Schwellenwerte prüfen
error Bestellung konnte nicht gespeichert werden Untersuchen, eventuell alarmieren
critical Datenbank dauerhaft nicht erreichbar Sofortige technische Reaktion

Ein häufiger Fehler ist, jede Meldung als error zu markieren. Dann verliert der Schweregrad seine Bedeutung. Ich verwende warning für auffällige, aber noch abgefangene Zustände und reserviere error für Vorgänge, die tatsächlich fehlgeschlagen sind.

Welche Informationen ein guter Log-Eintrag braucht

Ein brauchbarer Eintrag beantwortet mindestens vier Fragen: Was ist passiert, wann ist es passiert, welcher Vorgang war betroffen und wie kann ich ihn wiederfinden? Für Webanwendungen kommen meist HTTP-Methode, Route, Statuscode, Nutzer- oder Mandanten-ID und eine Request-ID hinzu.

 'payment_failed',
    'request_id' => $requestId,
    'order_id' => 4815,
    'provider' => 'stripe',
    'duration_ms' => 842,
    'level' => 'error',
    'time' => date(DATE_ATOM),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

Die Request-ID verbindet Einträge aus PHP, Webserver, Datenbank und externen Diensten. Gerade bei asynchronen Jobs oder mehreren Containern spart dieses kleine Feld oft mehr Zeit als jede zusätzliche Debug-Ausgabe.

Ich vermeide vollständige Nutzlasten, sofern sie für die Diagnose nicht zwingend nötig sind. Besonders kritisch sind Passwörter, Session-Cookies, API-Schlüssel, Kreditkartendaten und personenbezogene Inhalte. Auch E-Mail-Adressen oder IP-Adressen können je nach Zweck und Aufbewahrung datenschutzrechtlich relevant sein.

Ein Log ist kein Ersatz für Metriken und Traces. Logs erklären einzelne Ereignisse, Metriken zeigen Mengen und Trends, während Traces den Weg einer Anfrage durch mehrere Dienste verfolgen. Für eine kleine PHP-Anwendung reichen Logs zunächst aus, bei verteilten Systemen sollte ich diese drei Perspektiven nicht miteinander verwechseln.

Rotation, Rechte und Fehlersuche ohne blinde Flecken

Eine Logdatei wächst, solange Fehler auftreten oder die Anwendung läuft. Ohne Rotation kann sie den Datenträger füllen und dadurch ausgerechnet den Dienst beeinträchtigen, den sie überwachen soll. Für produktive Server gehören deshalb Rotation, Kompression und eine definierte Aufbewahrungsdauer zur Grundkonfiguration.

Die konkrete Dauer hängt vom Zweck und von internen Regeln ab. Für technische Diagnose reichen häufig 14 bis 30 Tage, während sicherheitsrelevante oder geschäftliche Nachweise andere Vorgaben haben können. Eine pauschale Aufbewahrung über Jahre ist selten eine gute Idee.

Auch die Dateirechte müssen zum Prozess passen. Der Benutzer von Apache, Nginx oder PHP-FPM braucht Schreibzugriff auf das Ziel, aber die Logdatei sollte nicht öffentlich aus dem Webverzeichnis abrufbar sein. Ein Pfad wie /var/www/html/app.log ist daher riskanter als ein geschützter Speicherort außerhalb des Document Roots.

Lesen Sie auch: Git-Best-Practices für klare Commits und sichere Releases

Wenn keine Meldung erscheint

  1. Prüfen, ob tatsächlich die erwartete PHP-SAPI verwendet wird.
  2. log_errors und error_log in dieser Umgebung kontrollieren.
  3. Schreibrechte des PHP-Prozesses auf Datei und Verzeichnis prüfen.
  4. Webserver-, PHP-FPM-, Container- und Systemlogs getrennt durchsuchen.
  5. Mit einer eindeutigen Testmeldung verifizieren, ob der Codepfad erreicht wird.

Ein Klassiker ist die Verwechslung von CLI und Webserver. Ein Befehl im Terminal kann eine andere php.ini laden als eine Anfrage über PHP-FPM. Ebenso kann ein relativer Pfad je nach Startverzeichnis an einer unerwarteten Stelle landen.

Das Unterdrücken von Fehlern mit dem Operator @ halte ich nur in sehr wenigen, bewusst geprüften Fällen für vertretbar. Es nimmt der Fehlersuche wichtige Signale und kann dazu führen, dass Probleme still weiterlaufen. Besser ist es, erwartbare Fehler sauber abzufangen und mit einem passenden Level zu protokollieren.

Ein belastbares Logging-Konzept für PHP

Für eine kleine Anwendung genügt meist log_errors = On, eine geschützte Zieldatei und ein sparsamer Einsatz von error_log(). Mit wachsender Anwendung lohnt sich eine PSR-3-kompatible Bibliothek, weil Level, Kontext, JSON-Format und mehrere Ausgabekanäle dann zentral konfiguriert werden können.

Mein pragmatischer Mindeststandard besteht aus keinen Fehlermeldungen im Browser, eindeutigen Ereignisnamen, einer Request-ID, geprüften Dateirechten und einer automatischen Rotation. Wer zusätzlich regelmäßig nach Fehlerraten, Antwortzeiten und wiederkehrenden Ereignissen schaut, macht aus einzelnen Logzeilen ein echtes Betriebswerkzeug.

Die beste Protokollierung ist nicht die lauteste. Sie ist so konkret, dass ein anderer Entwickler den Vorfall nachvollziehen kann, so sparsam, dass wichtige Meldungen nicht untergehen, und so sicher, dass der Log selbst kein neues Risiko darstellt.

Häufig gestellte Fragen

display_errors steuert, ob Fehlermeldungen im Browser erscheinen, während log_errors die interne Protokollierung aktiviert. In der Produktion sollte display_errors deaktiviert und log_errors aktiviert sein, damit keine internen Details an Besucher gelangen.

Prüfen Sie zunächst die tatsächlich verwendete PHP-SAPI, da CLI und PHP-FPM unterschiedliche php.ini-Dateien laden können. Kontrollieren Sie anschließend log_errors, error_log, die Schreibrechte des PHP-Prozesses sowie Webserver-, PHP-FPM-, Container- und Systemlogs.

error_log() genügt für einzelne Meldungen, kleine Skripte und Cronjobs. Monolog lohnt sich bei mehreren Modulen, Log-Leveln, Ausgabekanälen oder strukturierten Zusatzdaten, weil Handler, Formatierer und Prozessoren zentral konfiguriert werden können.

Hilfreich sind Ereignisname, Zeitstempel, Schweregrad, Request-ID sowie passende Kontextfelder wie order_id oder duration_ms. Passwörter, Session-Cookies, API-Schlüssel, Kreditkartendaten und unnötige personenbezogene Inhalte dürfen nicht protokolliert werden; produktive Logs sollten außerdem rotiert und geschützt gespeichert werden.

Artikel bewerten

Bewertung: 0.00 Stimmenanzahl: 0

Tags:

logrotation json fehlerprotokollierung monolog syslog

Beitrag teilen

Artur Lemke

Artur Lemke

Mein Name ist Artur Lemke und seit nunmehr 11 Jahren beschäftige ich mich intensiv mit der Welt der Webentwicklung, der digitalen Strategie und künstlichen Intelligenz. Diese Themen faszinieren mich, weil sie die Art und Weise, wie wir arbeiten und interagieren, grundlegend verändern. Ich liebe es, komplexe Zusammenhänge zu durchdringen und sie so aufzubereiten, dass sie für jeden verständlich werden. Hier auf metawebart.de teile ich mein Wissen, analysiere aktuelle Trends und helfe Ihnen dabei, die Potenziale von KI und digitalen Strategien für Ihr eigenes Vorhaben zu erkennen und zu nutzen. Dabei lege ich großen Wert darauf, fundierte und praxisnahe Informationen zu liefern, die Ihnen wirklich weiterhelfen.

Kommentar schreiben