15.2 Einbindung in die Applikation 

In der Applikation sollen in diesem Kapitel alle wesentlichen Ereignisse geloggt werden. Dazu gehört primär eine Log-Datei mit denjenigen Fehlern, die während der Ausführung auftreten. Zusätzlich sollen alle relevanten Aktionen wie neu eingestellte Inhalte aller Art sowie erfolgreiche oder gescheiterte Authentifizierungsversuche festgehalten werden. Als Vorbereitung für das folgende Kapitel 16, »Diagramme«, sollen die Zugriffe der Besucher festgehalten werden. Hierbei interessieren Sprache, Betriebssystem und der verwendete Browser. Anschließend sollen diese Ergebnisse graphisch ausgewertet werden.
Die Access-Logs für das folgende Kapitel sollen in eine Datenbank geschrieben werden, um eine einfache SQL-basierte Auswertung zu ermöglichen. Aufgrund eines eigenen, nicht normalisierten Formats in der Datenbank wird dafür ein eigener Log-Writer geschrieben, der die notwendigen zusätzlichen Daten speichert, um diese auf einfache Weise auszuwerten.
In der Beispielapplikation wählen wir dieses Vorgehen, weil es uns ermöglicht, einfach und effektiv Grafiken im nächsten Kapitel zu erstellen. Die Auswertung der Access-Logs des Webservers über kompilierte Programme ist erheblich effektiver und sollte definitiv der Weg der eigenen Wahl sein.
15.2.1 Fehler protokollieren 

Damit Sie wissen, wann ein Besucher einen Fehler verursacht hat, sollten diese zuerst protokolliert werden. Das gestaltet sich sehr einfach, da durch die Struktur der Applikation im Falle eines Fehlers immer ein entsprechendes Signal ausgelöst und zentral behandelt wird. Dieser Mechanismus wurde in Kapitel 3, »Die Applikationsbasis«, bereits im Detail vorgestellt.
Zuerst muss der Log-Handler mit den angesprochenen Filterregeln initialisiert werden, was wie gewohnt in der Methode gpBlogController::init() in der Datei classes/controller.php stattfindet, in der auch die anderen Komponenten initialisiert werden.
// Initialize eventlog mapper
$log = ezcLog::getInstance();
$mapper = $log->getMapper();
// Add error log
$fileWriter = new ezcLogUnixFileWriter(
dirname( __FILE__ ) . '/../log', 'error.log'
);
$filter = new ezcLogFilter();
$filter->severity = ezcLog::WARNING | ezcLog::ERROR | ezcLog::FATAL;
$rule = new ezcLogFilterRule( $filter, $fileWriter, true );
$mapper->appendRule( $rule );Listing 15.1 EventLog initialisieren
Die Instanz von ezcLog enthält eine Instanz der Klasse ezcLogMapper, die die Abbildungen von Log-Nachrichten über einen Satz von Regeln auf die Log-Backends verwaltet. Zu dem Objekt in der Variablen $mapper werden die Regeln der Klasse ezcLogFilterRule hinzugefügt.
In der sechsten Zeile wird mit ezcLogUnixFileWriter ein Log-Backend initialisiert, das als Pfad zu den Log-Dateien das Verzeichnis log/ im Wurzelverzeichnis der Applikation erhält und seine Log-Nachrichten in die Datei error.log schreiben soll. Das Objekt der Klasse ezcLogFilter bestimmt die Regeln, welche Log-Nachrichten verarbeitet werden sollen. In diesem Beispiel werden die zu verarbeitenden Nachrichten auf solche eingeschränkt, deren Dringlichkeit eine Warnung, ein Fehler oder ein fataler Fehler ist. Mit dem Filter $filter und dem Backend $fileWriter wird eine neue Log-Regel erstellt, die abschließend zu $mapper hinzugefügt wird.
Der dritte Parameter im Konstruktor der ezcLogFilterRule definiert, ob nach dem erfolgreichen Verarbeiten dieser Regeln noch weitere Regeln verarbeitet werden sollen. Da im Verlauf dieses Kapitels noch weitere Log-Handler hinzugefügt werden, die zum Teil auch die Fehlernachrichten erhalten sollen, wird dies gesetzt.
Ausgelöst werden die Log-Nachrichten mit Fehlermeldungen nur in der Datei classes/actions/error.php, falls eine der Fehleraktionen auftritt.
ezcLog::getInstance()->log( $message, ezcLog::ERROR );
Die Nachricht selbst wird als Parameter $message der Methode übergeben. Die Dringlichkeit orientiert sich an der aufgerufenen Methode und entspricht einer der folgenden dafür vorbelegten Konstanten.
|
ezcLog::DEBUG |
Debug-Informationen |
|
ezcLog::SUCCESS_AUDIT |
Prüfungserfolg |
|
ezcLog::FAILED_AUDIT |
fehlgeschlagene Prüfung |
|
ezcLog::INFO |
Information |
|
ezcLog::NOTICE |
Notiz |
|
ezcLog::WARNING |
Warnung |
|
ezcLog::ERROR |
Fehler |
|
ezcLog::FATAL |
fataler Fehler |
Falls ein solches Signal in der Applikation ausgelöst wird, wird die Nachricht entsprechend in der Datei log/error.log angehängt. Bei dem Scheitern der Validierung einer E-Mail-Adresse steht im Log anschließend die folgende Nachricht:
Jul 25 11:06:59 [Error] [default] [default] Invalid data for input field email, please go back and correct!
15.2.2 Weitere Nachrichten loggen 

