PHP-Errorlog

Im deutschen WordPress-Forum bekommt man sehr häufig Berichte wie „Es funktioniert nicht, ich bekomme nur eine weiße Seite“ zu sehen. Erstaunlicherweise scheinen viele User nichts vom PHP-Errorlog zu wissen, weswegen ich dazu hier kurz ein paar Informationen aufschreiben will. Ich versuche, den Artikel so allgemein und verständlich wie möglich zu halten und ergänze gerne weitere Infos.

Was Server betrifft werde ich mich hier übrigens auf den weitverbreiteten Apache beschränken, da ich mit IIS und anderen Servern noch nicht gearbeitet habe. Ich ergänze aber gerne relevante Infos zu anderen Servern.

PHP-Fehler

Wie in jeder Programmiersprache kann man in PHP Fehler erzeugen, durch falsche Übergabewerte oder direkte Fehler im Code. Diese Fehler können am Bildschirm angezeigt, in eine Datei geschrieben oder einfach ignoriert werden. Normalerweise wird empfohlen, in lokalen Testsystemen die Fehler am Bildschirm ausgeben zu lassen (in der HTML-Seite), im Produktiv-System auf dem Server aber in eine Datei schreiben zu lassen, im weiteren Errorlog genannt. Funktioniert dann das PHP-Script nicht erwartungsgemäß oder wird gar nur eine weiße Seite statt des erwarteten Outputs angezeigt, empfiehlt es sich immer, zuerst mal im Errorlog nach Fehlermeldungen zu schauen.

PHP unterscheidet sich in einer Hinsicht übrigens von vielen anderen Programmiersprachen: Da es nicht compiliert wird, fallen Fehler erst bei der Ausführung des Codes auf. Alle Fehler, die bei einem Java-Programm z.B. einen Compile-Fehler erzeugen würden, den man sofort sieht und beheben kann, können sich im PHP-Code lange verstecken und erst bei der Ausführung des Scriptes auffallen (wenn man Pech hat, fallen Sie dem Besucher und nicht dem Besitzer der Seite auf). Um so wichtiger ist es, die Fehlermeldungen zu sehen, um unnötiges Rätselraten zu vermeiden.

Und zum Thema ‚weiße Seite‘: Wenn bei der Abarbeitung eines Scriptes ein Fehler auftritt, wird das Script abgebrochen. Wenn kein übergeordnetes Script den Fehler fängt und loggt oder verwirft, wird die komplette Seitengenerierung an dieser Stelle unterbrochen. Das äußert sich zum Beispiel darin, dass plötzlich die Sidebar auf der Seite fehlt, wenn darin ein Fehler auftrat. Wenn der Fehler allerdings passiert, noch bevor HTML-Code für die Seite generiert wurde, bekommt man ggf. nur eine leere Seite vom Browser angezeigt. Dabei schickt der Server übrigens nicht zwangsläufig einen HTTP-Statuscode 500 (Interner Server-Fehler) zurück. Zumindest in meinen Tests gab es trotz des Fehlers einen 200er-Code (Status: OK).

Aktuelle Serverkonfiguration herausfinden

Ok, nehmen wir an, etwa funktioniert nicht wie erwartet. Wenn man sich nicht sicher ist, sollte man zuerst einmal herausfinden, wie der Server in Bezug auf den Umgang mit PHP-Fehlern eingestellt ist. Speichert folgenden kurzen Code als Datei „test.php“, ladet sie auf den Server und ruft sie auf:

<?php
  echo 'Und jetzt der Fehler: ';
  echo gibtsnicht();
?>

Das sollte eine Fehlermeldung wie diese ergeben:

PHP Fatal error: Call to undefined function  gibtsnicht() in xxx\test.php on line 2

Wenn euch die Meldung am Bildschirm angezeigt wird, ist euer Server so eingestellt, dass PHP-Fehler direkt ausgegeben werden. Das ist für ein lokales System ok, denn es erspart einem während der Entwicklung ständig eine Logdatei kontrollieren zu müssen. Auf einem Produktivsystem ist das jedoch eher unerwünscht: Fehlermeldungen können zum einen sensitive Informationen enthalten, die nicht jeder Besucher der Seite sehen soll. Zum anderen sehen sie natürlich unprofessionell aus und zerstören ggf. das Layout der Seite.

