Acrescentando o carimbo de data e hora junto com as linhas do arquivo de log

10

Eu tenho um arquivo de registro e preciso anexar registros de data e hora a cada linha à medida que eles são adicionados. Então, estou procurando um script que acrescente registros de data e hora a cada entrada em linhas de log e que possa ser executado como uma tarefa cron.

    
por Kratos 20.11.2012 / 11:42

4 respostas

18

O caminho geral

$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log

Como funciona:

  1. cat lê o arquivo chamado input.log e apenas o imprime em seu fluxo de saída padrão.

    Normalmente, a saída padrão é conectada a um terminal, mas esse pequeno script contém | , portanto, o shell redireciona a saída padrão de cat para a entrada padrão de sed .

  2. sed lê dados (como cat produz), processa-os (de acordo com o script fornecido com -e option) e imprime-os em sua saída padrão. O script "s/^/$(date -R) /" significa substituir todo início de linha por um texto gerado pelo comando date -R (o comando de construção geral para substituição é: s/pattern/replace/ ).

  3. Em seguida, de acordo com >> bash redireciona a saída de sed para um arquivo chamado output.log ( > significa substituir o conteúdo do arquivo e >> significa anexar ao final).

O problema é o $(date -R) avaliado uma vez quando você executa o script para que ele insira o current timestamp no início de cada linha. O timestamp atual pode estar longe de um momento em que uma mensagem foi gerada. Para evitá-lo, você precisa processar as mensagens à medida que são gravadas no arquivo, e não com uma tarefa cron.

FIFO

O redirecionamento de fluxo padrão descrito acima, chamado pipe . Você pode redirecioná-lo não apenas com | entre os comandos no script, mas através de um arquivo FIFO (também conhecido como pipe nomeado ). Um programa gravará no arquivo e outro lerá os dados e os receberá no primeiro envio.

Escolha um exemplo:

$ mkfifo foo.log.fifo
$ while true; do cat foo.log.fifo | sed -e "s/^/$(date -R) /" >> foo.log; done;

# have to open a second terminal at this point
$ echo "foo" > foo.log.fifo
$ echo "bar" > foo.log.fifo
$ echo "baz" > foo.log.fifo

$ cat foo.log      

Tue, 20 Nov 2012 15:32:56 +0400 foo
Tue, 20 Nov 2012 15:33:27 +0400 bar
Tue, 20 Nov 2012 15:33:30 +0400 baz

Como funciona:

  1. mkfifo cria um canal nomeado

  2. while true; do sed ... ; done executa um loop infinito e, a cada iteração, executa sed com o redirecionamento de foo.log.fifo para sua entrada padrão; sed bloqueia a espera por dados de entrada e, em seguida, processa uma mensagem recebida e a imprime na saída padrão redirecionada para foo.log .

    Neste ponto, você precisa abrir uma nova janela de terminal, porque o loop ocupa o terminal atual.

  3. echo ... > foo.log.fifo imprime uma mensagem para sua saída padrão redirecionada para o arquivo fifo e sed a recebe e processa e grava em um arquivo normal.

A nota importante é o fifo, assim como qualquer outro pipe não faz sentido se um de seus lados não estiver conectado a nenhum processo. Se você tentar gravar em um pipe, o processo atual irá bloquear até que alguém leia os dados do outro lado do canal. Se você quiser ler de um pipe, o processo irá bloquear até que alguém grave dados no pipe. O loop sed no exemplo acima não faz nada (dorme) até você fazer echo .

Para sua situação particular, basta configurar seu aplicativo para gravar mensagens de log no arquivo fifo. Se você não puder configurá-lo - simplesmente exclua o arquivo de log original e crie um arquivo fifo. Mas observe novamente que se o loop sed morrer por algum motivo - seu programa será bloqueado ao tentar write do arquivo até que alguém read do fifo.

O benefício é o registro de data e hora atual avaliado e anexado a uma mensagem enquanto o programa o grava no arquivo.

Processamento assíncrono com tailf

Para tornar a gravação no log e no processamento mais independente, você pode usar dois arquivos regulares com tailf . Um aplicativo gravará uma mensagem em um arquivo bruto e outro processo lerá novas linhas (siga para as gravações de maneira assíncrona) e processará os dados gravando no segundo arquivo.

Vamos dar um exemplo:

# will occupy current shell
$ tailf -n0 bar.raw.log | while read line; do echo "$(date -R) $line" >> bar.log; done;