Neben den Fehlern, die in einer Applikation auftreten und protokolliert werden müssen, gibt es auch andere Aktionen, die relevant sein können, um Prozesse nachzuvollziehen. All diese Nachrichten sollen in einer eigenen Datei log/action.log auftauchen. Dazu können Sie einen Log-Handler definieren, in dem alle Nachrichten landen, falls nicht eine andere Regel zuvor deren ausschließliche Behandlung erfordert hat.
// Add default rule
$fileWriter = new ezcLogUnixFileWriter(
dirname( __FILE__ ) . '/../log', 'action.log'
);
$filter = new ezcLogFilter();
$rule = new ezcLogFilterRule( $filter, $fileWriter, true );
$mapper->appendRule( $rule );Listing 15.2 Alle Aktionen loggen
Diese Regel definiert keinerlei Anforderungen an den Filter, sodass alle Nachrichten akzeptiert und in die Datei action.log geschrieben werden. Eine solche Regel sollten Sie sinnvollerweise zuletzt definieren, damit Nachrichten an den Log-Handler, die eine ausschließliche Behandlung erfordern, sonst ebenfalls in dieser Log-Datei landen. Damit lassen sich nun auch an anderen Stellen der Applikation prozessrelevante Ereignisse protokollieren, wie zum Beispiel erfolgreiche und fehlgeschlagene Authentifizierungsversuche aus Kapitel 11, »Authentifizierung«, mittels der vorgestellten Authentication-Komponente. Dazu wird an den entsprechenden Stellen in der Datei classes/actions/comment.php, an denen die Authentifizierung für die Verfasser von Kommentaren stattfindet, eine entsprechende Nachricht geloggt.
ezcLog::getInstance()->log(
"Failed authenticating OpenID user '{$_SESSION['openid']}'",
ezcLog::FAILED_AUDIT
);Listing 15.3 Authentifizierung loggen
Entsprechende Nachrichten werden auch für erfolgreiche Authentifizierungsversuche und das Scheitern der anderen Backends eingefügt. Mit ihrer Hilfe lassen sich später in den Log-Dateien beispielsweise Brute-Force-Attacken feststellen. Eine erfolgreiche Authentifizierung zeigt sich in der Log-Datei dann folgendermaßen:
Jul 25 11:11:33 [Success audit] [default] [default] Suceessfullyð authenticated OpenID user 'http://xlogon.net/koredn'
Genauso werden das Anlegen von Kommentaren sowie das Anlegen, Löschen und Ändern von Blog-Einträgen mit der Dringlichkeit ezcLog::INFO in der Log-Datei festgehalten. Das erfolgreiche Anlegen eines Blog-Kommentars lässt sich damit im Log nachvollziehen.
Jul 25 15:32:32 [Success audit] [default] [default] Suceessfullyð authenticated OpenID user 'http://xlogon.net/koredn' Jul 25 15:32:32 [Info] [default] [default] Created blog comment
ð with id 18.
15.2.3 Zusätzliche Daten loggen 

Wenn Log-Nachrichten gespeichert werden, ist es häufig wichtig, in welchem Zusammenhang der Fehler oder die Aktion auftrat. Dazu kann es sinnvoll sein, den verursachenden Benutzer im Log identifizieren zu können oder Statusparameter der Applikation selbst anzuhängen. Im Blog sind keine Informationen über den Besucher, ausgenommen seine IP-Adresse, bekannt. Doch genau diese ist für auftretende Fehler erforderlich, um zu überprüfen, ob verschiedene gescheiterte Authentifizierungsversuche und andere Attacken stets vom gleichen Rechner beziehungsweise gleichem Netzwerk ausgehen.
Es gibt zwei Möglichkeiten, einer Log-Nachricht mehr Informationen anzuhängen: Beim Auslösen der Nachricht oder generell für Klassen von Nachrichten. Entsprechend soll allen relevanten Nachrichten die IP-Adresse desjenigen Besuchers, der die Meldung ausgelöst hat, angehängt werden.
// Automatically append IP data to sever errors
$log->setSeverityAttributes(
ezcLog::ERROR | ezcLog::FATAL | ezcLog::FAILED_AUDIT,
array(
'IP' => $_SERVER['REMOTE_ADDR'],
)
);Listing 15.4 Daten an Log-Nachrichten anhängen
Dieser Codeabschnitt wird bei der Initialisierung der EventLog-Komponente in der Methode gpBlogController::init() eingefügt. Sofern die Dringlichkeit der Nachricht einer der angegebenen Stufen entspricht, wird der zusätzliche Wert – in diesem Fall ist es die IP-Adresse des Besuchers – angehängt. Der ezcLogUnixFileWriter verwendet diese zusätzlichen Informationen und hängt sie an die geloggten Nachrichten an.
Jul 25 15:47:17 [Error] [default] [default] Invalid data for input field E-Mail, please go back and correct! (IP: 127.0.0.1)
15.2.4 Kategorien und Quelle 

