Regex 如何从日志文件中过滤错误,但过滤错误警报?

Regex 如何从日志文件中过滤错误,但过滤错误警报?,regex,logging,grep,pcre,Regex,Logging,Grep,Pcre,目标是获得如下输出,其中包含每个文件的文件名和错误计数 /var/log/file2.log:5 /var/log/file3.log:7 /var/log/file5.log:3 /var/log/file7.log:2 假设有许多名为“*.log”的日志文件,并且所有文件的格式如下所示 2021-03-16 01:01:01.001 ERROR 1234 --- [main] com.example.ClassAAAA : real error 2021-03-16 01:01:

目标是获得如下输出,其中包含每个文件的文件名和错误计数

/var/log/file2.log:5
/var/log/file3.log:7
/var/log/file5.log:3
/var/log/file7.log:2
假设有许多名为“
*.log
”的日志文件,并且所有文件的格式如下所示

2021-03-16 01:01:01.001 ERROR 1234 --- [main] com.example.ClassAAAA      : real error
2021-03-16 01:01:01.001  INFO 1234 --- [main] com.example.ClassAAAA      : message
2021-03-16 01:01:01.001 ERROR 1234 --- [main] com.example.LibBB          : real error
2021-03-16 01:01:01.001  INFO 1234 --- [main] com.example.LibBB          : message
2021-03-16 01:01:01.001 ERROR 1234 --- [main] com.example.ClassCCCCCC    : real error
2021-03-16 01:01:01.001 ERROR 1234 --- [main] com.example.ClassCCCCCC    : real error
2021-03-16 01:01:01.001  INFO 1234 --- [main] com.example.ClassCCCCCC    : message
2021-03-16 01:01:01.001 ERROR 1234 --- [main] com.example.ClassD         : false alert
2021-03-16 01:01:01.001  INFO 1234 --- [main] com.example.ClassD         : message
2021-03-16 01:01:01.001 ERROR 1234 --- [main] com.example.LibEEE         : false alert
2021-03-16 01:01:01.001  INFO 1234 --- [main] com.example.LibEEE         : message
2021-03-16 01:01:01.001 ERROR 1234 --- [main] com.example.LibFFFFF       : false alert
2021-03-16 01:01:01.001 ERROR 1234 --- [main] com.example.LibFFFFF       : false alert
2021-03-16 01:01:01.001  INFO 1234 --- [main] com.example.LibFFFFF       : message
主要问题是,存在一些已知的错误警报,这些警报不应被计算在内

例如,我们希望通过以下命令统计除
ClassD
LibEEE
LibFFFFF
以外的任何类记录的错误

grep -Pc '^.* ERROR .*((?<!(ClassD|LibEEE|LibFFFFF)).)*$' *.log | grep -v :0
如果有效,则上述示例日志段的计数将为4,分别由
ClassAAAA
LibBB
classcccc
记录

更新 衷心感谢大家抽出时间分享您的想法。我之所以进入lookbehind的think集合,是因为我想先用“
\s*:
”来限定类名,但最终还是忽略了空格和冒号


虽然目标现在可以实现,但好奇的是-在这种情况下可以使用lookback吗?

这应该是awk的一项任务,请您尝试以下内容。 使用GNU
awk
中显示的样本编写和测试。您可以将多个文件传递给此命令,它将逐个文件打印(文件名和错误计数与所示示例相同)

说明:添加上述内容的详细说明

awk '                        ##Starting awk program from here.
BEGIN{ OFS=":" }             ##Setting OGFS to colon in BEGIN section of this program.
FNR==1{                      ##Checking condition if this is first line then do following.
  if(count){                 ##Checking if count is not null then.
    print FILENAME,count     ##Printing file name with OFS and count value here.
  }
  count=0                    ##Setting count to 0 here.
}
/ERROR/ && !/ClassD|LibEEE|LibFFFFF/{  ##checking condition if line does not contain ClassD, LibEEE and LibFFFFF then do following.
  count++                    ##Increase count with 1 here.
}
END{                         ##Starting END block of this program from here.
  if(count){                 ##Checking if count is not null then.
    print FILENAME,count     ##Printing file name with OFS and count value here.
  }
}
'  *.log                     ##Passing all .log files to program here.

在PCRE regex lookbehind中,顶层只能有不同长度的模式:
(?可以,但
(?不可以,因为替换在捕获组中

实际上,在这里,您可以使用前瞻,其中备选方案的长度无关紧要:

grep-Pc'\sERROR(?)*(ClassD | LibEEE | LibFFFFF))\s'| grep-v:0

这个图案很相配

  • \s
    -任何空白
  • 错误
    -子字符串
  • (?!.*(ClassD | LibEEE | LibFFFFF))
    -一种负前瞻,它尽可能多地匹配除换行符以外的任何零个或多个字符,后跟组中的一个单词
  • \s
    -任何空白

要打印正确的文件名:

awk '
FNR==1 {
  if (c)
    print f,c
  c=0
  f=FILENAME
}
$3=="ERROR" && $7!~/ClassD|LibEEE|LibFFFFF/ {
  ++c
}
END {if (c) print f,c}
' *.log
或键入更少但占用更多内存:

awk '
$3=="ERROR" && $7!~/ClassD|LibEEE|LibFFFFF/ {
  ++c[FILENAME]
}
END {for (f in c) print f,c[f]}
' *.log

使用GNU awk作为结束文件:

$ awk '
    ($3=="ERROR") && $7 !~ /\.(ClassD|Lib(EEE|FFFFF))$/ { c++ }
    ENDFILE { printf "%s:%d\n", FILENAME, c; c=0 }
' file*
file:4

预期的匹配是什么?为什么不使用负前瞻而不是负前瞻?那么为什么使用前瞻?使用前瞻:
grep-Pc'\sERROR(?。*(ClassD | LibEEE | LibFFFFF))\s'| grep-v:0
look-behind交替应该具有相同的长度。如果您可以只使用
LibFFF
而不是
LibFFF
来处理,那么
^.*ERROR.*(?就可以了。您询问是否可以使用lookbehind。是的,但在像您这样的情况下,使用regex时,lookahead更自然。
awk '
$3=="ERROR" && $7!~/ClassD|LibEEE|LibFFFFF/ {
  ++c[FILENAME]
}
END {for (f in c) print f,c[f]}
' *.log
$ awk '
    ($3=="ERROR") && $7 !~ /\.(ClassD|Lib(EEE|FFFFF))$/ { c++ }
    ENDFILE { printf "%s:%d\n", FILENAME, c; c=0 }
' file*
file:4