Filtrando várias linhas de um log

4

Esta pergunta deve ser movida para stackoverflow?

Muitas vezes preciso ler arquivos de log gerados por aplicativos java usando log4j. Normalmente, uma mensagem registrada (vamos chamá-lo de uma entrada de log) se estende por várias linhas. Exemplo:

INFO  10:57:01.123 [Thread-1] [Logger1] This is a multi-line
text, two lines
DEBUG 10:57:01.234 [Thread-1] [Logger2] This entry takes 3 lines
line 2
line 3

Observe que cada entrada de log inicia em uma nova linha e a primeira palavra da linha é TRACE, DEBUG, INFO ou ERROR e pelo menos um espaço. Aqui, há 2 entradas de log, a primeira em milissegundos 123, a outra em milissegundos 234.

Eu gostaria de um comando rápido (usando uma combinação de sed / grep / awk / etc) para filtrar entradas de log (grep apenas filtra linhas), por exemplo: remova todas as entradas de log que contenham o texto 'Logger2'. br> Eu considerei fazer as seguintes transformações:

1) junte linhas pertencentes às mesmas entradas de log com uma seqüência especial de caracteres (por exemplo: ##); Desta forma, todas as entradas de log terão exatamente uma linha

INFO  10:57:01.123 [Thread-1] [Logger1] This is a multi-line##text, two lines
DEBUG 10:57:01.234 [Thread-1] [Logger2] This entry takes 3 lines##line 2##line 3

2) grep
3) divida as linhas de volta (ou seja: substitua ## por \ n)

Eu tive problemas na etapa 1 - não tenho experiência suficiente com sed.

Talvez os 3 passos acima não sejam necessários, talvez o sed possa fazer todo o trabalho.

    
por botismarius 18.04.2015 / 10:10

3 respostas

5

Não há necessidade de misturar muitos instrumentos. Tarefa pode ser feita por sed only

sed '/^INFO\|^DEBUG\|^TRACE\|^ERROR/{
         /Logger2/{
             :1
             N
             /\nINFO\|\nDEBUG\|\nTRACE\|\nERROR/!s/\n//
             $!t1
             D     }
                                    }' log.entry
    
por 18.04.2015 / 11:33
2

perl filter para registros de log de várias linhas (record begin mark)

Use o seguinte script perl como um protótipo de trabalho.
Uso script_path regular_expression log_files
por exemplo. script_path "line \d" log_file_1 log_file_2

#!/usr/bin/perl
$pattern = qr/(?^s)$ARGV[0]/; shift; # process filtering expression
# (?^s) - treats matched string as single line
my $line = ''; # accumulates current log file record/paragraph
while(<>) {
 if( /^(TRACE|DEBUG|INFO|ERROR) /o ) { # start of new record
   &flush; # flush/print previous recors
 }
 $line.=$_;
}
&flush;
exit;

sub flush {
  local $_ = $line;
  if( length($_) and /$pattern/ ) {
    print;
  }
  $line = '';
}
    
por 18.04.2015 / 11:17
2

Com base em uma resposta no link , isso parece se encaixar na fatura

#!/usr/local/bin/bash

PATTERN1='TRACE *';
PATTERN2='DEBUG *';
PATTERN3='INFO *';
PATTERN4='ERROR *';
LINEOUT=""
while read line; do
    case $line in
        $PATTERN1)
                echo $LINEOUT
                LINEOUT="$line"
                        ;;
        $PATTERN2)
                echo $LINEOUT
                LINEOUT="$line"
                        ;;
        $PATTERN3)
                echo $LINEOUT
                LINEOUT="$line"
                        ;;
        $PATTERN4)
                echo $LINEOUT
                LINEOUT="$line"
                        ;;
        "")
                LINEOUT=""
                ;;

        *)      LINEOUT="$LINEOUT ## $line"
                ;;
    esac        
done
echo $LINEOUT

NB: Isso adicionará um espaço em branco no início da saída

    
por 18.04.2015 / 11:24