PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Was ist gerade auf meinem Server los?

with 4 comments

Ich möchte hier ein kleines PHP-Script vorstellen das ich mir vor einiger Zeit mal geschrieben habe um einen schnellen Überblick meines Servers zu bekommen. Ich möchte dazu die letzten Zeilen einiger Logdateien anzeigen lassen. Anstatt mich dazu via SSH zum Server zu verbinden und alle Dateien durchzuschauen (tail -f), habe ich hier eine kleine Admin-Webseite, die mir das ganze auf einen Blick zeigt, und zwar fast in Echtzeit (je nachdem wie man die Refresh-Rate einstellt).

So sieht das ganze dann aus:

Logdateien werden häufig sehr groß, mehrere hundert MB sind keine Seltenheit. Beim Auslesen der Dateien muss man also darauf achten, nicht die ganze Datei in den Speicher zu laden. Funktionen wie file_get_contents() oder file() etc. fallen schonmal als Lösung raus. Des Rätsels Lösung sind Streams bzw. Streamwrapper. Wir öffnen die Datei mit Hilfe der Funktion fopen(), und können uns dann innerhalb der Datei bewegen (fseek()) bzw. Zeichen lesen (fgets() bzw. fgetc())

Doch wie finden wir die Punkte, an der die letzten Zeilen beginnen? Ganz einfach, wie fangen ganz hinten an und tasten uns zurück. Immer wenn wir auf ein \n stossen haben wir eine vollständige Zeile gefunden und speichern diese in einem Array. Das machen wir so lange bis wir die Anzahl der gewünschten Zeilen haben und können diese dann ausgeben.

Ein Stolperstein könnte noch entstehen aufgrund von restriktiven PHP-Einstellungen. Häufig untersagt man seinen Apache-VHosts, aus ihren Document-Roots auszubrechen und aktiviert noch weitere Sicherheitsmaßnahmen. Damit dieses Script funktioniert muss zumindestens die OpenBaseDir Einstellung angepasst werden, ansonsten kommt das Script nicht an die entsprechenden Verzeichnisse falls sie außerhalb der erlaubten Verzeichnisse liegen.

Hier das Script. Es hat nur eine sehr einfache Authentifizierung eingebaut, und auch die Ausgabe ist nicht W3C konform, aber es funktioniert in allen Browsern und das reicht mir. Außerdem werden <iframe>s benutzt, um die einzelnen Logdateien anzuzeigen. Wer mehr Zeit investieren möchte kann das ganze natürlich auch mittels <div>s und AJAX umbauen.

<?php
authenticate();

$files = array(
	'/var/log/messages' => 15,
	'/var/log/phperrors.log' => 15,
	'/var/log/syslog' => 15,
	'/var/log/mail.log' => 15,
	'/var/log/auth.log' => 10,
	'/var/kunden/logs/phpgangsta-error.log' => 5,
);

if (isset($_GET['logfile']) && isset($files[$_GET['logfile']])) {
	$lines = readLastLinesOfFile($_GET['logfile'], $files[$_GET['logfile']]); ?>
	<html>
		<head>
			<meta http-equiv="refresh" content="5">
		</head>
		<body style="margin: 0">
			<div style="font-size: 0.6em; white-space: nowrap">
				Filesize: <?=round(filesize($_GET['logfile'])/1024/1024,2) ?> MB<br/>
			<?	foreach ($lines as $line) {
					echo "<nowrap>$line</nowrap><br/>\n";
				} ?>
			</div>
		</body>
	</html>
<? } else { ?>
		<html>
			<head>
			</head>
			<body>
			<?	foreach($files as $filename => $lines) {
					echo $filename; ?>
					<iframe src="tail.php?logfile=<?=urlencode($filename)?>" style="height: <?=$lines*13+15?>px; width: 100%;"></iframe>
			<?	} ?>
			</body>
		</html>
<? }