Wenn ihr nach dem Aufruf obiger Datei nur die erste Zeile ausgegeben bekommt („Und jetzt der Fehler: “), aber nicht die Fehlermeldung, schreibt euer Server die Meldung entweder in eine Logdatei oder ignoriert sie. Die Chancen sind gut, dass ihr nach dem Aufruf also eine Logdatei auf dem Server zu liegen habt.

Wo diese Datei zu finden ist und ob ihr darauf überhaupt zugreifen könnt, hängt stark von eurem Provider ab. Es könnte sich aber lohnen, einfach mal in den oberen Ebenen eures Webspaces zu schauen. Bei meinem Provider liegt die Datei z.B. unter „/kunden/xxxxx/logs/phperror.log“. Dieser Pfad ist aus dem Internet heraus nicht aufrufbar (die Webseiten liegen unter „/kunden/xxxxx/websites/“), aber per FTP oder WebDAV kommt man an die Datei heran.

Wenn ihr es genau wissen wollt, erstellt euch eine zweite Datei und speichert das folgende als „info.php“ ab:

<?php
  phpinfo();
?>

Diese Datei auf den Server laden und aufrufen. Es werden verschiedenste Informationen über die PHP-Installation ausgegeben. Die Einstellungen zum Logging sollten in der Tabelle unter der Überschrift „PHP Core“ zu finden sein. Die Namen der Direktiven sind die gleichen, die weiter unten im Detail erklärt sind, interessant sind dabei vor allem „error_log“, „display_errors“ und „log_errors“. Hinter „error_log“ steht der Pfad der Logdatei. Die Direktive „display_errors“ sollte auf „Off“ gestellt sein (=Anzeige von Fehlermeldungen am Bildschirm), „log_errors“ dagegen auf „On“ (=Fehlermeldungen in eine Datei schreiben). Ist „log_errors“ auf „Off“ gestellt, werden Fehler nicht in eine Datei geschrieben.

phpinfo-Ausgabe

Ausschlaggebend ist hier übrigens der „Local Value“, falls sich „Local Value“ und „Master Value“ unterscheiden. Wenn euch beim Aufruf der info.php übrigens nichts angezeigt wird, dann ist in der PHP-Konfiguration dieser Befehl unterdrückt worden. Da könnt ihr dann erstmal auch nichts dran machen.

Steuern des Loggings

Wenn der Server nicht zufriedenstellend eingestellt ist, die Logdatei zum Beispiel nicht zu finden ist, sollte man sich etwas näher mit der Konfiguration des Errorloggings beschäftigen. Generell kann das an verschiedenen Stellen konfiguriert werden: In der php.ini-Datei, in .htaccess-Dateien, in der Apache-Konfiguration (httpd.conf) und sogar im PHP-Programm selber. Am üblichsten ist meines Wissens nach die Konfiguration in einer php.ini-Datei oder im PHP-Programm selbst. Auf .htaccess-Dateien und die Apache-Konfiguration gehe ich deswegen im folgenden nicht näher ein, auch mangels Erfahrungswerten. Bei den allermeisten Providern hat man diese Möglichkeit sowieso nur, wenn man sich einen komplett eigenen Server mietet. Ein paar Infos dazu gibt es aber im PHP-Manual: How to change configuration settings

Manche Anwendungen bringen eigene Error-Handler mit. Einstellungen dafür sind dann entweder direkt im Programm oder in Konfigurationsfiles dieses Programms zu tätigen. Eine grobe Übersicht, wie das funktionieren kann, bekommt man ebenfalls im PHP-Manual: Error Handling and Logging Functions. Wenn dies der Fall ist, sollte das allerdings in der Dokumentation des PHP-Programmes ausreichend beschrieben sein. Für WordPress, welches mir den Anlass zu diesem Artikel gab, trifft das jedoch nicht zu, hier ist die in der Regel die php.ini ausschlaggebend.

Die php.ini-Datei ist eine Textdatei, welche Konfigurationseinstellungen für PHP enthält, u.a. auch für den Umgang mit PHP-Fehlern. Die einzelnen Einstellungsmöglichkeiten einer php.ini-Datei erkläre ich gleich noch. Zuerst muss man sie jedoch finden. Hier gibt es zwei Möglichkeiten: Es gibt eine globale Konfigurationsdatei, und wenn es erlaubt ist kann man diese lokal überschreiben.

