Регулярное выражение perl для поиска Java StackTrace по ключевому слову - PullRequest
4 голосов
/ 18 октября 2011

Мне нужно получить полную трассировку стека из лог-файла по ключевому слову.

Этот код работает нормально, но медленнее на больших файлах (чем файл, тем медленнее). Я думаю, что лучший способ улучшить регулярные выражения, чтобы найти ключевое слово, но я не мог сделать это.


#!/usr/bin/perl

use strict;
use warnings;

my $regexp;
my $stacktrace;
undef $/;

$regexp = shift;
$regexp = quotemeta($regexp);

while (<>) {
  while ( $_ =~ /(?<LEVEL>^[E|W|D|I])\s
                 (?<TIMESTAMP>\d{6}\s\d{6}\.\d{3})\s
                 (?<THREAD>.*?)\/
                 (?<CLASS>.*?)\s-\s
                 (?<MESSAGE>.*?[\r|\n](?=^[[E|W|D|I]\s\d{6}\s\d{6}\.\d{3}]?))/gsmx ) {
    $stacktrace = $&;
    if ( $+{MESSAGE} =~ /$regexp/ ) {
      print "$stacktrace";
    }
  }
}

Использование: ./grep_log4j.pl <pattern> <file>

Пример: ./grep_log4j.pl Exception sample.log

Я думаю, проблема в $stacktrace = $&;, потому что, если удалить эту строку и просто напечатать все соответствующие строки, скрипт работает быстро. Версия скрипта для печати всех совпадений:

#!/usr/bin/perl

use strict;
use warnings;

undef $/;

while (<>) {
  while ( $_ =~ /(?<LEVEL>^[E|W|D|I])\s
                 (?<TIMESTAMP>\d{6}\s\d{6}\.\d{3})\s
                 (?<THREAD>.*?)\/
                 (?<CLASS>.*?)\s-\s
                 (?<MESSAGE>.*?[\r|\n](?=^[[E|W|D|I]\s\d{6}\s\d{6}\.\d{3}]?))/gsmx ) {
    print_result();
  }
}

sub print_result {
    print "LEVEL: $+{LEVEL}\n";
    print "TIMESTAMP: $+{TIMESTAMP}\n";
    print "THREAD: $+{THREAD}\n";
    print "CLASS: $+{CLASS}\n";
    print "MESSAGE: $+{MESSAGE}\n";
}

Использование: ./grep_log4j.pl <file>

Пример: ./grep_log4j.pl sample.log

Шаблон Lo4j: %-1p %d %t/%c{1} - %m%n

Пример файла журнала:

I 111012 141506.000 thread/class - Received message: something
E 111012 141606.000 thread/class - Failed handling mobile request
java.lang.NullPointerException
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
  at java.lang.Thread.run(Thread.java:619)
W 111012 141706.000 thread/class - Received message: something
E 111012 141806.000 thread/class - Failed with Exception
java.lang.NullPointerException
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
  at java.lang.Thread.run(Thread.java:619)
D 111012 141906.000 thread/class - Received message: something
S 111012 142006.000 thread/class - Received message: something
I 111012 142106.000 thread/class - Received message: something
I 111013 142206.000 thread/class - Metrics:0/1

Мое регулярное выражение вы можете найти в http://gskinner.com/RegExr/ по ключевому слову log4j:

Ответы [ 2 ]

1 голос
/ 18 октября 2011

Вы используете:

$/ = undef;

Это заставляет perl считывать весь файл в память.

Я бы построчно обработал этот файл следующим образом (предполагая, что трассировка стекасвязан с сообщением над трассировкой):

my $matched;
while (<>) {
  if (m/^(?<LEVEL>\S+) \s+ (?<TIMESTAMP>(\d+) \s+ ([\d.])+) \s+ (?<THREADCLASS>\S+) \s+ - \s+ (?<REST>.*)/x) {
    my %captures = %+;
    $matched = ($+{REST} =~ $regexp);
    if ($matched) {
      print "LEVEL: $captures{LEVEL}\n";
      ...
    }
  } elsif ($matched) {
    print;
  }
}

Вот общий метод анализа многострочных блоков.Следующий цикл читает STDIN по одной строке за раз и передает полные блоки файла журнала в подпрограмму process:

my $first;
my $stack = "";
while (<STDIN>) {
  if (m/^\S /) {
    process($first, $stack) if $first;
    $first = $_;
    $stack = "";
  } else {
    $stack .= $_;
  }
}
process($first, $stack) if $first;

sub process {
  my ($first, $stack) = @_;
  # ... do whatever you want here ...
}
0 голосов
/ 18 октября 2011

Проблема в неправильном использовании [] в вашем регулярном выражении.

[...] для определения классов символов

(...) для группировки

Все, что вам нужно, это изменить [E|W|D|I] на [EWDI] везде и не использовать [] для группировки в MESSAGE.

Вот последний код, который работает для меня:

#!/usr/bin/perl

use strict;
use warnings;

undef $/;

while (<>) {
    while (
        $_ =~ /(?<LEVEL>^[EWDIS])\s
                 (?<TIMESTAMP>\d{6}\s\d{6}\.\d{3})\s
                 (?<THREAD>.*?)\/
                 (?<CLASS>.*?)\s-\s
                 (?<MESSAGE>.*?[\r\n](?=[EWDIS]\s\d{6}\s\d{6}\.\d{3}|$))/gmxs
      )
    {
        print_result();
    }
}

sub print_result {
    print "LEVEL: $+{LEVEL}\n";
    print "TIMESTAMP: $+{TIMESTAMP}\n";
    print "THREAD: $+{THREAD}\n";
    print "CLASS: $+{CLASS}\n";
    print "MESSAGE: $+{MESSAGE}\n";
}

Обратите внимание, что в списке флагов вы пропустили букву 'S'.

Этот пример также может содержать ошибки, ноэто работает в общем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...