Receba periodicamente novas linhas do arquivo, possivelmente logado

2

Estou procurando uma maneira bastante simples (sem desenvolvimento envolvido - eu poderia escrever isso em Python, mas espero que já exista algo por aí).

Eu tenho um arquivo de log (no meu caso, escrito por rsyslogd). Para fins de análise, quero lê-lo a cada 1 minuto e calcular métricas para o último minuto. quantos page hits meu servidor http teve. Meus 2 requisitos:

1) Eu só quero olhar para as linhas que foram adicionadas desde a última vez que li o arquivo. (Eu só preciso do último minuto ou mais, e o arquivo é muito grande para reler e filtrar a cada minuto).

2) Uma vez por dia, o arquivo é logrorado. A primeira vez após a rotação do log eu quero todas as linhas do arquivo anterior que eu ainda não li, além de todas as linhas do novo arquivo.

Suponho que agora sou o único com tais requisitos - o que os outros fazem?

    
por Nitzan Shaked 14.08.2013 / 13:22

1 resposta

2

Assumindo que your-filter lê seus dados de stdin:

while your-filter; do
  sleep 60
done < file.log

Isso pressupõe que your-filter apenas leia os dados e não tente lseek , por exemplo.

Agora, para resolver o problema de rotação de log, se no Linux (onde, ao contrário da maioria dos outros sistemas, /dev/fd/n são links simbólicos para os arquivos reais), com ksh , bash , zsh , dash , yash (a maioria das shells POSIX, exceto as mais pedantemente POSIX, como posh as -ef não é POSIX):

while your-filter; do
  if [ file.log -ef /dev/stdin ]; then
    sleep 60
  else
    exec < file.log
  fi
done < file.log

Após a rotação do log, isso chamaria your-filter duas vezes, se você preferir que ele seja chamado uma vez com a concatenação do antigo e do novo:

while 
  if [ file.log -ef /dev/stdin ]; then
    your-filter
  else
    exec 3<&0 < file.log
    (cat <&3; cat) | your-filter &&
      exec 3<&-
  fi
do
  sleep 60
done < file.log

Agora, após a rotação do log, pode haver um momento em que o arquivo file.log antigo foi renomeado, mas o novo file.log ainda não foi criado. Nesse caso, o acima falhará se usar o exec < file.log nesse muito momento. Então você poderia consertar isso com:

while 
  if [ file.log -ef /dev/stdin ] || ! command exec 3< file.log; then
    your-filter
  else
    (cat; cat <&3) | your-filter &&
      exec <&3 3<&-
  fi
do
  sleep 60
done < file.log

Então, ele continua lendo o arquivo antigo até que o novo seja exibido.

command é necessário para evitar que exec faça o shell sair quando ele falhar (como requer o POSIX). Não é necessário com zsh ou bash quando não estiver no modo sh .

Agora, dormimos por 60 segundos no loop e your-filter pode levar alguns segundos para ser executado. Se for importante que your-filter seja executado a cada minuto, em média, com ksh , bash ou zsh , você poderá alterá-lo para:

t=$SECONDS
while 
  if [ file.log -ef /dev/stdin ] || ! command exec 3< file.log; then
    your-filter
  else
    (cat; cat <&3) | your-filter &&
      exec <&3 3<&-
  fi
do
  t=$(($t + 60))
  sleep "$((t - SECONDS))"
done < file.log

Com ksh93 e zsh e desde que seu sleep aceite argumentos de ponto flutuante, você pode executar typeset -F SECONDS .

    
por 14.08.2013 / 13:55