Rsyslog mit MySQL als zentraler Logserver

Diese Anleitung bezieht sich auf Debian 6 (Squeeze). Die in Debian Lenny enthaltene Version von Rsyslog ist für unten stehende Verwendeung zu alt.

System vorbereitet

Wenn man viele Daten per Syslog in der Datenbank speichern will, sollte man schon bei der Installation des Betriebssystems darauf achten, dass Filesystem optimal eingestellt ist.

XFS tunen

  • Partition für MySQL formatieren
mkfs.xfs -l internal,size=64m -d agcount=45 /dev/sdX
  • Mountoptionen für die MySQL Partition
/dev/sdX /var/lib/mysql xfs noatime,nodiratime,nobarrier,logbufs=8,allocsize=128m 0 2
XFS wird mit einem flüchtigen Writecache verwendet. Im Falle eines unkontrollierten Serverabsturzes, zum Beispiel durch Stromverlust, können Daten, die noch nicht auf die physikalische Festplatte geschrieben wurden, verloren gehen!

Benötigte Pakete installieren

aptitude install rsyslog rsyslog-mysql mysql-server lighttpd php5-cgi php5-mysql php5-gd

Datenbanken und Tabellen anlegen

MySQL Einstellungen anpassen

Innodb Plugin nutzen

rsyslog sollte für alle Tabellen die Tabellenengine Innodb nutzen. Seit der Version 5.1.38 können Sie zwei verschiedene "Arten" von Innodb nutzen:

  • Standard Innodb
  • Innodb Plugin

Mehr erfahren Sie unter http://dev.mysql.com/doc/refman/5.1/en/innodb.html

Das Innodb Plugin bietet verschieden Optimierungen, die Sie nutzen sollten. Tragen Sie in der my.cnf folgendes ein, um das Plugin zu Verwenden

ignore-builtin-innodb
plugin-load               = innodb=ha_innodb_plugin.so
innodb_file_per_table     = 1
innodb_file_format        = barracuda
innodb_buffer_pool_size   = 2048M  #(So viel wie möglich, nicht mehr als 80% des verfügbaren RAMs)
innodb_thread_concurrency = 8      # Set Cores*2

In vielen Fällen können Sie Abstriche bei der Transaktionsicherheit zugunsten von Performance machen. Die häufigsten Inserts in die Datenbank sind die neu eingetroffenen Messwerte der Items. Wenn solche Inserts schon bestätigt werden, wenn Sie in den RAM und noch nicht auf der Festplatte geschrieben wurden, dann ist das in vielen Fällen vertretbar, bringt aber eine höhere Schreibgeschwindigkeit.

Lesen Sie Details unter http://dev.mysql.com/doc/refman/5.1/en/innodb-parameters.html nach und fügen Sie ggf. in die my.cnf folgendes ein:

innodb_doublewrite             = 0
innodb_flush_log_at_trx_commit = 0
innodb_support_xa              = No

Datenbank User anlegen

mysql -e "CREATE DATABASE Syslog"
mysql -e "GRANT INSERT ON Syslog.* TO rsyslog_writer IDENTIFIED BY 'password'"
mysql -e "GRANT SELECT ON Syslog.* TO rsyslog_reader IDENTIFIED BY 'password'"
mysql -e "GRANT ALL ON Syslog.* TO rsyslog_master IDENTIFIED BY 'password'"

Sie können auch die menügeführte Installation von Debian verwenden. Dann wird die Tabelle aber mit dem Tabellentreiber MyISAM angelegt. Je nach Verwednungszweck und Menge an Daten, die in die Tabelle(n) geschrieben werden, ist MyISAM auch ein gute Wahl. Diese Anleitung optimiert rsyslog bzw. MySQL so, dass massenhaft Daten geschrieben werden.

MySQL Modul von rsyslog aktivieren

Wenn Sie rsyslog-mysql per Paketmanager installieren, sollte das Modul auch sofort aktiviert sein. Kontrollieren Sie, ob im Verzeichnis /etc/rsyslog.d/ die Datei mysql.conf liegt.

In dieser Datei werden die Zugangsdaten zu Datenbank gespeichert.

# /etc/rsyslog.d/mysql.conf
# Enter database settings in the following order
# database-server,database-name,database-userid,database-password
$ModLoad ommysql
*.* :ommysql:localhost,Syslog,rsyslog,rsyslog

Oben genannter Eintrag bedeutet, dass alle Logeinträge in der Datenabank Syslog in der Tabelle SystemEvents gespeichert werden. An dieser Stelle kann man den Namen der Tabelle nicht angeben. Soll eine andere Tabelle als SystemEvents verwendet werden, muss man ein angepasstes Logtemplate erstellen.

Eine Standardtabelle anlegen