Die globale php.ini-Datei liegt in der PHP-Installation, in manchen Systemen aber auch beim Apache-Server. Lokal unter XAMPP für Windows ist die php.ini z.B. unter „xampp/apache/bin“ zu finden. Die Datei im PHP-Pfad kann man ignorieren, sie wird nicht benutzt. Auf dem Server ist dies abhängig von der PHP-Installation und damit vom Provider. Wenn man nicht seinen eigenen Server hat, wird man an der globalen Datei normalerweise nichts verändern können. Je nach Provider sollten aber lokale php.ini-Dateien erlaubt sein.

Für andere Provider kann ich dazu nichts sagen, aber bei domainFACTORY ist es zum Beispiel so: php.ini-Dateien wirken in unteren Tarifen nur lokal im aktuellen Verzeichnis (siehe Anmerkung 1), man muss seine php.ini also in jedes Verzeichnis kopieren (ggf. per Script) welches PHP-Dateien enthält. In höheren Tarifen kann man die globale Datei aus dem Admin-Menü heraus bearbeiten. Mehr Infos gibt es in den FAQ . Update Feb 2012: Schon eine ganze Weile steht der PHP.INI-Editor in allen Tarifen zur Verfügung und man kann sich den Firlefanz mit dem Kopieren der php.ini-Datei sparen.

phpinfo-Ausgabe

Um herauszufinden, wo sich die php.ini-Datei befindet, schaut man sich am besten wieder den Output des phpinfo-Befehls an (die info.php von eben). Gleich in der ersten Tabelle sollte es einen Eintrag „Loaded Configuration File“ geben, dahinter steht der Pfad der geladenen php.ini-Datei. Ruft man dies aus einem Ordner mit einer lokalen Datei heraus auf, steht deren Pfad dort, ansonsten der Pfad der globalen Konfigurationsdatei. Wenn hier nur „php.ini“ ohne einen Pfad steht, hat PHP keine Datei gefunden und arbeitet mit Default-Einstellungen.

Bevor wir uns die php.ini-Datei im Detail anschauen, noch ein Hinweis: Wird die globale Datei geändert, muss der Apache-Server neugestartet werden. Änderungen in lokalen Dateien wirken sich dagegen sofort aus.

neugestartet werden. Änderungen in lokalen Dateien wirken sich dagegen sofort aus.

Die php.ini-Datei

Öffnet die Datei mit einem Texteditor eurer Wahl und sucht euch die Sektion „Error handling and logging“ heraus. Das Semikolon am Zeilenanfang kennzeichnet dabei übrigens einen Kommentar. Solchermaßen gekennzeichnete Zeilen haben also keine Auswirkung. Wenn ihr euch nicht sicher seid, macht vor Änderungen an der ini-Datei am besten eine Kopie.

Ok, die Anweisungen dieses Abschnitts im einzelnen. Im PHP-Manual gibt es auch eine Übersicht der php.ini-Direktiven, wo man zu einigen Direktiven weitere Infos bekommt. Aber im Prinzip sind alle Direktiven in der Datei schon recht ausführlich kommentiert.

//error_reporting is a bit-field.  Or each number up to get
//desired error reporting level

error_reporting = E_ALL & ~E_NOTICE

Hier wird eingestellt, was geloggt werden soll, also Meldungen welcher Stufe. Nicht jeder Fehler ist gleich wichtig, deshalb gibt es verschiedene Stufen: E_ERROR, E_WARNING, E_NOTICE und eine Reihe weiterer. Der Default-Wert ist „E_ALL & ~E_NOTICE“ (=alle Meldungen außer solchen der Stufe E_NOTICE). Das ist für den normalen Nutzer eine sinnvolle Einstellung. Wer auch Meldungen der Stufen E_Notice und E_Strict sehen möchte (das sind Hinweise, wenn PHP-Funktionen nicht ganz standardgemäß verwendet werden), stellt hier „E_ALL | E_STRICT“ ein. PHP ist normalerweise recht fehlertolerant, so dass kleinere Unregelmäßigkeiten nicht zwangsläufig zum Abbruch des Scriptes führen. Aber auf einem lokalen Testsystem können auch diese Meldungen interessant sein.

//Print out errors (as a part of the output).  For production
//web sites, you're strongly encouraged to turn this feature
//off, and use error logging instead (see below).  Keeping
//display_errors enabled on a production web site may reveal
//security information to end users, such as file paths on your
//Web server, your database schema or other information.
display_errors = On

//Even when display_errors is on, errors that occur during PHP's
//startup sequence are not displayed.  It's strongly recommended
//to keep display_startup_errors off, except for when debugging.
display_startup_errors = Off

