So beenden Sie den Linux-Befehl tee, ohne die Anwendung zu beenden, von der er empfangen wird

19

Ich habe ein Bash-Skript, das ausgeführt wird, solange der Linux-Computer eingeschaltet ist. Ich starte es wie unten gezeigt:

( /mnt/apps/start.sh 2>&1 | tee /tmp/nginx/debug_log.log ) &

Nach dem Start kann ich den Befehl tee in meiner ps- Ausgabe wie folgt sehen:

$ ps | grep tee
  418 root       0:02 tee /tmp/nginx/debug_log.log
3557 root       0:00 grep tee

Ich habe eine Funktion, die die Größe des Protokolls überwacht, das tee erzeugt und den tee- Befehl beendet, wenn das Protokoll eine bestimmte Größe erreicht:

monitor_debug_log_size() {
                ## Monitor the file size of the debug log to make sure it does not get too big
                while true; do
                                cecho r "CHECKING DEBUG LOG SIZE... "
                                debugLogSizeBytes=$(stat -c%s "/tmp/nginx/debug_log.log")
                                cecho r "DEBUG LOG SIZE: $debugLogSizeBytes"
                                if [ $((debugLogSizeBytes)) -gt 100000 ]; then
                                                cecho r "DEBUG LOG HAS GROWN TO LARGE... "
                                                sleep 3
                                                #rm -rf /tmp/nginx/debug_log.log 1>/dev/null 2>/dev/null
                                                kill -9 `pgrep -f tee`
                                fi
                                sleep 30
                done
}

Zu meiner Überraschung wird das Beenden des tee- Befehls auch durch die start.sh-Instanz beendet. Warum ist das? Wie kann ich den tee- Befehl beenden, aber meine start.sh weiterhin ausführen? Vielen Dank.

PhilBot
quelle

Antworten:

34

Nach dem teeBeenden wird der Befehl feed it so lange ausgeführt, bis versucht wird, weitere Ausgaben zu schreiben. Dann erhält es ein SIGPIPE (13 auf den meisten Systemen), um zu versuchen, ohne Leser auf eine Pipe zu schreiben.

Wenn Sie Ihr Skript so ändern, dass es SIGPIPE abfängt und eine geeignete Aktion ausführt (z. B. das Schreiben der Ausgabe beenden), sollten Sie in der Lage sein, das Skript nach dem Beenden von tee fortzusetzen.


Noch besser wäre es, anstatt zu töten tee überhaupt, Verwendung logrotatemit der copytruncateOption aus Gründen der Einfachheit.

Um zu zitieren logrotate(8):

copytruncate

Kürzen Sie die ursprüngliche Protokolldatei, nachdem Sie eine Kopie erstellt haben, anstatt die alte Protokolldatei zu verschieben und optional eine neue zu erstellen. Es kann verwendet werden, wenn ein Programm nicht angewiesen werden kann, seine Protokolldatei zu schließen, und daher möglicherweise für immer in die vorherige Protokolldatei schreibt (anhängt). Beachten Sie, dass zwischen dem Kopieren und dem Abschneiden der Datei eine sehr kurze Zeitspanne liegt, sodass möglicherweise einige Protokolldaten verloren gehen. Wenn diese Option verwendet wird, hat die Option create keine Auswirkung, da die alte Protokolldatei an Ort und Stelle bleibt.

Platzhalter
quelle
9
Sie möchten auch tee -afor verwenden tee, um die Datei im Anhänge-Modus zu öffnen. Andernfalls schreibt tee nach dem Abschneiden weiterhin mit demselben Versatz in die Datei (und auf Systemen, die keine Sparse-Dateien unterstützen, wie dies unter macOS der Fall ist) Ordnen Sie den Abschnitt der Datei, der zu dieser Position führt, neu zu (und nehmen Sie dabei doppelt so viel Speicherplatz ein).
Stéphane Chazelas
4
Eine andere Möglichkeit wäre, eine Pipe an logger -szu senden, damit Syslog sich um die Protokollierung kümmert ( -sum auch auf stderr zu drucken).
Stéphane Chazelas
1
+1 für logrotate. Tolles Programm
Dmitry Kudriavtsev
2
Oder auf einem System mit systemd und journald, systemd-cat anstelle von logger. Dann erhalten Sie jede Menge Ausgabefilterung und -rotation kostenlos.
Zan Lynx
3

Das "Warum" erklären

Kurz gesagt: Wenn schreibt failing nicht tat Ursache ein Programm zu beenden (Standard), würden wir ein Chaos haben. Bedenken find . | head -n 10Sie - Sie möchten nicht findweiterlaufen und den Rest Ihrer Festplatte scannen, nachdem Sie headbereits die 10 benötigten Zeilen belegt und fortgefahren haben.

Besser machen: Drehen Sie in Ihrem Logger

Betrachten Sie Folgendes, das teeals anschauliches Beispiel überhaupt nicht verwendet wird :

#!/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_logund dem Umbenennen der Datei in, /tmp/nginx/debug_log.oldwenn mehr als 100 KB Inhalt vorhanden sind. Da der Logger selbst die Rotation durchführt, gibt es keine unterbrochene Pipe, 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. Erwägen:

  • svlogd, der Service-Logger aus der Runit-Suite.
  • s6-log, eine aktiv gewartete Alternative aus der Skanet-Suite.
  • multilog von DJB Daemontools, dem Urvater dieser Familie von Prozessüberwachungs- und Überwachungswerkzeugen.
Charles Duffy
quelle