function readLastLinesOfFile($filePath, $lines = 10) {
    //global $fsize;
    $handle = fopen($filePath, "r");
    if (!$handle) {
    	return array();
    }
    $linecounter = $lines;
    $pos = -2;
    $beginning = false;
    $text = array();
    while ($linecounter > 0) {
        $t = " ";
        while ($t != "\n") {
            if(fseek($handle, $pos, SEEK_END) == -1) {
                $beginning = true;
                break;
            }
            $t = fgetc($handle);
            $pos--;
        }
        $linecounter--;
        if ($beginning) {
            rewind($handle);
        }
        $text[$lines-$linecounter-1] = str_replace(array("\r", "\n", "\t"), '', fgets($handle));
        if ($beginning) break;
    }
    fclose($handle);
    return array_reverse($text);
}

function authenticate() {
	if (!isset($_SERVER['PHP_AUTH_USER']) || $_SERVER['PHP_AUTH_USER'] != 'padmin' || $_SERVER['PHP_AUTH_PW'] != 'meinadminpwd') {
	    header('WWW-Authenticate: Basic realm="Mein Bereich!"');
	    header('HTTP/1.0 401 Unauthorized');
	    echo 'Tja, so nicht mein Freund!';
	    exit;
	}
} ?>

Sicherlich nicht der weltbeste Code, aber es erfüllt seit langer Zeit seinen Zweck und benötigt keine speziellen PHP-Extensions oder Betriebssysteme. Man könnte sich z.B. auch eine Lösung erstellen, wo mittels

system('tail -n 10 /var/log/syslog')

gearbeitet wird, aber das läuft eben nicht überall.

Sehr interessant sieht auch diese tail-Lösung mittels libevent unter PHP aus. Dazu muss man allerdings libevent und die PHP-extension libevent installieren, es läuft also nicht direkt auf jedem Server. Es dürfte allerdings deutlich performanter sein bei sehr großen Dateien würde ich tippen.

Written by Michael Kliewe

Februar 1st, 2010 at 9:32 am

4 Responses to 'Was ist gerade auf meinem Server los?'

Subscribe to comments with RSS or TrackBack to 'Was ist gerade auf meinem Server los?'.

  1. Schönen guten Abend,

    das ist ein sehr schönes PHP-Skript um die log Files auszuwerten. Ich denke aber, dass bei großen Dateien dies problematisch wird. Da die maximale Ausführungszeit von PHP relativ klein ist. Bei einen Server wo relativ viel Betrieb ist, wird dies keine sinnvolle Lösung sein. Natürlich könnte man die maximale Ausführungszeit erhöhen.

    Grüße Nico

    Nico Schubert

    1 Feb 10 at 19:40

  2. Da müssen die Dateien schon sehr sehr groß werden damit diese Lösung an die Timeoutgrenze stößt, denn dem Stream ist es prinzipiell egal wie groß die Datei ist, da sie ja nicht sequenziell gelesen werden muss, sondern immer „von hinten“ nur wenige Kilobyte gelesen werden.

    Habe das gerade mal mit einer 2GB Logdatei getestet und die letzten 20 Zeilen waren innerhalb von 0.2 Sekunden extrahiert. Für die meisten (privaten und kleinbetrieblichen) Server sollte das reichen, um einen schnellen Überblick zu bekommen. Dank logrotate etc. werden die Dateien selten größer als einige Gigabyte.

    Michael Kliewe

    1 Feb 10 at 23:57

  3. Sehr gut ausgedachte Lösung.

    Das einlesen der Dateien war oftmals problematisch wegen der Grösse.. aber hier sehe ic sehr viel potenzial, endlich mal Logs auszuwerten.

    Kombiniert mit einer schönen Ausgabe: Perfekt!

    Dooki

    2 Feb 10 at 13:39

  4. Cool. Spart mir Zeit :-).

    Thanks for sharing

    Toni

    Toni

    8 Feb 10 at 00:48

Leave a Reply

You can add images to your comment by clicking here.