Como finalizar o comando tee do Linux sem interromper o aplicativo que está recebendo do

19

Eu tenho um script bash que é executado enquanto a máquina Linux estiver ligada. Começo como mostrado abaixo:

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

Após o lançamento, posso ver o comando tee na minha saída ps, como mostrado abaixo:

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

Eu tenho uma função que monitora o tamanho do log que tee produz e mata o comando tee quando o log atinge um determinado tamanho:

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
}

Para minha surpresa, matar o comando tee também mata pela instância start.sh. Por que é isso? Como posso terminar o comando tee, mas meu start.sh continua em execução? Obrigado.

PhilBot
fonte

Respostas:

34

Quando teetermina, o comando que o alimenta continuará sendo executado, até que tente gravar mais saída. Em seguida, ele receberá um SIGPIPE (13 na maioria dos sistemas) por tentar gravar em um canal sem leitores.

Se você modificar seu script para interceptar o SIGPIPE e executar alguma ação apropriada (como parar de gravar a saída), poderá continuar com ele após o término do tee.


Melhor ainda, em vez de matar tee , use logrotatecom a copytruncateopção de simplicidade.

Para citar logrotate(8):

copytruncate

Trunque o arquivo de log original depois de criar uma cópia, em vez de mover o arquivo de log antigo e, opcionalmente, criar um novo. Ele pode ser usado quando algum programa não pode ser solicitado a fechar seu arquivo de log e, portanto, pode continuar gravando (anexando) no arquivo de log anterior para sempre. Observe que há um intervalo de tempo muito pequeno entre copiar o arquivo e truncá-lo, portanto, alguns dados de log podem ser perdidos. Quando essa opção é usada, a opção de criação não terá efeito, pois o arquivo de log antigo permanece no local.

Curinga
fonte
9
Você também gostaria de usar o tee -apara teeabrir o arquivo no modo de acréscimo; caso contrário, o tee continuará gravando o arquivo no mesmo deslocamento após a truncagem (e em sistemas que não suportam arquivos esparsos, como no macOS, que realoque a seção do arquivo que leva até essa posição, ocupando o dobro do espaço em disco).
Stéphane Chazelas
4
Outra opção seria canalizar logger -spara o syslog para cuidar do registro ( -spara também imprimir no stderr).
Stéphane Chazelas
1
+1 para logrotate. Ótimo programa
Dmitry Kudriavtsev
2
Ou em um sistema usando systemd e journald, systemd-cat em vez de logger. Então você obtém muita filtragem e rotação de saída gratuitamente.
precisa saber é o seguinte
3

Explicando o "porquê"

Resumindo: se falhas de gravação não causassem a saída de um programa (por padrão), teríamos uma bagunça. Considere find . | head -n 10- você não quer findcontinuar correndo, examinando o restante do seu disco rígido, depois de headjá ter tomado as 10 linhas necessárias e prosseguido.

Melhorando: gire dentro do seu logger

Considere o seguinte, que não usa teenada, como um exemplo demonstrativo:

#!/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 executado como:

/mnt/apps/start.sh 2>&1 | above-script /tmp/nginx/debug_log

... isso começará anexando a /tmp/nginx/debug_log, renomeando o arquivo para /tmp/nginx/debug_log.oldquando mais de 100 KB de conteúdo estiverem presentes. Como o próprio criador de logs está fazendo a rotação, não há canal quebrado, nenhum erro e nenhuma janela de perda de dados quando a rotação ocorre - todas as linhas serão gravadas em um arquivo ou outro.

Obviamente, implementar isso no bash nativo é ineficiente, mas o acima é um exemplo ilustrativo. Existem inúmeros programas disponíveis que implementarão a lógica acima para você. Considerar:

  • svlogd, o log de serviço do conjunto Runit.
  • s6-log, uma alternativa mantida ativamente do pacote skanet.
  • multilog DJB Daemontools, o avô desta família de ferramentas de supervisão e monitoramento de processos.
Charles Duffy
fonte