Das Symptom
Die Festplatte eines Servers läuft voll. Im Logverzeichnis liegt die erwartete
Ordnung: app.log, app.log.1, app.log.2.gz und so weiter, die Rotation läuft
täglich. Nur stimmt etwas nicht. app.log hat null Byte und ist seit Tagen null Byte.
app.log.1 dagegen hat vierzig Gigabyte und wächst im Sekundentakt. Jeden Morgen
benennt logrotate die Riesendatei um, legt eine leere neue an, und die Riesendatei
wächst unter neuem Namen weiter. Komprimiert wird sie nie, denn sie ist ja gerade
erst rotiert worden.
Der erste Verdacht trifft logrotate. Der zweite das Dateisystem. Beide sind unschuldig.
Die „Layer 8“-Wahrheit
Ein Programm, das eine Logdatei geöffnet hat, hält keinen Dateinamen, sondern einen
Dateigriff auf die Datei selbst. Wird die Datei umbenannt, zeigt der Griff weiter
auf dieselbe Datei, jetzt unter dem Namen mit .1. Die neue Datei, die logrotate per
create anlegt, hat niemand geöffnet; sie bleibt leer. Das Programm schreibt treu
dorthin, wo es immer geschrieben hat, und niemand hat ihm gesagt, dass sich etwas
geändert hat.
Die Handbuchseite von logrotate kennt diesen Fall und beschreibt ihn bei der Option,
die dafür gebaut wurde. copytruncate ist einsetzbar, „when some program cannot be
told to close its logfile and thus might continue writing (appending) to the previous
log file forever.“ Die Option ändert das Verfahren: „Truncate the original log file to
zero size in place after creating a copy, instead of moving the old log file and
optionally creating a new one.“ Die Datei bleibt also, wo sie ist, der Griff des
Programms bleibt gültig, und das Programm schreibt in eine geleerte Datei weiter. Zwei
Nebenwirkungen nennt die Seite gleich mit: create ist dann wirkungslos, weil die alte
Datei an Ort und Stelle bleibt, und zwischen Kopieren und Leeren liegt ein kleines
Zeitfenster, in dem Zeilen verloren gehen können.
Deshalb ist copytruncate die zweitbeste Lösung. Die beste ist, dem Programm das
Neuöffnen mitzuteilen. Die meisten Dienste, die Logdateien schreiben, haben dafür ein
Signal oder einen Reload-Befehl; welches, steht in ihrer Dokumentation, nicht in der
von logrotate. Dieser Befehl gehört in den postrotate-Abschnitt. Dann wird die alte
Datei umbenannt, die neue angelegt, der Dienst öffnet neu, und nichts fehlt.
Ein Detail macht beide Wege robust: delaycompress. Die Handbuchseite beschreibt
sie als Verschieben der Kompression der vorherigen Logdatei auf den nächsten
Rotationszyklus, wirksam nur zusammen mit compress, und nennt als Einsatzfall
wieder das Programm, das nicht zum Schließen seiner Logdatei gebracht werden kann
und deshalb noch eine Weile in die vorherige Datei schreibt. Komprimiert wird damit
immer erst die Datei, in die garantiert niemand mehr schreibt.
Der Fehler lag von Anfang an nicht in der Rotation, sondern in der Annahme, ein umbenannter Name würde ein laufendes Programm interessieren. Tut er nicht. Das Programm hat die Datei; der Name ist nur das, was Menschen sehen.