iniciando da última linha processada ao processar o arquivo de log várias vezes

2

Estou projetando um sistema de registro e geração de relatórios para um aplicativo da Web.
Dentro do aplicativo da web, alguns eventos podem acontecer ("usuário logado", "ação realizada pelo usuário X", etc.).
Eu gostaria que o aplicativo registrasse esses eventos em um arquivo de log simples.
Então eu gostaria de escrever outro programa que periodicamente seria executado e extrair dados desse arquivo para manter um relatório agregado em execução em uma tabela de banco de dados (como "quantos eventos do tipo X acontecem por hora", ou "obter o total diário de evento X ").

Um dos desafios é garantir que o programa de relatórios não processe a mesma linha duas vezes.

Existe uma maneira Unix de projetar esse tipo de sistema ou lidar com o tipo de problema "rastrear linhas já processadas"?

Já pensei em rotacionar os logs antes de processá-los e atribuir números de linha exclusivos a cada linha, mas os dois parecem ser hacky.

Obrigado.

    
por Pierre 19.04.2011 / 00:42

1 resposta

2

Girar os logs antes de analisá-los parece uma boa ideia, desde que seus aplicativos não mantenham o arquivo de log aberto permanentemente. Se eles fizerem isso, girá-los não funcionará - mas, como você está escrevendo a estrutura de logging, você pode lidar com isso.

Se você quer um script simples, pode usar algo como o seguinte (pode ser feito em praticamente qualquer linguagem de script):

#! /bin/bash

process_line() {
    # do the work here
    echo "== $1 =="
}

logfile=$1
statefile=${logfile}.state

if [ -f ${statefile} ] ; then
    processed=$(cat $statefile)
else
    processed=0
fi

curline=0
IFS='
'

while read line ; do
    if [ $curline -ge $processed ] ; then
        echo processing $line
        process_line "$line"
    fi
    curline=$(($curline+1))
done < ${logfile}

echo $curline > $statefile

Basicamente, ele salva onde processou a entrada em um arquivo separado ( $statefile ) e processa a entrada linha por linha a partir desse ponto (ignorando os já processados).

Isso exigiria um pouco mais de tratamento de erros, obviamente, e se a entrada for grande, não é ideal. (Pode ser melhor salvando um byte offset e procurando, ou usando dd bs=1 skip=$already_read count=$(($size-$already_read)) para canalizar para outro processo em vez de fazer as coisas linha por linha, mas eu usaria perl se esse tipo de otimização fosse necessário.)

Como está, o script processará as linhas duas vezes se for interrompido. Você pode limitar a quantidade de "replay" atualizando o arquivo de estado após cada linha, em vez de uma vez no final.

Se você processar os logs e rotacioná-los, precisará ter cuidado com esses arquivos de estado. Eles precisariam ser girados também, e o script é executado uma vez após a rotação para processar as últimas linhas de saída.

Há uma coisa que esta abordagem não lida facilmente: linhas parciais. Se o aplicativo estiver sendo gravado enquanto o script é executado, há uma chance de o script ver uma última linha parcial. Não será capaz de dizer a diferença, por isso vai gravar como tendo sido processado. (Esse é um problema com o qual você precisará lidar com praticamente qualquer abordagem).

Isso pode ser evitado com um marcador EOL no formato de arquivo de log e verificando isso antes de processar a linha. Mas isso não é muito bonito.

Em vez de fazer o processamento em bash no próprio script, você poderia usá-lo assim (substitua process_line por echo ):

$ ./logger /var/log/app12.log | ./analyzer --logtype=app12

./analyzer obterá os dados como entrada.

    
por 19.04.2011 / 09:02