Archive for Januar, 2009

trained monkeys on youtube

youtube error trained monkeys on youtube

lighttpd access log pipe + buffer STDIN und STDOUT

Ich schreibe gerade an einem lighttpd perl script, das über STDIN access-log lines parst und in eine Remote MySQL Datenbank schreibt (DBI).

Mit lighttpd lassen sich die accesslogs per STDIN in einen von lighttpd gestarteten Prozess schreiben:

accesslog.filename = "|/var/www/test.pl"

Das Script test.pl erhält die Daten per und läuft anhand eines while loops:

while (<stdin>) { ... mysql insert ... }

lighttpd kurz neustarten – funktioniert.

Erklärung

lighttpd startet den Prozess test.pl, welcher die Log-Lines über die pipe auf STDOUT schreibt. Der test.pl-Prozess wartet anhand des while-loops auf hereinkommende Log-Lines (STDIN). Kommen Log-Lines auf STDIN an, fängt der while-loop an diese auf einen entfernen MySQL Log Server zu schreiben.

Die pipe in lighttpd nichts anderes als:

cat access.log | ./test.pl

Nur das cat access.log der lighttpd Prozess ist, der auf STDOUT schreibt.

Nun kommen wir zum eigentlichen Problem:

Problem

Die pipe hat einen begrenzen Buffer: 64K. Werden also die Log-Lines von test.pl nicht schnell genug abgearbeitet, darf der Prozess der auf STDOUT schreibt, warten.

Das beudetet bei lighttpd: Werden zu schnell zu viele Log-Lines generiert, kann test.pl diese nicht abarbeiten und die Website steht still.

Lösung

Buffering der Log-Lines. Mir wurden in #perl einige Lösungen angeboten:
- IO::All
- IO::Async
- Buffern über Dateien
- Buffern in Ram-Disk

Auslagern auf Dateien kommt nicht in Frage, da ich temporäre Logs bei 80% übrigen Ram (was 6GB ausmacht) nicht in eine Datei schreibe.
Buffern in Ram-Disk wäre zwar nice und einfach, jedoch wieder Änderungen am System, was ich wegen diesem kleinem Script nicht wollte.
Ebenso fallen die externen Module weg, da das Script später auf vielen Servern eingesetzt wird, möchte ich nicht jedesmal in CPAN rumfummeln.

Nun habe ich ein Programm gefunden, das genau diese Aufgabe erledigt: bfr

Wir installieren kurz bfr:

apt-get install bfr

Und konfigurieren lighttpd um:

accesslog.filename = "| bfr -b 2M -m 0 -p |/var/www/test.pl"

Die Buffergröße wird durch -b 50M festgelegt (default: 1M).
Durch “-p” erhalten wir eine Progress-Anzeige, die die aktuelle Nutzung des Buffers anzeigt (kann nach Erfassung der notwendigen Buffergröße entfernt werden).
Mit “-m 0″ sagen wir bfr, das sobald Daten vorhanden sind, diese weitergeleitet werden. 1M würde beispielsweise bedeutet: Sammele 1M und schiebe diese dann durch die pipe.

Nun wird alles in den von bfr erzeugten Buffer geschrieben und bfr schreibt sobald die pipe nicht blockiert ist per STDOUT an den Prozess.

Damit der Buffer Sinn macht, muss es natürlich eine Zeit geben, an dem das Script die Daten abarbeiten kann. Gibt es permanent zuviele Log-Lines und das Script kommt nicht nach, sollte man nach anderen Lösungen suchen.

Ich hoffe ich konnte helfen :-).