//Log errors into a log file (server-specific log, stderr, or
//error_log (below)). As stated above, you're strongly advised to
//use error logging in place of error displaying on production web
//sites.
log_errors = Off

Diese drei Einstellungen betreffen die Frage, ob und wie geloggt werden soll. Obige Werte sind die Standardeinstellungen von XAMPP und bedeuten: Fehler werden auf dem Bildschirm (im Browser) ausgegeben, Startfehler werden nicht ausgegeben und es werden keine Fehler in eine Datei geschrieben. Diese Einstellungen sind in Ordnung für das lokale Arbeiten mit einer Testinstanz: Passiert ein Fehler, wird er einfach im Browser ausgegeben. Für den Einsatz auf dem Server sind diese Einstellungen wie oben schon erläutert nicht geeignet: Hier sollten Fehler mittels Off/Off/On immer in eine Textdatei geschrieben und nicht im Browser ausgegeben werden!

//Set maximum length of log_errors. In error_log information about
//the source is added. The default is 1024 and 0 allows to not
//apply any maximum length at all.
log_errors_max_len = 1024

//Do not log repeated messages. Repeated errors must occur in same
//file on same line until ignore_repeated_source is set true.
ignore_repeated_errors = Off

//Ignore source of message when ignoring repeated messages. When
//this setting is On you will not log errors with repeated messages
//from different files or sourcelines.
ignore_repeated_source = Off

//If this parameter is set to Off, then memory leaks will not be shown
//(on stdout or in the log). This has only effect in a debug compile,
//and if error reporting includes E_WARNING in the allowed list
report_memleaks = On

//Store the last error/warning message in $php_errormsg (boolean).
track_errors = Off

Diese Einstellungen regeln verschiedene Dinge, man muss sie im Normalfall nicht anpassen. Die maximale Länge einzelner Meldungen sowie das Loggen sich wiederholender Meldungen haben vor allem Einfluss auf die Lesbarkeit des Logfiles in Extremfällen (z.B. wenn der gleiche Fehler alle paar Sekunden auftritt).

/&/Disable the inclusion of HTML tags in error messages.
/&/Note: Never use this feature for production boxes.
html_errors = Off
/*
If html_errors is set On PHP produces clickable error messages
that direct to a page describing the error or function causing
the error in detail. You can download a copy of the PHP manual
from http://www.php.net/docs.php and change docref_root to
the base URL of your local copy including the leading '/'. You
must also specify the file extension being used including the
dot. Note: Never use this feature for production boxes.
docref_root = "/phpmanual/"
docref_ext = .html

String to output before an error message.
error_prepend_string = "<font color=ff0000>"

String to output after an error message.
error_append_string = "</font>"
*/

Diese Einstellungen regeln die Ausgabe von Fehlermeldungen auf dem Bildschirm. Hat man dies bei „display_errors“ ausgestellt, kann man sie wie hier gezeigt einfach auskommentiert lassen. Auf seiner lokalen Testinstanz kann man damit erreichen, dass z.B. Fehlermeldungen zur entsprechenden Seite des PHP-Manuals verlinken etc. Im Normalfall kann man sie jedoch ignorieren.

//Log errors to specified file.
//error_log = "\xampp\apache\logs\phperror.log"
//Log errors to syslog (Event Log on NT, not valid in Windows 95).
error_log = syslog

Und hier die wichtige Einstellung, wohin geloggt werden soll. Das wirkt sich natürlich nur aus, wenn oben „log_errors“ auf „On“ gestellt wurde. Dann kann man hier den Pfad zu einer Datei eintragen oder eben mit „syslog“ die Meldungen ins Systemlog schreiben lassen (was eher nicht so gut ist). Achtung: Beide Anweisungen stellen natürlich das gleiche dar und beide sind auskommentiert. Einfach bei einer davon das Semikolon entfernen und dann den Wert entsprechend anpassen, z.B. zu „C:\xampp\apache\logs\phperror.log“. Ihr könnt euch das Logfile aber auch dorthin legen, wo ihr es einfacher und ohne viel Geklicke wiederfindet, z.B. „D:\webapps\phperror.log“. Auf dem Server sollte hier ein Pfad eingetragen sein, der oberhalb eurer Webroots liegt, also aus dem Internet heraus nicht aufrufbar ist, z.B. „/kunden/xxxxx/logs/phperror.log“ (wenn eure Webseiten unter „/kunden/xxxxx/websites/“ liegen).