$ echo "foo" >> bar.raw.log
$ echo "bar" >> bar.raw.log
$ echo "baz" >> bar.raw.log

$ cat bar.log

Wed, 21 Nov 2012 16:15:33 +0400 foo
Wed, 21 Nov 2012 16:15:36 +0400 bar
Wed, 21 Nov 2012 16:15:39 +0400 baz

Como funciona:

  1. Execute o processo tailf que seguirá as gravações para bar.raw.log e imprima-as na saída padrão redirecionada para o loop while read ... echo infinito. Esse loop executa duas ações: ler dados da entrada padrão para uma variável de buffer chamada line e, em seguida, gravar o registro de data e hora gerado com os seguintes dados em buffer no bar.log .

  2. Escreva algumas mensagens para o bar.raw.log . Você tem que fazer isso em uma janela de terminal separada, porque a primeira será ocupada por tailf , que seguirá as gravações e fará seu trabalho. Muito simples.

Os profissionais são que seu aplicativo não bloquearia se você matasse tailf . Os contras são timestamps menos precisos e duplicação de arquivos de log.

    
por 20.11.2012 / 12:27
4

Você pode usar o script ts perl de moreutils :

$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
    
por 20.11.2012 / 14:34
2

Modificado da resposta de Dmitry Vasilyanov.

No script bash, você pode redirecionar e agrupar a saída com timestamp linha por linha, em tempo real.

Quando usar:

  • Para trabalhos de script bash, insira a linha antes do script principal
  • Para tarefas que não são de script, crie um script para chamar o programa.
  • Para o controle de serviços pelo sistema, é melhor usar tailf para o arquivo de log, como disse Dmitry Vasilyanov.

Um exemplo chamado foo.sh :

#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)

echo "foo"
sleep 1
echo "bar" >&2
sleep 1
echo "foobar"

E o resultado:

$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar

Como funciona

  1. exec &> Redirecionar stdout e stderr para o mesmo lugar
  2. >( ... ) pipe é enviado para um comando interno assíncrono
  3. A parte restante funciona como Dmitry Vasilyanov expandido.

Por exemplo:

  • registro de data e hora do canal e registro no arquivo

    #!/bin/bash        
    exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
    
    echo "some script commands"
    /path-to/some-thrid-party-programs
    
  • Ou imprima o registro de data e hora e registre-o no stdout

    #!/bin/bash        
    exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line"; done;)
    
    echo "some script commands"
    /path-to/some-thrid-party-programs
    

    salve-os em /etc/crontab setting

    * * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
    
por 12.05.2014 / 13:54
1

Eu usei ts desta forma para obter uma entrada com um registro de data e hora em um log de erros para um script que eu uso para obter o Cacti preenchido com estatísticas de um host remoto.

Para testar o Cacti eu uso rand para adicionar alguns valores aleatórios que eu uso para gráficos de temperatura para monitorar a temperatura do meu sistema.

O Pushmonstats.sh é um script que coleta as estatísticas de temperatura do sistema do meu PC e as envia para um Raspberry Pi no qual o Cacti é executado. Algum tempo atrás, a rede estava presa. Eu só tenho tempo limite SSH no meu log de erro. Infelizmente, não há entradas de tempo nesse log. Eu não sabia como adicionar um carimbo de hora a uma entrada de log. Então, depois de algumas buscas na internet, eu tropecei neste post e foi isso que eu fiz usando ts .

Para testá-lo, usei uma opção desconhecida para rand . Que deu um erro ao stderr. Para capturá-lo, eu o redireciono para um arquivo temporário. Então eu uso o cat para mostrar o conteúdo do arquivo e canalizá-lo para ts , adicionar um formato de hora que encontrei neste post e finalmente logar no arquivo de erro. Então eu limpo o conteúdo do arquivo temporário, senão recebo entradas duplas para o mesmo erro.

Crontab:

* * * * * /home/monusr/bin/pushmonstats.sh 1>> /home/monusr/pushmonstats.log 2> /home/monusr/.err;/bin/cat /home/monusr/.err|/usr/bin/ts %F-%H:%M:%.S 1>> /home/monusr/pushmonstats.err;> /home/monusr/.err

Isso fornece o seguinte no meu log de erros:

2014-03-22-19:17:53.823720 rand: unknown option -- '-l'

Talvez isso não seja uma maneira muito elegante de fazer isso, mas funciona. Eu me pergunto se há uma abordagem mais elegante para isso.

    
por 22.03.2014 / 19:57

Tags