Mein Platz in der WordPress-Community
- Mein wordpress.org-Konto besteht seit 2006.
- Ich habe zum WordPress-Core in den Versionen 7.0 und 7.1 beigetragen.
- Ich betreue 30 Plugins auf wordpress.org.
- Ich organisiere WordCamps und trete dort als Speaker auf.
- Ich war bei jedem WordCamp Europe von 2017 bis 2026 dabei.
Worum es auf dieser Seite geht
Wenn eine PHP-Anwendung im Betrieb Fehler macht, bleiben zwei Dinge zurück: eine Beschwerde und eine Logdatei. Ob aus der Beschwerde in kurzer Zeit eine Ursache wird, entscheidet die Logdatei. Auf dieser Seite beschreibe ich, wie ich Logging und Fehlerbehandlung in eigenen PHP-Anwendungen einrichte: was PHP selbst protokolliert, wie PSR-3 und Monolog das ordnen, was nicht ins Log gehört und wie lange personenbezogene Daten dort liegen dürfen.
Ich bin ein einzelner Entwickler und arbeite remote und schriftlich. Es geht um Anwendungen mit Laravel, Symfony oder ohne Framework, nicht um WordPress-Seiten. Die Beispiele laufen mit PHP 8.4 und Monolog 3.12.1.
Was PHP selbst einstellt: anzeigen oder protokollieren
Die Dokumentation von PHP nennt als Standardwert für display_errors den Wert “1” und für log_errors den Wert “0”. Die mitgelieferte php.ini-production setzt display_errors auf Off. Zur Anzeige im Browser steht dort, sie sei eine Hilfe für die Entwicklung und gehöre nie auf ein Produktivsystem. Für Websites im Netz rät die Dokumentation ausdrücklich, Fehler zu protokollieren statt anzuzeigen.
| Einstellung | Was sie steuert | Was ich in Produktion setze |
|---|---|---|
display_errors | Fehlermeldungen in der ausgelieferten Seite | Off |
display_startup_errors | Fehler beim Start von PHP | Off, nur zur gezielten Fehlersuche an |
log_errors | Schreiben ins Fehlerlog | On |
error_log | Zieldatei, oder der Sonderwert syslog | Datei außerhalb des Webverzeichnisses, vom Webserver-Benutzer beschreibbar |
error_reporting | Welche Fehlerklassen gemeldet werden | E_ALL, seit PHP 8.0 der Standard |
; php.ini oder PHP-FPM-Pool, nicht im Anwendungscode
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/anwendung-fehler.log
error_reporting = E_ALLDie Werte gehören in die Konfiguration des Servers. Die Dokumentation weist darauf hin, dass ein zur Laufzeit per ini_set() gesetztes display_errors bei fatalen Fehlern nichts bewirkt, weil die Anweisung dann nie ausgeführt wird. Eine Fehlermeldung im Browser kann außerdem Dateipfade und Teile der Anwendung offenlegen, die ein Besucher nicht sehen soll.
PSR-3: eine Schnittstelle, acht Stufen
Das Logging einer Anwendung sollte nicht an einer Bibliothek hängen. Der Standard PSR-3 der PHP-FIG beschreibt dafür das LoggerInterface mit acht Methoden, eine je Stufe nach RFC 5424: debug, info, notice, warning, error, critical, alert, emergency. Eine neunte Methode, log(), nimmt die Stufe als Argument.
Zwei Regeln aus dem Standard bestimmen, wie ich Meldungen schreibe. Die Meldung darf Platzhalter in geschweiften Klammern enthalten, etwa {order}, und der Name muss einem Schlüssel im Kontext-Array entsprechen. Und eine Ausnahme gehört im Kontext unter den Schlüssel exception, damit der Logger den Stacktrace herauslesen kann.
<?php
declare(strict_types=1);
use Psr\Log\LoggerInterface;
function chargeOrder(LoggerInterface $logger, int $orderId): void
{
$logger->info('Zahlung gestartet für Bestellung {order}', ['order' => $orderId]);
try {
// Aufruf des Zahlungsanbieters
} catch (RuntimeException $e) {
$logger->error('Zahlung fehlgeschlagen für Bestellung {order}', [
'order' => $orderId,
'exception' => $e,
]);
}
}Die Meldung bleibt ein fester Satz, die veränderlichen Werte stehen im Kontext. Das macht die Log-Zeilen durchsuchbar: Wer nach Zahlung fehlgeschlagen sucht, findet alle Fälle, egal für welche Bestellung. Die Funktion kennt nur das Interface. Ob dahinter Monolog oder ein anderer PSR-3-Logger steckt, ist eine Konfigurationsfrage.
Monolog einrichten
Monolog ist ein PSR-3-Logger, installiert mit composer require monolog/monolog. Die Version 3.12.1 erschien laut Changelog am 29. September 2026 und verlangt nach der composer.json mindestens PHP 8.1. Ein Logger hat einen Kanal als Namen und einen Stapel von Handlern, die jeden Eintrag nacheinander bekommen. Jeder Handler hat einen Formatter.
Die Stufen beschreibt die Dokumentation so: WARNING für Außergewöhnliches, das kein Fehler ist, etwa die Nutzung veralteter Schnittstellen. ERROR für Laufzeitfehler, die kein sofortiges Handeln verlangen, aber protokolliert und überwacht werden sollen. CRITICAL für eine ausgefallene Komponente oder eine unerwartete Ausnahme. ALERT für den Fall, dass sofort gehandelt werden muss, etwa bei einer komplett ausgefallenen Website. Meine Zuordnung dazu: Eine unbehandelte Ausnahme ist critical, ein nicht erreichbarer Zahlungsanbieter error, eine abgelehnte Zahlung warning.
<?php
declare(strict_types=1);
use Monolog\Formatter\JsonFormatter;
use Monolog\Formatter\RedactingFormatter;
use Monolog\Handler\FingersCrossedHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Level;
use Monolog\Logger;
use Monolog\Processor\PsrLogMessageProcessor;
$datei = new RotatingFileHandler(__DIR__ . '/var/log/app.log', 14, Level::Info);
$datei->setFormatter(new RedactingFormatter(new JsonFormatter()));
$logger = new Logger(
'bestellungen',
[new FingersCrossedHandler($datei, Level::Error)],
[new PsrLogMessageProcessor()]
);Der FingersCrossedHandler sammelt nach der Dokumentation alle Einträge, bis einer die eingestellte Stufe erreicht. Dann gibt er alle weiter, auch die mit niedriger Stufe. Im Alltag bleibt das Log klein, und im Fehlerfall steht der Weg bis zum Fehler darin. Die gesammelten Einträge können Personendaten enthalten, sie unterliegen daher denselben Regeln wie alles im Log, siehe unten. Der RotatingFileHandler legt eine Datei je Tag an und löscht Dateien, die älter sind als $maxFiles; die 14 im Beispiel ist ein Platzhalter. Die Dokumentation empfiehlt für größere Setups logrotate.
Für unbehandelte Ausnahmen registriere ich einen Handler, den PHP aufruft, wenn eine Ausnahme nirgends gefangen wird. Danach beendet PHP die Ausführung, die Seite muss also selbst eine neutrale Antwort liefern.
set_exception_handler(function (Throwable $e) use ($logger): void {
$logger->critical('Unbehandelte Ausnahme', ['exception' => $e]);
http_response_code(500);
echo 'Es ist ein Fehler aufgetreten.';
});Sinnvoll sind außerdem getrennte Kanäle. Mit $logger->withName('sicherheit') entsteht ein zweiter Logger, dessen Einträge sich im selben Log mit grep auf den Kanalnamen filtern lassen.
Was nicht ins Log gehört
Passwörter, Tokens, der Authorization-Header, Cookies, komplette Anfragekörper und Zahlungsdaten haben im Log nichts verloren. Für Kundendaten gilt: lieber die interne ID als Name und Anschrift. Der Grundsatz der Datenminimierung in Art. 5 Abs. 1 Buchst. c DSGVO verlangt, dass personenbezogene Daten dem Zweck angemessen und auf das notwendige Maß beschränkt sind. Ein Fehlerlog braucht zum Nachvollziehen eines Fehlers meistens nur die ID.
Monolog bringt seit Version 3.11.0 vom 2. September 2026 den RedactingFormatter mit. Er umhüllt einen anderen Formatter und maskiert Werte von Kontext-Schlüsseln wie password, token, authorization und cookie. Er entfernt außerdem bekannte Geheimnisse aus der fertigen Ausgabe und kann zusätzlich reguläre Ausdrücke anwenden. Das Muster TOKEN_PATTERN für lange, tokenähnliche Zeichenfolgen ist nach der Quelle bewusst optional, weil es auch Hashes oder UUIDs treffen kann.
In einem Testlauf mit dem Code oben erschien 'password' => 'geheim123' im Kontext als "password":"[REDACTED]". Das ist ein Sicherheitsnetz, kein Freibrief: Maskiert wird, was der Name verrät. Ein Passwort, das jemand in einen Freitext schreibt, erkennt kein Formatter. Deshalb halte ich Meldungen frei von Daten und lege Werte nur gezielt in den Kontext.
IP-Adressen und Speicherdauer
Erwägungsgrund 30 der DSGVO nennt IP-Adressen ausdrücklich als Online-Kennungen, die in Kombination mit anderen Informationen dazu genutzt werden können, Profile zu erstellen und Personen zu identifizieren. Ein Log mit IP-Adresse ist deshalb eine Verarbeitung personenbezogener Daten. Drei Artikel bestimmen den Rahmen:
- Art. 6 Abs. 1 Buchst. f: Die Verarbeitung ist rechtmäßig, wenn sie zur Wahrung berechtigter Interessen erforderlich ist und die Interessen der betroffenen Person nicht überwiegen. Ob das für Ihr Log trägt, bewerten Ihre Datenschutzberatung oder Ihr Datenschutzbeauftragter.
- Art. 5 Abs. 1 Buchst. b, c und e: Zweckbindung, Datenminimierung und Speicherbegrenzung. Personen dürfen nur so lange identifizierbar bleiben, wie es für den Zweck erforderlich ist. Die Verordnung nennt dafür keine feste Zahl von Tagen.
- Art. 32: Der Verantwortliche trifft geeignete technische Maßnahmen, die unter anderem Pseudonymisierung einschließen können.
Die Frist legen also Sie mit Ihrer Beratung fest. Ich setze sie technisch um. Dafür gibt es vier Stellschrauben:
- Gar nicht protokollieren. Der
WebProcessorvon Monolog ergänzt jeden Eintrag um URI, Methode und Client-IP. Ich schalte ihn nur ein, wenn es dafür einen benannten Zweck gibt. - Kürzen. Meine Gewohnheit ist, das letzte Oktett einer IPv4-Adresse und bei IPv6 die hinteren Bytes auf null zu setzen. Ob das Ihrer Datenschutzberatung genügt, ist eine rechtliche Frage.
- Rotation auf die Frist abstimmen.
maxFilesim Beispiel ist die technische Umsetzung der Tage, die Sie festlegen. - Backups beachten. Liegt das Logverzeichnis im Backup, überleben die Einträge die Rotation. Ich nehme es entweder aus dem Backup aus oder stimme die Aufbewahrung ab.
function anonymizeIp(string $ip): string
{
$bin = inet_pton($ip);
if ($bin === false) {
return 'ungueltig';
}
$behalten = strlen($bin) === 4 ? 3 : 6; // IPv4: 3 Bytes, IPv6: 6 Bytes
return inet_ntop(substr($bin, 0, $behalten) . str_repeat("\0", strlen($bin) - $behalten));
}
$logger->pushProcessor(function (\Monolog\LogRecord $record): \Monolog\LogRecord {
$record->extra['ip'] = anonymizeIp($_SERVER['REMOTE_ADDR'] ?? '');
return $record;
});Aus 203.0.113.77 wird 203.0.113.0, aus 2001:db8:abcd:12:1:2:3:4 wird 2001:db8:abcd::. Hinter einem Proxy oder CDN enthält REMOTE_ADDR die Adresse des Proxys. Welchem Header Sie trauen dürfen, hängt von Ihrer Infrastruktur ab, das kläre ich vorher. Schickt die Anwendung Logs an einen externen Dienst, ist dieser Dienst in der Regel Auftragsverarbeiter, und Art. 28 Abs. 1 DSGVO verlangt, dass Sie nur mit Dienstleistern arbeiten, die hinreichende Garantien für geeignete Maßnahmen bieten.
Alarmierung: wer erfährt wann von einem Fehler
Ein Log, das niemand liest, hilft erst beim nächsten Streit. Die Dokumentation von Monolog ordnet die Stufen so ein: ERROR soll protokolliert und überwacht werden, ALERT soll Alarme auslösen. Daraus folgt meine Aufteilung. critical und höher gehen sofort an eine Mailbox oder einen Chat, error zählt der Monatsbericht, warning und darunter bleiben im Log.
Monolog liefert dafür Handler, etwa für Mail über NativeMailerHandler oder SymfonyMailerHandler und für Slack über Webhooks. Wichtig ist der DeduplicationHandler. Nach der Dokumentation sammelt er Einträge bis zum Ende der Anfrage und gibt sie nur weiter, wenn sie sich in einem Zeitraum (standardmäßig 60 Sekunden) nicht wiederholen. Ist die Datenbank nicht erreichbar, scheitert jede Anfrage, und ohne diesen Handler käme für jede eine Nachricht. Steht der Server, schreibt auch kein Logger mehr. Deshalb braucht es zusätzlich eine Verfügbarkeitsprüfung von außen. Wer die Meldungen empfängt und in welchem Rhythmus sie gelesen werden, halte ich schriftlich fest.

