Quando tee
termina, il comando che lo alimenta continuerà a essere eseguito, fino a quando non tenterà di scrivere altro output. Quindi otterrà un SIGPIPE (13 sulla maggior parte dei sistemi) per provare a scrivere su una pipe senza lettori.
Se modifichi il tuo script per intercettare SIGPIPE e intraprendi un'azione appropriata (come, interrompi la scrittura dell'output), dovresti essere in grado di farlo continuare dopo che tee è terminato.
Meglio ancora, piuttosto che uccidere tee
per niente, usa logrotate
con il copytruncate
opzione per semplicità.
Per citare logrotate(8)
:
copytruncate
Tronca il file di registro originale in posizione dopo averne creato una copia, invece di spostare il vecchio file di registro e, facoltativamente, crearne uno nuovo. Può essere utilizzato quando non si può dire a qualche programma di chiudere il suo file di registro e quindi potrebbe continuare a scrivere (aggiungere) al file di registro precedente per sempre. Si noti che esiste un intervallo di tempo molto breve tra la copia del file e il suo troncamento, quindi alcuni dati di registrazione potrebbero andare persi. Quando viene utilizzata questa opzione, l'opzione create non avrà alcun effetto, poiché il vecchio file di registro rimane al suo posto.
Spiegazione del "perché"
In breve:se le scritture falliscono non causare l'uscita di un programma (per impostazione predefinita), avremmo un pasticcio. Considera find . | head -n 10
-- non vuoi find
per continuare a eseguire la scansione del resto del disco rigido, dopo head
ha già preso le 10 righe di cui aveva bisogno e ha proceduto.
Fallo meglio:ruota all'interno del tuo logger
Considera quanto segue, che non usa tee
affatto, come esempio dimostrativo:
#!/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
Se eseguito come:
/mnt/apps/start.sh 2>&1 | above-script /tmp/nginx/debug_log
...questo inizierà aggiungendo a /tmp/nginx/debug_log
, rinominando il file in /tmp/nginx/debug_log.old
quando sono presenti oltre 100KB di contenuti. Poiché è il logger stesso a eseguire la rotazione, non ci sono pipe rotte, errori e finestre di perdita di dati quando avviene la rotazione:ogni riga verrà scritta in un file o in un altro.
Naturalmente, l'implementazione di questo in bash nativo è inefficiente, ma quanto sopra è un esempio illustrativo. Sono disponibili numerosi programmi che implementeranno la logica di cui sopra per te. Considera:
svlogd
, il service logger della suite Runit.s6-log
, un'alternativa mantenuta attivamente dalla suite skanet.multilog
da DJB Daemontools, il nonno di questa famiglia di strumenti per la supervisione e il monitoraggio dei processi.