Neben der Dringlichkeit von Log-Nachrichten gibt es noch eine weitere vordefinierte Filtermöglichkeit in der EventLog-Komponente. Beim Auslösen einer Log-Nachricht können dieser optional beide mitgegeben werden. Dieser Mechanismus wird im Blog verwendet, um die Access-Logs von den anderen Nachrichten zu trennen.
// Initialize eventlog mapper
$log = ezcLog::getInstance();
$mapper = $log->getMapper();
// Automatically append user stuff to log messages from source
// Access
$log->setSourceAttributes(
array( 'Access' ),
array(
'UserAgent' => $_SERVER['HTTP_USER_AGENT'],
'IP' => $_SERVER['REMOTE_ADDR'],
'language' => $_SERVER['HTTP_ACCEPT_LANGUAGE'],
)
);
// Add logging of access stats
$fileWriter = new ezcLogUnixFileWriter(
dirname( __FILE__ ) . '/../log', 'access.log'
);
$filter = new ezcLogFilter();
$filter->source = array( 'Access' );
$rule = new ezcLogFilterRule( $filter, $fileWriter, false );
$mapper->appendRule( $rule );Listing 15.5 Access-Log-Regeln initialisieren
Die Quelle wird in der siebten Zeile des Beispiels verwendet, um ebenfalls automatisch relevante Kontextinformationen anzuhängen. Um Access-Logs zu erstellen, interessiert nicht nur die IP, sondern auch der Browser und die Sprache des Besuchers. Der Filter greift sich alle Nachrichten aus der Kategorie, und der dritte Parameter beim Erstellen der Regel bestimmt, dass diese Nachrichten nicht von anderen Filtern weiterverarbeitet werden. Die vollständigen Zugriffsstatistiken würden in den anderen Log-Dateien nur irritieren. Uns interessieren besonders die Zugriffe auf einzelne Blog-Einträge und Mediendateien, weswegen diese Informationen über den zweiten genannten Weg direkt mit an die Log-Nachrichten beim Auslösen angehängt werden.
ezcLog::getInstance()->log(
"Display blog entry '{$id}'.",
ezcLog::INFO,
array(
'source' => 'Access',
'type' => 'blog',
'data' => $id,
)
);Listing 15.6 Zugriff auf Blog-Eintrag loggen
In den ersten beiden Zeilen steht eine übliche Log-Nachricht, wie sie auch bisher bekannt war. Diese Nachricht wird um optionale lokale Parameter erweitert, die im Array als dritter Parameter der Funktion ezcLog::log() übergeben werden. Hierbei gibt source die Quelle an, auf deren Basis im vorherigen Beispiel das Log-Handling definiert wurde. Für die spätere Auswertung wird die Nachricht um den Typ und optional ein Datenfeld erweitert, auf deren Basis später die Statistiken erstellt werden. Für das Betrachten einer Mediendatei wird der Code entsprechend angepasst.
ezcLog::getInstance()->log(
"Display image '{$file}'.",
ezcLog::INFO,
array(
'source' => 'Access',
'type' => 'image',
'data' => $file,
)
);Listing 15.7 Zugriff auf eine Mediendatei loggen
In diesem Fall wird als Daten die betrachtete Datei gespeichert. Die Log-Dateien enthalten diese Informationen, sodass der Eintrag für das Besuchen des ersten Blog-Eintrags wie folgt ausschaut:
Jul 25 16:24:02 [Info] [Access] [default] Display blog entry '1'. (UserAgent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.5) Gecko/20070719 Firefox/2.0.0.5, IP: 127.0.0.1, language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3, type: blog, data: 1)
Auch wenn alle benötigten Informationen enthalten sind, ist diese Zeichenkette aufgrund der vielen Informationen weder leicht zu lesen noch effizient automatisiert zu verarbeiten, um daraus Statistiken zu erstellen. Aus diesem Grund wird im nächsten Abschnitt ein eigener, angepasster Log-Writer vorgestellt, der diese Daten aufbereitet in einer Datenbank speichert.
15.2.5 Eigener Log-Writer 