Wenn Sie dem Debian-Installer erlaubt haben, rsyslog-mysql einzurichten, dann sollten Sie unten stehende Tabelle schon in Ihrer Datenbank vorfinden.

Wenn Sie InnoDB und komprimierte Tabellen nutzen möchten, können Sie die vorhandene Tabelle anpassen.

mysql Syslog -e "ALTER TABLE SystemEvents ENGINE=innodb DEFAULT CHARSET=latin1 row_format=COMPRESSED KEY_BLOCK_SIZE=4"

Mit nachfolgender Tabelle wird ein Tabelle angelegt, die rsyslog ohne Anpassungen zum Speichern von Logeinträgen verwenden kann. Sie können auch ein eigenes Tabellenschema entwerfen, welches Sie dann aber auch rsyslog über ein Tabellentemplate bekannt geben müssen.

CREATE TABLE `SystemEvents` (
  `ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `CustomerID` bigint(20) DEFAULT NULL,
  `ReceivedAt` datetime DEFAULT NULL,
  `DeviceReportedTime` datetime DEFAULT NULL,
  `Facility` smallint(6) DEFAULT NULL,
  `Priority` smallint(6) DEFAULT NULL,
  `FromHost` varchar(60) DEFAULT NULL,
  `Message` text,
  `NTSeverity` int(11) DEFAULT NULL,
  `Importance` int(11) DEFAULT NULL,
  `EventSource` varchar(60) DEFAULT NULL,
  `EventUser` varchar(60) DEFAULT NULL,
  `EventCategory` int(11) DEFAULT NULL,
  `EventID` int(11) DEFAULT NULL,
  `EventBinaryData` text,
  `MaxAvailable` int(11) DEFAULT NULL,
  `CurrUsage` int(11) DEFAULT NULL,
  `MinUsage` int(11) DEFAULT NULL,
  `MaxUsage` int(11) DEFAULT NULL,
  `InfoUnitID` int(11) DEFAULT NULL,
  `SysLogTag` varchar(60) DEFAULT NULL,
  `EventLogType` varchar(60) DEFAULT NULL,
  `GenericFileName` varchar(60) DEFAULT NULL,
  `SystemID` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=innodb DEFAULT CHARSET=latin1 row_format=COMPRESSED KEY_BLOCK_SIZE=4

Ja nach dem, wie Sie später Ihre Syslog Tabellen durchsuchen wollen, ist es ratsam Indizes zu setzten, damit das Suchen schneller geht. Z.B:

mysql> alter table SystemEvents add index SyslogTag(SyslogTag);
mysql> alter table SystemEvents add index FromHost(FromHost);

Testen

Erzeugen Sie einen Logeintrag

logger foo

Testen Sie, ob etwas in der Datenbank ankommt

mysql> select ReceivedAt,Message from SystemEvents;
+---------------------+---------+
| ReceivedAt          | Message |
+---------------------+---------+
| 2010-11-03 16:15:06 |  foo    |
+---------------------+---------+
1 row in set (0.00 sec)

Eine benutzerdefinierte Tabelle anlegen

Im folgenden Beispiel wird eine benutzerdefinierte Tabelle für alle php Meldungen angelegt.

CREATE TABLE `php_log` (
  `from_host` varchar(100) NOT NULL,
  `priority` int(2) NOT NULL,
  `message` text NOT NULL,
  `device_reported_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `received_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `syslog_tag` varchar(20) NOT NULL,
  KEY `from_host` (`from_host`),
  KEY `syslog_tag` (`syslog_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

Rsyslog verwendet wenn kein Template definiert ist, immer die Tabelle Systemevents. Damit eine benutzerdefinierte Tabelle verwendet wird, muss also jetzt ein Template in der Datei /etc/rsyslog.conf oder einer beliebigen Datei im Verzeichnis /etc/rsyslog.d angelegt werden, welche wie folgt aussehen könnte:

$template php_log,"insert into php_log (from_host, priority, message, device_reported_time, received_at, syslog_tag ) values ('%HOSTNAME%', \
'%syslogpriority%', \
'%msg%', \
'%timereported:::date-mysql%', \
'%timegenerated:::date-mysql%', \
'%syslogtag%')",SQL

Damit dieses Template verwendet wird, geben Sie in der rsyslog-Konfiguration folgende Bedingung an.

:syslogtag, startswith, "php" :ommysql:localhost,Syslog,rsyslog_writer,syslog;php_log  ~

Achten Sie auf die Position, an der die Bedingung eingefügt wird. Durch das ~ am Ende, werden nach dieser Bedingung keine weiteren Regeln ausgewertet.

Das grafische Webfrontend Loganalyzer installieren

PHP für Lighttpd aktivieren

Nach der Installation von Lighhtpd und PHP ist PHP im Webser noch nicht aktiviert.

Fügen Sie in die Datei /etc/lighttpd/conf-available/15-fastcgi-php.conf folgende Zeile ein:

server.modules   += ( "mod_fastcgi" )

und aktivieren Sie die Konfiguration durch Setzen eines symbolischen Links.

cd /etc/lighttpd/conf-enabled/
ln -s ../conf-available/10-fastcgi.conf .
/etc/init.d/lighttpd restart
echo '<?php phpinfo(); ?>'>/var/www/info.php

Nun sollten Sie im Browser die PHP-Info-Seite sehen können.

Loganalyzer Downloaden

cd /tmp
wget http://download.adiscon.com/loganalyzer/loganalyzer-3.0.0.tar.gz
tar xzf loganalyzer-3.0.0.tar.gz
/tmp/loganalyzer-3.0.0/src
cp -r * /var/www/
chown -R www-data:www-data /var/www/

Loganalyzer einrichten

Wenn sich die PHP Dateien im Verzeichnis /var/www bfinden, rufen Sie die Startseite des Logserver per Browser auf. Loganalyzer wird anzeigen, dass er noch nicht konfiguriert wurde.

  • Führen Sie die Konfiguration nun per Browser durch.
  • Wählen Sie als "Source Type" MySQL Nativ aus

Loganalyzer config.png

Wenn Sie die Konfiguration nachträglich ändern wollen, finden Sie diese unter /var/www/config.php

phpMyAdmin als Alternative installieren

  • Laden Sie phpMyAdmin herunter und entpacken Sie es im webroot-Verzeichnis.
aptitude install php5-mcrypt
cd /tmp/
wget http://heanet.dl.sourceforge.net/project/phpmyadmin/phpMyAdmin/3.3.8/phpMyAdmin-3.3.8-all-languages.tar.gz
cd /var/www
tar xzf /tmp/phpMyAdmin-3.3.8-all-languages.tar.gz
chown -R www-data:www-data phpMyAdmin-3.3.8-all-languages/
ln -s phpMyAdmin-3.3.8-all-languages/ phpmyadmin
  • Optional können Sie eine Konfiguration anlegen, so dass die Benutzer die Zugangssdaten zur Datenbank nicht eingeben und nicht kennen müssen. Wenn Sie diese Konfiguration nicht anlegen, dann müssen Sich alle Benutzer mit den MySQL Zugangsdaten anmelden.

Legen Sie im Hauptverzeichnis von phpMyAdmin eine Datei config.inc.php an.

<?php
/*
 * Servers configuration
 */
$i = 0;

$i++;
// First server: local syslog MySQL Database
$cfg['Servers'][$i]['auth_type']       = 'config';
$cfg['Servers'][$i]['host']            = 'localhost';
$cfg['Servers'][$i]['user']            = 'rsyslog_reader';
$cfg['Servers'][$i]['password']        = 'password';
$cfg['Servers'][$i]['connect_type']    = 'tcp';
$cfg['Servers'][$i]['compress']        = false;
$cfg['Servers'][$i]['extension']       = 'mysql';
$cfg['Servers'][$i]['AllowNoPassword'] = false;

$cfg['UploadDir'] = '/tmp';
$cfg['SaveDir'] = '';
?>

Passwortschutz

Logs können sensible Daten enthalten und sollte nicht für Jedermann lesbar sein. Nicht ohne Grund ist /var/log/syslog nur für root lesbar.

Richten Sie deshalb einen Passwortschutz für Loganalyzer und phpmyadmin ein.

Fügen Sie zum Beispiel folgende Direktiven in die Datei /etc/lighttpd/conf-enabled/05-auth.conf ein.

server.modules                += ( "mod_auth" )

auth.backend                 = "plain"
auth.backend.plain.userfile  = "/etc/lighttpd/lighttpd.user"

auth.require = 	( "/" =>
		(
			"method" => "basic",
			"realm" => "something",
			"require" => "valid-user"
		)
		)

In der Datei /etc/lighttpd/lighttpd.user tragen Sie dann Benutzer und Passwörter mit ':' getrennt ein.

syslog:passwort

Remote Logging einrichten

fremde Logs akzeptieren

Damit der Logserver von fremden Hosts Logs akzeptiert, muss rsyslog im Netz auf eingehende Verbindungen lauschen. Aktivieren Sie dies in der Datei /etc/rsyslog.conf

# provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514

Dann rsyslog neu starten!

Auf einen fremden Server loggen

Sollte fremde Systeme remote loggen, erreichen Sie dies durch das Hinzufügen folgender Konfiguration zum Beispiel in der Datei /etc/rsyslog.d/remotelog.conf

# Log to remote host
*.* @192.168.100.1:514

Mit dieser Direktive werden alle Logmeldungen an den entfernten Server übertragen. Das Loggen in lokale Dateien bleibt bestehen, das heißt die Logmeldungen finden Sie lokal und auf dem entfernten Host.

lokales Logging deaktivieren

Möchten Sie bestimmte Meldungen nur an einen entferntes Syslog schicken, aber nicht lokal speichern, müssen Sie einen Filter anwenden.

Rsyslog arbeitet die Konfiguration von Oben nach Unten ab. Mit einem Tildezeichen '~' können Sie aber veranlassen, dass die weitere Verarbeitung abgebrochen wird.

Achten Sie also darauf, an welcher Stelle sich die Filter und Direktiven sich befinden.

Beispiel 1:

# Log verything to remote host
*.* @remotelog.example.com:514
# Log php related messages only to remote host. No local logging
# If the below condition matches, rsyslog stops further processing
:syslogtag, startswith, "php"  ~

Diese Regel sollte vor dem Schreiben der lokalen Logfiles eingefügt werden. Alle Meldungen werden an den entfernten Syslog geschickt. Wenn eine Meldung mit dem Syslogtag "php" beginnt, dann wird abgebrochen, das heißt es erfolgen keine Einträge in die lokalen Logfiles.

Beispiel 2:

# Log only php messages to remote log
:syslogtag, startswith, "php"  @remotelog.example.com:514
# and do not log to local syslog
:syslogtag, startswith, "php"  ~

Es gibt kein generelles Logging auf einem entfernten Server. Nur PHP Meldungen werden an den entfernten Syslog geschickt und nicht lokal gespeichert.

Syslog für Webapplikationen nutzen

Mit PHP Loggen

PHP kann syslog auf verschiedene Weisen nutzen

Die syslog() Funktion

Mit der Funktion syslog() werden Nachrichten direkt an den lokalen Syslog geschickt. Gemäß den Syslogregeln wird diese Nachricht dann weiterverarbeitet.

Beispiel:

<?php
// Produziere eine Eintrag im Syslog
// http://de.php.net/manual/en/function.syslog.php
syslog(LOG_NOTICE,'Eine Testmeldung');

Über diesen Weg werden nur Meldungen an Syslog geschickt, die explizit mit syslog() geloggt werden. Alle anderen Fehler oder Debug-Ausgaben werden gemäß den voreingestellten Standards behandelt.

Logging per ini_set() oder php.ini

Alternativ Sie in der php.ini global das Logging per Syslog aktivieren. Nehmen Sie folgende Einstellungen vor:

log_errors = On
error_log  = syslog
Beachten Sie, dass die meisten Distributionen verscheiden php.ini Dateien pflegen. Das Commandline PHP verwendet /etc/php5/cli/php.ini. Apache verwendet /etc/php5/apache/php.ini und lighttpd /etc/php5/cgi/php.ini

Wenn Sie das Loggen per Syslog nicht global aktivieren können oder wollen, so können Sie es auch gezielt im PHP-Code aktivieren. Verwenden Sie dazu init_set() wie folgt:

<?php
ini_set('log_errors','ON');
ini_set('error_log','syslog');

Sobald das Loggen per Syslog aktiviert ist, können Sie zum Beispiel mit der Funktion trigger_error() Informationen loggen.

Beispiel:

<?php
// http://de.php.net/manual/en/function.trigger-error.php
trigger_error('Ein Hinweis',E_USER_NOTICE);
trigger_error('Ein schwerer Fehler',E_USER_ERROR);

trigger_error() schreibt dabei automatisch den Scriptnamen und die Zeilennummer ins Log, von der aus der Eintrag gemacht wurde.

Oben genanntes Beispiel erzeugt folgende Einträge im Syslog:

mysql> select Message from SystemEvents order by id desc limit 2;
+-----------------------------------------------------------------------------+
| Message                                                                     |
+-----------------------------------------------------------------------------+
|  PHP Fatal error:  Ein schwerer Fehler in /root/trigger_error.php on line 4 |
|  PHP Notice:  Ein Hinweis in /root/trigger_error.php on line 3              |
+-----------------------------------------------------------------------------+
2 rows in set (0.00 sec)

Aufräumen und alte Logs löschen

In vielen Fällen sind die alten Logs wichtig, um zu erklären, was zu einem Fehler geführt hat. Es gibt aber auch viele Logs, die nicht archiviert werden müssen und die nur unnötig Festplattenplatz verschwenden.

Wenn Sie zum Beispiel von den PHP-Fehlermeldungen nur die letzten 30 Tage in Ihrer Syslog-Datenbank vorhalten wollen, können Sie per Cronjob regelmäßig die alten Logs löschen.

Eine SQL-Abfrage könnte dann zum Beispiel so aussehen:

DELETE FROM php_log
WHERE device_reported_time <= NOW() - INTERVAL 30 DAY;