Como encerrar o comando tee do Linux sem matar o aplicativo que ele está recebendo do

18

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

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

Depois disso, posso ver o comando tee na minha saída ps , conforme 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 certo 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 de start.sh. Por que é isso? Como posso terminar o comando tee mas o meu start.sh continua a correr? Obrigado.

    
por PhilBot 05.01.2018 / 01:31

2 respostas

34

Quando tee terminar, o comando que o alimenta continuará sendo executado, até tentar gravar mais resultados. Então, ele obterá um SIGPIPE (13 na maioria dos sistemas) para tentar gravar em um pipe sem leitores.

Se você modificar seu script para capturar SIGPIPE e tomar alguma ação apropriada (como, parar de escrever saída), então você deve ser capaz de continuar após o término do tee.

Melhor ainda, em vez de matar tee , use logrotate com a opção copytruncate para simplificar.

Para citar logrotate(8) :

copytruncate

Truncate the original log file in place after creating a copy, instead of moving the old log file and optionally creating a new one. It can be used when some program cannot be told to close its logfile and thus might continue writing (appending) to the previous log file forever. Note that there is a very small time slice between copying the file and truncating it, so some logging data might be lost. When this option is used, the create option will have no effect, as the old log file stays in place.

    
por 05.01.2018 / 02:04
3

Explicando o "Porquê"

Resumindo: Se as gravações com falha não fizerem com que um programa saia (por padrão), teremos uma bagunça. Considere find . | head -n 10 - você não quer que find continue em execução, examinando o restante de seu disco rígido, depois que head já pegou as 10 linhas necessárias e continuou.

Fazendo melhor: girar dentro do seu registrador

Considere o seguinte, que não usa tee , 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 executar como:

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

... isso começará adicionando /tmp/nginx/debug_log , renomeando o arquivo para /tmp/nginx/debug_log.old quando houver mais de 100KB de conteúdo. Como o registrador em si está fazendo a rotação, não há nenhum canal quebrado, nenhum erro e nenhuma janela de perda de dados quando a rotação ocorre - cada linha será gravada em um arquivo ou outro.

É claro que implementar isso na bash nativa é ineficiente, mas o exemplo acima é ilustrativo. Existem vários programas disponíveis que implementarão a lógica acima para você. Considere:

  • svlogd , o registrador de serviços do pacote Runit.
  • s6-log , uma alternativa mantida ativamente a partir da suíte da skanet.
  • multilog do DJB Daemontools, o avô dessa família de ferramentas de supervisão e monitoramento de processos.
por 05.01.2018 / 23:07