A melhor maneira de seguir um log e executar um comando quando algum texto aparece no log

44

Eu tenho um log do servidor que gera uma linha de texto específica em seu arquivo de log quando o servidor está ativo. Eu quero executar um comando uma vez que o servidor esteja ativo e, portanto, faça algo como o seguinte:

tail -f /path/to/serverLog | grep "server is up" ...(now, e.g., wget on server)?

Qual é a melhor maneira de fazer isso?

    
por jonderry 26.04.2011 / 23:26

6 respostas

29

Um jeito simples seria o awk.

tail -f /path/to/serverLog | awk '/Printer is on fire!/ { system("shutdown -h now") }
                                  /new USB high speed/ { system("echo New USB" | mail admin")'

E sim, ambos são mensagens reais de um log do kernel. Perl pode ser um pouco mais elegante de usar para isso e também pode substituir a necessidade de cauda. Se usar perl, será algo parecido com isto:

open(my $fd, "<", "/path/to/serverLog") or die "Can't open log";
while(1) {
    if(eof $dataFd) {
        sleep 1;
        $fd->clearerr;
        next;
    }
    my $line = <$fd>;
    chomp($line);
    if($line =~ /Printer is on fire!/) {
        system("shutdown -h now");
    } elsif($line =~ /new USB high speed/) {
        system("echo New USB" | mail admin");
    }
}
    
por 26.04.2011 / 23:48
12

Esta questão parece já ter sido respondida, mas penso que existe uma solução melhor.

Em vez de tail | whatever , acho que o que você realmente quer é swatch . O Swatch é um programa projetado explicitamente para fazer o que você está perguntando, observando um arquivo de log e executando ações com base em linhas de log. Usar tail|foo exigirá que você tenha um terminal funcionando ativamente para fazer isso. O Swatch, por outro lado, é executado como um daemon e sempre estará vigiando seus registros. O Swatch está disponível em todas as distribuições do Linux,

Eu encorajo você a experimentar. Enquanto você pode bater um prego com a parte de trás de uma chave de fenda não significa que você deveria.

O melhor tutorial de 30 segundos de amostra que pude encontrar está aqui: link

    
por 16.05.2011 / 17:12
9

Se você está procurando apenas uma possibilidade e deseja permanecer principalmente no shell, em vez de usar awk ou perl , pode fazer algo como:

tail -F /path/to/serverLog | 
grep --line-buffered 'server is up' | 
while read ; do my_command ; done

... que executará my_command sempre que " servidor estiver ativo " aparecer no arquivo de log. Para várias possibilidades, talvez você possa descartar o grep e, em vez disso, usar um case dentro do while .

O capital -F informa tail para observar o arquivo de log a ser rotacionado; isto é, se o arquivo atual for renomeado e outro arquivo com o mesmo nome tiver seu lugar, tail mudará para o novo arquivo.

A opção --line-buffered informa grep para liberar seu buffer após cada linha; caso contrário, my_command pode não ser alcançado em tempo hábil (supondo que os logs tenham linhas razoavelmente dimensionadas).

    
por 27.04.2011 / 09:35
7

É estranho que ninguém tenha mencionado sobre multitail utility, que tem essa funcionalidade pronta para uso. Um dos exemplos de uso:

Show the output of a ping-command and if it displays a timeout, send a message to all users currently logged in

multitail -ex timeout "echo timeout | wall" -l "ping 192.168.0.1"

Veja também outros exemplos de multitail usage.

    
por 27.04.2011 / 20:39
6

Foi assim que comecei a fazer isso, mas me tornei muito mais sofisticado com isso. Algumas coisas para se preocupar:

  1. Se a parte final do log já contiver "servidor está ativo".
  2. Finalizar automaticamente o processo final depois de encontrado.

Eu uso algo nos moldes disso:

RELEASE=/tmp/${RANDOM}$$
(
  trap 'false' 1
  trap "rm -f ${RELEASE}" 0
  while ! [ -s ${RELEASE} ]; do sleep 3; done
  # You can put code here if you want to do something
  # once the grep succeeds.
) & wait_pid=$!
tail --pid=${wait_pid} -F /path/to/serverLog \
| sed "1,10d" \
| grep "server is up" > ${RELEASE}

Funciona mantendo tail aberto até que o arquivo ${RELEASE} contenha dados.

Quando o grep for bem-sucedido:

  1. escreve a saída para ${RELEASE} , que será
  2. termine o processo ${wait_pid} para
  3. sair do tail

Nota: O sed pode ser mais sofisticado para determinar realmente o número de linhas que o tail produzirá na inicialização e remover esse número. Mas geralmente é 10.

    
por 26.04.2011 / 23:49
6

poderia fazer o trabalho sozinho

Vamos ver como é simples e legível:

mylog() {
    echo >>/path/to/myscriptLog "$@"
}

while read line;do
    case "$line" in
        *"Printer on fire"* )
            mylog Halting immediately
            shutdown -h now
            ;;
        *DHCPREQUEST* )
            [[ "$line" =~ DHCPREQUEST\ for\ ([^\ ]*)\  ]]
            mylog Incomming or refresh for ${BASH_REMATCH[1]}
            $HOME/SomethingWithNewClient ${BASH_REMATCH[1]}
            ;;
        * )
            mylog "untrapped entry: $line"
            ;;
    esac
  done < <(tail -f /path/to/logfile)

Enquanto você não usa regex do bash, isso pode ficar muito rápido!

Mas + é um conjunto muito eficiente e interessante

Mas para servidores de alta carga, e como gosto de sed porque é muito rápido e muito escalável, geralmente uso isso:

while read event target lost ; do
    case $event in
        NEW )
            ip2int $target intTarget
            ((count[intTarget]++))
        ...

    esac
done < <(tail -f /path/logfile | sed -une '
  s/^.*New incom.*from ip \([0-9.]\+\) .*$/NEW /p;
  s/^.*Auth.*ip \([0-9.]\+\) failed./FAIL /p;
  ...
')
    
por 22.07.2013 / 19:04