2011-10-18 51 views
4

我需要通過關鍵字從logfile中grep完整的stacktrace。perl通過關鍵字尋找Java StackTrace的正則表達式關鍵字

此代碼工作正常,但在大文件上減慢(超過文件慢)。 我認爲提高正則表達式以找到關鍵字的最好方法,但我無法完成。


#!/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 

我的正則表達式,你可以通過關鍵字的log4j上找到http://gskinner.com/RegExr/

+1

一般而言,您應該避免使用'$&' - 請參閱'perldoc perlre'中的'WARNING'。在您的程序中使用它會導致速度損失。 – ErikR

回答

1

您正在使用:

$/ = 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

感謝您的回答。我知道如果我使用'$/= undef;'我把文件讀入內存,而不是嚇人我:)否則,如果逐行讀取文件我怎麼能找到匹配關鍵字在第二行和更多? – Gofrolist

+0

例如:我需要通過關鍵字'java.lang.Thread.run'找到日誌文件中的所有堆棧跟蹤。 第二種說法:在你的例子中,代碼首先匹配字符串並使用命名組,但下一行如果爲true,則使用另一個regexp'$ matched =($ + {REST} =〜$ regexp);'並且該字符串重寫所有先前的命名組。正因爲如此,我在我的第一個代碼示例中使用'$ stacktrace = $&;'。 – Gofrolist

+0

覆蓋'%+'的好習慣 - 我會修正這個例子。 – ErikR

0

的問題是在你的正則表達式濫用[]

[...]是定義character classes

(...)是分組

所有你需要的是改變[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」字母,在標誌列表。

這個例子也可能包含錯誤,但它一般工作。

+0

感謝您糾正我的正則表達式。 但我的問題不在這裏。這個代碼示例打印所有日誌消息,但下一步我需要找到每個包含我的關鍵字的日誌消息。例如:從包含'java.lang.Thread.run'的sampple日誌文件中打印兩條日誌消息。 – Gofrolist

+0

您可以通過兩種方式實現此目的:1.在while()循環中檢查$ + {MESSAGE}以包含所需的字符串。由於您使用的是/ g,所以不會花費太多 2.將'。*'改爲' [^ \ n] *?java \ .lang.Thread.run'。也應該工作。 此外,我會將這個太大而困難的正則表達式分爲兩部分:本身之前的所有內容,並連續使用它。 – yko

相關問題