Der eigene Log-Writer soll die Auswertung der Access-Logs, die über die EventLog-Komponente für die graphische Auswertung in Kapitel 16, »Diagramme«, erstellt werden, erleichtern, indem die aggregierten Daten aufbereitet in die Datenbank geschrieben werden. Hierdurch sollen sich später auf einfache Weise Statistiken aggregieren lassen. Ein eigener Log-Writer muss das Interface ezcLogWriter mit der Methode writeLogMessage() implementieren, die die Nachrichten und alle Kontextinformationen erhält. Die in der Datei classes/logger.php neu definierte Klasse gpBlogAccessLogWriter implementiert ausschließlich diese Methode und kann nach dem Hinzufügen in der autoload-Datei verwendet werden. Dies wurde bereits in Abschnitt 2.2.3, »Eigenes Autoload einbinden«, beschrieben.
public function writeLogMessage(
$message, $severity, $source, $category, $optional = array()
) {
$browser = get_browser( $optional['UserAgent'] );
preg_match( '(^\W*(\w+))', $optional['language'], $match );
$primaryLanguage = strtolower( $match[1] );
$db = ezcDbInstance::get();
$query = $db->createInsertQuery();
$query->insertInto( 'access' )
->set( 'date', time() )
->set( 'browser',
$query->bindValue( $browser->browser ) )
->set( 'os',
$query->bindValue( $browser->platform ) )
->set( 'language',
$query->bindValue( $primaryLanguage ) )
->set( 'type',
$query->bindValue( $optional['type'] ) )
->set( 'data',
$query->bindValue( $optional['data'] ) );
$statement = $query->prepare();
$statement->execute();
}Listing 15.8 Log-Nachricht in der Datenbank speichern
Aus der User-Agent-Zeichenkette wird mittels der PHP-Funktion get_browser() geparst. Damit werden Informationen über den Browser und das Betriebssystem des Besuchers extrahiert. Damit diese Funktion funktioniert, muss eine browscap.ini-Datei in der Datei php.ini referenziert werden. Diese liegt im docs/-Verzeichnis, sodass folgender Eintrag in der Datei php.ini ausreicht.
browscap = /path/to/stage14/docs/browscap.ini
Nach der Modifikation der Datei php.ini liefert die Funktion alle für das Loggen relevanten Informationen als Objekt, aus dem in der Query die Eigenschaften browser und platform (für das Betriebssystem) verwendet werden.
Um die Sprache des Besuchers auszulesen, wird die vom Browser übermittelte bevorzugte Sprache des Besuchers im HTTP-Header HTTP_ACCEPT_LANGUAGE durch einen einfachen regulären Ausdruck ausgewertet, der die erste Buchstabenfolge als primäre Sprache speichert. Bibliotheken wie PEAR::HTTP stellen Methoden zur Verfügung, um dies effektiv auszulesen. Wir beschränken uns hier jedoch auf einfaches Pattern.
In die Datenbank werden die Informationen über die in Kapitel 6, »Datenbankanbindung«, vorgestellte Database-Komponente eingefügt. Die Datenbanktabelle enthält damit Informationen über den Zeitpunkt der Anfrage, den Browser, das Betriebssystem und die Sprache des Besuchers sowie den Typ und optional weitere Informationen zur besuchten Seite.
Um das eigene Log-Backend zu aktivieren, müssen Sie nun noch das bei der Initialisierung der Log-Abbildungen in der Datei classes/controller.php verwendete Log-Backend ersetzen. Dazu wird in der Methode init() die Klasse ezcLogUnixFileWriter durch die soeben erstellte gpBlogAccessLogWriter-Klasse ersetzt werden.
// Add logging of access stats
$accessLog = new gpBlogAccessLogWriter();
$filter = new ezcLogFilter();
$filter->source = array( 'Access' );
$rule = new ezcLogFilterRule( $filter, $accessLog, false );
$mapper->appendRule( $rule );Listing 15.9 Eigenen Log-Writer verwenden
Der Abschnitt ändert sich nicht bis auf das verwendete Backend. Bei Zugriffen auf die Blog-Einträge werden nun die Zugriffe in der Datenbank protokolliert.
+------------+---------+-------+----------+---------+------+ | date | browser | os | language | type | data | +------------+---------+-------+----------+---------+------+ | 1185357432 | Firefox | Linux | de | gallery | NULL | | 1185357464 | Firefox | Linux | de | blog | 1 | ... | 1185370235 | Firefox | Linux | de | blog | 2 | | 1185370354 | Firefox | Linux | de | blog | 1 | +------------+---------+-------+----------+---------+------+
Listing 15.10 Auszug aus der Datenbank
Mit diesen Daten sollten sich die Statistiken später leicht erstellen lassen.




Ihre Meinung