Foto: Berthold Werner, CC BY-SA 3.0, Wikimedia Commons.
Wenn in Wiesbaden etwas schiefgeht: das Log als Grundlage für die Meldung
Für Unternehmen in Hessen ist der HBDI zuständig. Er beschreibt auf seiner Seite zu Datenschutzverletzungen, was bei einer Meldung zählt. Der Verantwortliche muss jede Verletzung dokumentieren, und die Meldung soll unverzüglich und möglichst binnen 72 Stunden nach Bekanntwerden erfolgen. Eine erste Meldung darf unvollständig sein, fehlende Angaben sind laut HBDI kein Grund für eine späte Erstmeldung. Für die Bearbeitung nennt der HBDI als ersten Punkt die Sachverhaltsschilderung: Ein bloßes Stichwort wie “Phishing” genügt nicht, die Art und der Verlauf des Vorfalls sollen möglichst genau beschrieben werden.
Genau dafür ist ein brauchbares Log da. Zeitpunkte, betroffene Kennungen und der Ablauf eines Zugriffs lassen sich aus Einträgen mit Kanal, Stufe und ID ablesen. Aus Einträgen ohne Kontext lassen sie sich nicht rekonstruieren. Das ist der Grund, warum ich Logging nicht erst nach einem Vorfall einrichte. Die rechtliche Bewertung eines Vorfalls und der Text der Meldung liegen bei Ihnen und Ihrer Beratung.
Checkliste für Ihre Anwendung
| Prüfpunkt | Gut, wenn |
|---|---|
display_errors in Produktion | Off in der Server-Konfiguration, nicht im Code |
log_errors und error_log | an, Zieldatei außerhalb des Webverzeichnisses |
| Logger im Code | PSR-3-Interface, nicht eine feste Klasse |
| Meldungen | fester Satz, Werte im Kontext |
| Ausnahmen | im Kontext unter exception, nicht als Text zusammengeklebt |
| Passwörter, Tokens, Cookies | nie im Log, Maskierung als zweites Netz |
| IP-Adresse | nur mit Zweck, gekürzt, Entscheidung dokumentiert |
| Speicherdauer | von Ihnen festgelegt, in Rotation und Backup umgesetzt |
| Alarm | Empfänger und Stufe schriftlich festgelegt, Wiederholungen gebündelt |
| Prüfung von außen | Verfügbarkeitsprüfung unabhängig vom Server |
So läuft ein Projekt mit mir
Gestaltung, Mockups und Wireframes sind für diese Arbeit nicht nötig. Es geht um Code und Konfiguration.
- Schriftliche Anfrage. Sie schicken eine kurze Beschreibung der Anwendung, die PHP-Version und das Framework.
- Bestandsaufnahme. Ich lese die PHP-Konfiguration, die vorhandene Logik zum Protokollieren und Fehlerfangen und halte offene Punkte schriftlich fest. Nutzt die Anwendung schon einen Logger, baue ich darauf auf, statt einen zweiten daneben zu setzen.
- Festlegung. Welche Stufen alarmieren, welche Daten ins Log dürfen und wie lange sie bleiben, steht in einem kurzen Dokument. Die Frist und die Rechtsgrundlage tragen Sie mit Ihrer Beratung ein.
- Umsetzung auf einer Testumgebung. Ich provoziere jeden Fehlerpfad und prüfe, was im Log steht und was fehlen muss.
- Übergabe. Sie erhalten Konfiguration, Code und eine Anleitung, wie ein Eintrag gelesen wird. Danach betreuen Sie die Anwendung selbst oder beauftragen die Wartung in Wiesbaden. Ungenutzte Wartungsstunden verfallen am Monatsende.
Dafür brauche ich Lesezugriff auf den Code, die PHP-Konfiguration des Servers und ein Beispiel für eine Fehlermeldung, die Sie kennen. Zugangsdaten schicken Sie nicht per E-Mail, den Übergabeweg kläre ich vorher schriftlich mit Ihnen.
Häufige Fragen
Darf display_errors in Produktion an sein?
Nein. Die Dokumentation von PHP sagt, dass die Anzeige für die Entwicklung gedacht ist und nie auf Systemen laufen soll, die mit dem Internet verbunden sind. Für Produktion rät sie, Fehler zu protokollieren statt anzuzeigen. Ich setze die Werte in der Server-Konfiguration.
Welche Log-Stufe nehme ich wofür?
Monolog beschreibt acht Stufen nach RFC 5424. Meine Zuordnung: critical für unbehandelte Ausnahmen, error für Fehler, die geprüft werden sollen, warning für erwartbare Störungen wie eine abgelehnte Zahlung. info bleibt für Ereignisse, die Sie später nachvollziehen wollen.
Darf ich IP-Adressen im Log speichern?
Die DSGVO nennt IP-Adressen in Erwägungsgrund 30 als Online-Kennungen. Sie brauchen also einen Zweck und eine Rechtsgrundlage, häufig wird Art. 6 Abs. 1 Buchst. f geprüft. Technisch kürze ich Adressen oder protokolliere sie gar nicht. Die rechtliche Bewertung gehört zu Ihrer Datenschutzberatung.
Wie lange darf ein Log aufbewahrt werden?
Art. 5 Abs. 1 Buchst. e DSGVO verlangt, dass Personen nur so lange identifizierbar bleiben, wie es für den Zweck erforderlich ist. Eine feste Zahl von Tagen nennt die Verordnung nicht. Die Frist legen Sie fest, ich setze sie in der Rotation und im Backup um.
Reicht das Fehlerlog von PHP ohne Monolog?
Für ein kleines Skript ja: log_errors und error_log genügen. Bei einer Anwendung mit mehreren Teilen fehlen Stufen, Kontext, Kanäle und Maskierung. Dann setze ich auf PSR-3 und Monolog oder den Logger, den die Anwendung schon nutzt.
Hilft mir das Log, wenn ich dem HBDI einen Vorfall melden muss?
Ja, bei der Sachverhaltsschilderung. Der HBDI erwartet eine möglichst genaue Beschreibung von Art und Verlauf, ein Stichwort genügt nicht. Ein Log mit Zeitpunkt, Kanal und Kennung liefert dafür die Fakten. Die Meldung selbst verantworten Sie.
Schreiben Sie mir
Wenn Sie nach einem Fehler in Ihrer PHP-Anwendung zu wenig im Log finden oder zu viel, schicken Sie mir über die Kontaktseite ein paar Zeilen: die Adresse der Anwendung oder eine kurze Beschreibung, Ihr Ziel, eine Frist, falls es eine gibt, und den Stack mit PHP-Version und Framework. Meine Antwort kommt schriftlich: eine erste Einschätzung und die Fragen, die noch offen sind. Diese erste Einschätzung ist kostenlos.
Was ich sonst an PHP-Arbeit übernehme, steht auf der Seite PHP-Entwickler. Wie Ihr Shop Daten aufbewahrt und löscht, beschreibt die Seite zum WooCommerce-Entwickler in Wiesbaden.
Ausgewählte Projekte
Beispiele aus meiner Arbeit.
E-Commerce-Entwicklung: led-lumina.pl
led-lumina.pl ist ein professioneller Onlineshop, der ein umfassendes Angebot an innovativen LED-Lösungen für Privat- und Geschäftskunden präsentiert. Die Pl...
E-Commerce-Entwicklung: mochola.com
mochola.com ist eine moderne E-Commerce-Plattform, die auf WooCommerce basiert, das ich als Entwickler realisiert habe, und auf den Verkauf von Autoersatztei...
E-Commerce-Entwicklung: nehrebeccy.pl
nehrebeccy.pl ist eine moderne künstlerische Agentur, die Erfahrung in der Organisation kultureller Veranstaltungen mit der Präsentation eines vielfältigen k...
WordPress Support & Entwicklung in Wiesbaden
Brauchen Sie die Leistung: PHP-Entwickler in Wiesbaden?
Beschreiben Sie Ihr Projekt per E-Mail. Antwort innerhalb von 48 Stunden.
Projekt beschreibenWeitere WordPress-Dienste und Wissensbasis entdecken
Individuelle WordPress-Entwicklung und Architektur.
Shops, Checkout-Prozesse und Verkaufslogik.
Shopify-Themes, Liquid und WooCommerce → Shopify-Migrationen.
Headless WordPress, Sanity, Strapi und Contentful mit Astro oder Next.js.
Claude, OpenAI und RAG in WordPress mit BYOK und EU-Residenz.
Geschäftsprozessautomatisierung rund um WordPress und Firmensysteme.
Bibliothek konkreter Automatisierungsfälle.
NIS2- und DORA-Scoping, Lieferantenregister, Incident-Runbook.
Verwandte Kategorien
Unterstützende Artikel

Ein Leitfaden aus der Praxis für eine WordPress-Website, die 2026 rankt. Technisches SEO, Core Web Vitals, Schema, AEO, GEO, hreflang und die Reihenfolge, mit der es beim ersten Mal klappt.

Migration von Shopify zu WooCommerce ohne Datenverlust, Kundenverlust oder SEO-Ranking-Einbußen. Umfasst Produkttransfer, 301-Weiterleitungen, URL-Mapping, WP-CLI-Automatisierung und Checkliste nach der Migration.

Die vollständige Checkliste nach der WordPress-Installation. 50 Schritte zu Sicherheit, Performance, SEO und Backups. Eine professionelle Konfiguration ab dem ersten Tag.