Wenn tee
beendet wird, wird der Befehl, der es speist, weiter ausgeführt, bis es versucht, weitere Ausgaben zu schreiben. Dann erhält es eine SIGPIPE (13 auf den meisten Systemen) für den Versuch, in eine Pipe ohne Leser zu schreiben.
Wenn Sie Ihr Skript so ändern, dass es SIGPIPE abfängt und eine geeignete Aktion ausführt (z. B. das Schreiben von Ausgaben beenden), sollten Sie in der Lage sein, es fortzusetzen, nachdem tee beendet wurde.
Besser noch, anstatt tee
zu töten überhaupt Verwenden Sie logrotate
mit dem copytruncate
Option der Einfachheit halber.
Um logrotate(8)
zu zitieren :
copytruncate
Kürzen Sie die ursprüngliche Protokolldatei an Ort und Stelle, nachdem Sie eine Kopie erstellt haben, anstatt die alte Protokolldatei zu verschieben und optional eine neue zu erstellen. Es kann verwendet werden, wenn einem Programm nicht gesagt werden kann, dass es seine Protokolldatei schließen soll, und daher möglicherweise für immer an die vorherige Protokolldatei geschrieben (angehängt) wird. Beachten Sie, dass zwischen dem Kopieren der Datei und dem Abschneiden nur ein sehr kleiner Zeitabschnitt liegt, sodass möglicherweise einige Protokolldaten verloren gehen. Wenn diese Option verwendet wird, hat die Erstellungsoption keine Auswirkung, da die alte Protokolldatei bestehen bleibt.
Das „Warum“ erklären
Kurz gesagt:Wenn Schreibvorgänge nicht fehlschlugen ein Programm zum Beenden bringen (standardmäßig), hätten wir ein Durcheinander. Betrachten Sie find . | head -n 10
-- Sie wollen find
nicht um weiterzumachen und den Rest Ihrer Festplatte zu scannen, nach head
hat bereits die 10 benötigten Zeilen genommen und ist weitergefahren.
Besser machen:Rotieren Sie in Ihrem Logger
Betrachten Sie das Folgende, das tee
nicht verwendet überhaupt, als anschauliches Beispiel:
#!/usr/bin/env bash
file=${1:-debug.log} # filename as 1st argument
max_size=${2:-100000} # max size as 2nd argument
size=$(stat --format=%s -- "$file") || exit # Use GNU stat to retrieve size
exec >>"$file" # Open file for append
while IFS= read -r line; do # read a line from stdin
size=$(( size + ${#line} + 1 )) # add line's length + 1 to our counter
if (( size > max_size )); then # and if it exceeds our maximum...
mv -- "$file" "$file.old" # ...rename the file away...
exec >"$file" # ...and reopen a new file as stdout
size=0 # ...resetting our size counter
fi
printf '%s\n' "$line" # regardless, append to our current stdout
done
Wenn ausgeführt als:
/mnt/apps/start.sh 2>&1 | above-script /tmp/nginx/debug_log
... dies beginnt mit dem Anhängen an /tmp/nginx/debug_log
, indem Sie die Datei in /tmp/nginx/debug_log.old
umbenennen wenn über 100 KB Inhalt vorhanden sind. Da der Logger selbst die Rotation durchführt, gibt es keine gebrochene Leitung, keinen Fehler und kein Datenverlustfenster, wenn die Rotation stattfindet – jede Zeile wird in die eine oder andere Datei geschrieben.
Natürlich ist die Implementierung in nativer Bash ineffizient, aber das obige ist ein anschauliches Beispiel. Es stehen zahlreiche Programme zur Verfügung, die die obige Logik für Sie implementieren. Bedenken Sie:
svlogd
, der Service-Logger aus der Runit-Suite.s6-log
, eine aktiv gepflegte Alternative aus der skanet-Suite.multilog
von DJB Daemontools, dem Urvater dieser Familie von Prozessüberwachungs- und Überwachungstools.