O caminho geral
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
Como funciona:
-
cat
lê o arquivo chamadoinput.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 decat
para a entrada padrão desed
. -
sed
lê dados (comocat
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 comandodate -R
(o comando de construção geral para substituição é:s/pattern/replace/
). -
Em seguida, de acordo com
>>
bash
redireciona a saída desed
para um arquivo chamadooutput.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:
-
mkfifo
cria um canal nomeado -
while true; do sed ... ; done
executa um loop infinito e, a cada iteração, executased
com o redirecionamento defoo.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 parafoo.log
.Neste ponto, você precisa abrir uma nova janela de terminal, porque o loop ocupa o terminal atual.
-
echo ... > foo.log.fifo
imprime uma mensagem para sua saída padrão redirecionada para o arquivo fifo esed
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:
-
Execute o processo
tailf
que seguirá as gravações parabar.raw.log
e imprima-as na saída padrão redirecionada para o loopwhile read ... echo
infinito. Esse loop executa duas ações: ler dados da entrada padrão para uma variável de buffer chamadaline
e, em seguida, gravar o registro de data e hora gerado com os seguintes dados em buffer nobar.log
. -
Escreva algumas mensagens para o
bar.raw.log
. Você tem que fazer isso em uma janela de terminal separada, porque a primeira será ocupada portailf
, 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.