1

我正在编写一个 node.js 函数来 ssh 到远程机器,并尝试从各种不同的日志文件中抓取日志以查找异常。日志文件的重要部分如下所示:

.... gunk ....



2013-01-29 04:06:39,133 com.blahblah.BaseServlet processRequest Thread-1629  Site-102 Cons-0 Url-http://theurlthat.com/caused/the/problem App-26 yada yada yada

java.lang.NullPointerException
        at com.blahblah.MyClass.hi(MyClass.java:173)
        at com.blahblah.StandardStackTrace.main(StandardStackTrace.java:125)
        at com.blahblah.SoOnAndSo.forth(SoOnAndSo.java:109)
        at java.lang.Thread.run(Thread.java:595)


2013-01-29 04:06:39,133 com.blahblah.BaseServlet defaultThrowableHandler Thread-1629  Site-102 Cons-0 Url-http://theurlthat.com/caused/the/problem App-26 yad yada yada

TechnicalDifficultiesException: TD page delivered by handleThrowable
http://theurlthat.com/caused/the/problem


....more gunk....

我需要在日志文件中找到满足以下三个要求的异常和对应的日期:

  1. 例外必须是此静态文本之前的第一个:

    TechnicalDifficultiesException:handleThrowable 传递的 TD 页面

  2. 异常必须直接位于具有“BaseServlet.*Site-102”的两行之间

  3. 异常必须是满足上述条件的日志文件中最近的(最后一个)。日志会定期翻转,因此它必须是 Log 中的最后一个,或者如果不存在则 Log.001 中的最后一个,或者如果不存在则 Log.002 中的最后一个等。

由于该程序必须通过 ssh 连接到许多潜在服务器之一,因此最好只在 node.js 程序中维护逻辑,而不是在有日志的机器上维护。因此,perl/sed/awk/grep/etc 中的单行将是最理想的。

4

1 回答 1

2

所以你的问题看起来像这样,如果我理解正确的话:

  • 日志文件有许多由双换行符分隔的部分。
  • 每个都以带有日期等的行开头。
  • 我们只对标题匹配的部分感兴趣/BaseServlet.*?Site-102/
  • 如果一个节的主体匹配/^TechnicalDifficultiesException: TD page delivered by handleThrowable/,我们想要选择先前匹配的节的主体,我们应该验证它看起来像一个 java 异常。
  • 我们处理整个日志文件,并返回以这种方式找到的最后一个异常。

很公平。

#!/usr/bin/perl
use strict; use warnings;
local $/ = ""; # paragraph mode
my ($prev_sec, $prev_err);
SECTION:
while (my $head = <>) {
  my $body = <>;
  defined $body or die "Can't read from empty filehandle.";
  next SECTION unless $head =~ /BaseServlet.*?Site-102/;
  if ($body =~ /^TechnicalDifficultiesException: TD page delivered by handleThrowable/) {
    $prev_err = $prev_sec;
  }
  $prev_sec = $body;
}
die "No error found" unless defined $prev_err;
print $prev_err;

(没有真正测试那么多,但会从您的代码段中打印出错误)

对于单行代码来说,代码有点长。如果您愿意,您可以随时将源代码通过管道传输到 perl 解释器中。

perl -ne'BEGIN{$/=""}END{print$prev_err}$b=<>;defined$b or die"empty FH";/BaseServlet.*?Site-102/ or next;$prev_err=$prev_sec if $b=~/^TechnicalDifficultiesException: TD page delivered by handleThrowable/;$prev_sec=$b'

将日志文件指定为命令行参数,或将文件内容直接通过管道传输到该程序中。找到正确的日志文件并不难。在 Perl 的一个片段中:

my $log_dir = ...;
my ($log) = sort glob "$log_dir/LOG*";
die "no log in $log_dir" unless defined $log;

更新

如果还应捕获日期,则代码将更改为

#!/usr/bin/perl
use strict; use warnings;
local $/ = ""; # paragraph mode
my (@prev, @prev_err);
SECTION:
while (my $head = <>) {
  my $body = <>;
  defined $body or die "Can't read from empty filehandle.";
  next SECTION unless $head =~ /BaseServlet.*?Site-102/;
  if ($body =~ /^TechnicalDifficultiesException: TD page delivered by handleThrowable/) {
    @prev_err = @prev;
  }
  @prev = ($head, $body);
}
die "No error found" unless @prev_err;
my ($date) = $prev_err[0] =~ /^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d),/;
print "$date\n\n$prev_err[1]";

作为单线:

perl -ne'BEGIN{$/=""}END{@perr||die"No error found";($date)=$perr[0]=~/^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d),/;print"$date\n\n$perr[1]"}$b=<>;defined$b or die"empty FH";/BaseServlet.*?Site-102/ or next;@perr=@p if $b=~/^TechnicalDifficultiesException: TD page delivered by handleThrowable/;@p=($_,$b)'

我不明白它怎么只能返回第一场比赛;此代码应处理整个文件。如果您可以提供更完整的测试用例,我可以验证此代码是否按要求工作。

于 2013-02-04T22:59:50.523 回答