Wenn das Logging in eine Datei eingeschaltet aber keine Datei definiert ist, landen die Meldungen übrigens mit einiger Wahrscheinlichkeit im Apache-Errorlog. Je nach eurer Serverkonfiguration findet ihr sie also vielleicht auch dort wieder. Da die Meldungen nicht ganz dem Apache-Format entsprechen, kann es Probleme geben, wenn ihr das Apachelog mit einem Tool auswerten wollt. Die PHP-Meldungen sind also besser in einer eigenen Datei aufgehoben, allein schon der Übersichtlichkeit wegen.

Logging direkt aus PHP heraus

Wenn euer Provider euch die Steuerung per php.ini nicht erlaubt, könnt ihr es direkt mit PHP-Befehlen probieren. Dies müsstet ihr möglichst am Anfang aller Seiten einbinden, z.B. in eine gemeinsame Header-Datei. Mittels des Befehls ini_set könnt ihr Einstellungen für den Aufruf des aktuellen Scripts anpassen, z.B. so:

error_reporting(E_ALL);
ini_set('display_errors', 'Off');
ini_set('error_log', '/kunden/xxxxx/logs/phperror.log');

Die Namen der Direktiven entsprechen dabei wieder den eben diskutierten Einstellungen. Im Prinzip überschreibt ihr damit für die Dauer des aktuell ausgeführten Skriptes den Wert der Einstellung, der in der php.ini-Datei definiert ist.

Fehlermeldungen verstehen

Glückwunsch, euer System ist nun gemäß euren Wünschen eingestellt, was das Loggen von PHP-Fehlern betrifft. Und nun? Die geloggten Fehler zu verstehen, ist natürlich noch mal eine andere Sache, da die Meldungen generell Englisch und eher technisch gehalten sind. Aber das Loggen der genauen Fehlermeldung ist schon mal der halbe Schritt: Mit der Fehlermeldung könnt ihr z.B. per Google weitere Infos finden. Und wenn ihr in Foren um Hilfe bittet, erhöht es eure Chancen auf eine hilfreiche Antwort ungemein, die Fehlermeldung zu posten (dabei eventuelle sensitive Infos wie z.B. Passwörter natürlich maskieren).

Eigene Meldungen loggen

Wenn euer Logging an sich richtig eingestellt ist, könnt ihr auch beliebige Meldungen loggen. Es passiert kein Fehler, aber trotzdem funktioniert ein Programm nicht so wie es soll? Dann könnt ihr Variablen-Werte oder eigene Meldungen mittels der PHP-Funktion error_log ins Errorlog ausgeben. Das ist eine sehr simple Form des Debuggings. Wenn es haariger wird oder ihr höhere Ansprüche habt, dann gibt es da auch noch wesentlich mächtigere Debugging-Werkzeuge. Aber für einfache Fälle reicht error_log aus.

Die Beschreibung der Funktion aus dem PHP-Manual:

bool error_log(string $message[, int $message_type[, string $destination[, string $extra_headers]]]);

Auf jeden Fall mitgeben müsst ihr der Funktion die Meldung, die geloggt werden soll. Mit den optionalen Parametern kann das Verhalten der Funktion noch weiter gesteuert werden, aber normalerweise reicht ein Aufruf wie

error_log('Wert der Variablen: ' . $variable);

um mal schnell den Wert einer Variable zu bekommen oder zu schauen, ob eine if-Bedingung true ist etc.

Fazit

Ich hoffe, der Artikel hat euch an das Thema PHP-Errorlog etwas herangeführt und ich habe alle Zusammenhänge hier korrekt und verständlich dargestellt. Sorry wenn es etwas zu ausführlich geworden ist. ????

Anmerkungen

Anmerkung 1: Können lokale php.ini-Dateien auch für untergeordnete Verzeichnis gelten, ähnlich wie das bei .htaccess-Dateien der Fall ist? Kann ich also in den Ordner „A“ eine lokale php.ini-Datei legen und diese gilt auch für PHP-Scripte im Unterordner „A/B“? Bei meinem Provider ist das nicht so, aber es könnte generell natürlich sein, dass es geht und hier nur per Konfiguration verboten ist. Das kann ich leider im Moment nicht sagen, würde den Artikel aber gerne mit Erfahrungswerten anderer entsprechend anpassen. ????

Anmerkung 2: Nicht vergessen, die Testdateien test.php und info.php ggf. wieder vom Server zu löschen!


Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0