如何使用awk轻松过滤日志? [英] How to filter logs easily with awk?

查看:125
本文介绍了如何使用awk轻松过滤日志?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个日志文件 mylog 是这样的:

  [01 /月/ 2015:16:12:56 +0200]错误号1
[01 /月/ 2015:17:12:56 +0200]错误号2
[01 /月/ 2015:18:07:56 +0200]错误号3
[01 /月/ 2015:18:12:56 +0200]错误号4
[02 /月/ 2015:16:12:56 +0200]错误号5
[10 /八/ 2015:16:12:58 +0200]错误号6
[10 /八/ 2015:16:13:00 +0200]错误号7
[01 /十一月/ 2015:00:10:00 +0200]错误号8
[01 /十一月/ 2015:01:02:00 +0200]错误号9
[01/1月/ 2016:01:02:00 +0200]错误号10

和我想找到那些在18.00和10月1日之间发生的1.00 11月1日的行。也就是说,预期输出是:

  [01 /月/ 2015:18:07:56 +0200]错误号3
[01 /月/ 2015:18:12:56 +0200]错误号4
[02 /月/ 2015:16:12:56 +0200]错误号5
[10 /八/ 2015:16:12:58 +0200]错误号6
[10 /八/ 2015:16:13:00 +0200]错误号7
[01 /十一月/ 2015:00:10:00 +0200]错误号8

我已成功时代转换为时间戳使用 匹配() 然后的 mktime()根据 。第一个找到指定模式,存储阵列中的 A [] ,因此它可以被访问(有趣的,看看格伦·杰克曼的答案的access从线条图案捕获组一个很好的例子的)。由于 mktime 要求的格式 YYYY MM DD HH MM SS [DST] ,我也有一个月转换中表格的xxx 成一个数字,为​​此我使用由爱德·莫顿向答案本月转换从AAA降至XX的awk'{printf的%02D \\ N(匹配(JanFebMarAprMayJunJulAugSepOctNovDec,$ 0)+2)/ 3}

总之,我终于有变量中的时间戳 mytimestamp

 的awk'匹配($ 0 /([0-9] +)\\ /([AZ] [AZ] {2})\\ /([0-9] {4 })([0-9] {1,2})([0-9] {1,2})([0-9] {1,2})([+  - ] [0-9 ] {4})/一个){
        天=一[1];一个月=一个[2];一年=一个[3];
        小时=一个[4]; MIN =一个[5];秒=一个[6]; UTC =一个[7];
        月= sprintf的(%02D(匹配(JanFebMarAprMayJunJulAugSepOctNovDec,月)+2)/ 3);
        数值指明MyDate = sprintf的(%s%s%S%s%S%s%S,年,月,日,时,分,秒,UTC);
        mytimestamp = mktime(数值指明MyDate)
        打印mytimestamp
    }'mylog

返回:

  1443708776
1443712376
1443715676

等。

所以,现在我已经准备好对给定的日期转换。由于 AWK 需要大量的处理这种格式,我preFER通过外部shell变量为他们提供,使用日期-D我的约会+%S打印时间戳:

 开始=$(日期-D2015年10月1日18:00 +0200+%S)
最终=$(日期-D2015年11月1日01:00 +0200+%S)

总之,这个作品:

  AWK开始=​​$(日期-D2015年10月1日18时00分+0200+%S)结束=$(日期-D2015年11月1日1点+0200+%S)比赛($ 0 /([0-9] +)\\ /([AZ] [AZ] {2})\\ /([0-9] {4 })([0-9] {1,2})([0-9] {1,2})([0-9] {1,2})([+  - ] [0-9 ] {4})/一个){天=一个[1];一个月=一个[2];一年=一个[3];小时=一个[4]; MIN =一个[5];秒=一个[6]; UTC =一个[7];月= sprintf的(%02D(匹配(JanFebMarAprMayJunJulAugSepOctNovDec,月)+2)/ 3);数值指明MyDate = sprintf的(%s%s%S%s%S%s%S,年,月,日,时,分,秒,UTC); mytimestamp = mktime(数值指明MyDate);如果(开始< = mytimestamp和放大器;&安培; mytimestamp< =结束)打印}'mylog
[01 /月/ 2015:18:07:56 +0200]错误号3
[01 /月/ 2015:18:12:56 +0200]错误号4
[02 /月/ 2015:16:12:56 +0200]错误号5
[10 /八/ 2015:16:12:58 +0200]错误号6
[10 /八/ 2015:16:13:00 +0200]错误号7
[01 /十一月/ 2015:00:10:00 +0200]错误号8

不过,这似乎是一件应该更加直截了当相当多的工作。然而,在男人GAWK 引入时间功能部分是


  

由于AWK程序的主要用途之一是处理日志文件
  包含时间戳信息,GAWK提供了以下
  函数获取时间戳和格式它们。


所以我想:有没有更好的方式来做到这一点?例如,如果在格式而不是日/月/ YYYY:HH:MM:SS 是像 DD嗯YYYY HH:MM:SS ?无法才有可能提供匹配模式外,而不必改变它的每一个会发生这种情况的时间?难道我真的要使用匹配(),然后将该输出处理再喂 mktime()根据?不 GAWK 提供了更简单的方法来做到这一点?


解决方案

使用ISO 8601时间格式!


  

不过,这似乎是一件应该更加直截了当相当多的工作。


是的,这应该是简单的,为什么它不是,原因是因为日志没有使用 ISO 8601 。应用程序日志应使用ISO格式和UTC来显示时间,其它设置应考虑破损和固定。

您请求应该分为两部分进行分割。第一部分将封为圣人日志,转换日期ISO格式,第二个进行研究:

 的awk'
比赛($ 0 /([0-9] +)\\ /([AZ] [AZ] {2})\\ /([0-9] {4})([0-9] {1,2} ):([0-9] {1,2}):([0-9] {1,2})([+ - ] [0-9] {4})/一个){
  天= A [1]
  一个月=一个[2];
  年= A [3]
  小时=一个[4]
  MIN = A [5]
  秒=一个[6]
  UTC =一个[7];
  月= sprintf的(%02D(匹配(JanFebMarAprMayJunJulAugSepOctNovDec,月)+2)/ 3);
  myisodate = sprintf的(%4D条所2D-%2DT%2D%:%2D:2D%%6S,年,月,日,时,分,秒,UTC);
 $ 1 = myisodate
 打印
}'mylog

有关ISO 8601日期的好处 - 除了他们是一个的标准的 - 是的时间顺序与字典顺序相一致,因此,您可以使用 / ... /, / ...... / 运算符来提取您感兴趣的日期,例如找到的 2015年10月1日18:00 +0200 2015年11月1日01之间发生了什么:00 +0200 的,附加以下过滤到previous,规范过滤器:

 的awk'/ 2015年10月1日:18:00:00 + 0200 /,/ 2015年11月1日:01:00:00 + 0200 /'

Suppose I have a log file mylog like this:

[01/Oct/2015:16:12:56 +0200] error number 1
[01/Oct/2015:17:12:56 +0200] error number 2
[01/Oct/2015:18:07:56 +0200] error number 3
[01/Oct/2015:18:12:56 +0200] error number 4
[02/Oct/2015:16:12:56 +0200] error number 5
[10/Oct/2015:16:12:58 +0200] error number 6
[10/Oct/2015:16:13:00 +0200] error number 7
[01/Nov/2015:00:10:00 +0200] error number 8
[01/Nov/2015:01:02:00 +0200] error number 9
[01/Jan/2016:01:02:00 +0200] error number 10

And I want to find those lines that occur between 1 Oct at 18.00 and 1 Nov at 1.00. That is, the expected output would be:

[01/Oct/2015:18:07:56 +0200] error number 3
[01/Oct/2015:18:12:56 +0200] error number 4
[02/Oct/2015:16:12:56 +0200] error number 5
[10/Oct/2015:16:12:58 +0200] error number 6
[10/Oct/2015:16:13:00 +0200] error number 7
[01/Nov/2015:00:10:00 +0200] error number 8

I have managed to convert the times to timestamp by using match() and then mktime(). First one finds the specified pattern, that is stored in the array a[] so it can be accessed (interesting to see glenn jackman's answer to access captured group from line pattern for a good example). Since mktime requires a format YYYY MM DD HH MM SS[ DST], I also have to convert the month in the form Xxx into a digit, for which I use an answer by Ed Morton to "convert month from Aaa to xx": awk '{printf "%02d\n",(match("JanFebMarAprMayJunJulAugSepOctNovDec",$0)+2)/3}'.

All together, finally I have the timestamp in the variable mytimestamp:

awk 'match($0, /([0-9]+)\/([A-Z][a-z]{2})\/([0-9]{4}):([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}) ([+-][0-9]{4})/, a) {
        day=a[1]; month=a[2]; year=a[3];
        hour=a[4]; min=a[5]; sec=a[6]; utc=a[7];
        month=sprintf("%02d",(match("JanFebMarAprMayJunJulAugSepOctNovDec",month)+2)/3);
        mydate=sprintf("%s %s %s %s %s %s %s", year,month,day,hour,min,sec,utc);
        mytimestamp=mktime(mydate)
        print mytimestamp
    }' mylog

Returns:

1443708776
1443712376
1443715676

etc.

So now I am ready to convert against the given dates. Since awk takes a lot to handle such format, I prefer to provide them through an external shell variable, using date -d"my date" +"%s" to print the timestamp:

start="$(date -d"1 Oct 2015 18:00 +0200" +"%s")"
end="$(date -d"1 Nov 2015 01:00 +0200" +"%s")"

All together, this works:

awk start="$(date -d"1 Oct 2015 18:00 +0200" +"%s")" end="$(date -d"1 Nov 2015 01:00 +0200" +"%s")" 'match($0, /([0-9]+)\/([A-Z][a-z]{2})\/([0-9]{4}):([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}) ([+-][0-9]{4})/, a) {day=a[1]; month=a[2]; year=a[3]; hour=a[4]; min=a[5]; sec=a[6]; utc=a[7]; month=sprintf("%02d",(match("JanFebMarAprMayJunJulAugSepOctNovDec",month)+2)/3); mydate=sprintf("%s %s %s %s %s %s %s", year,month,day,hour,min,sec,utc); mytimestamp=mktime(mydate); if (start<=mytimestamp && mytimestamp<=end) print}' mylog
[01/Oct/2015:18:07:56 +0200] error number 3
[01/Oct/2015:18:12:56 +0200] error number 4
[02/Oct/2015:16:12:56 +0200] error number 5
[10/Oct/2015:16:12:58 +0200] error number 6
[10/Oct/2015:16:13:00 +0200] error number 7
[01/Nov/2015:00:10:00 +0200] error number 8

However, this seems to be quite a bit of work for something that should be more straight forward. Nonetheless, the introduction of the "Time functions" section in man gawk is

Since one of the primary uses of AWK programs is processing log files that contain time stamp information, gawk provides the following functions for obtaining time stamps and formatting them.

So I wonder: is there any better way to do this? For example, what if the format instead of dd/Mmm/YYYY:HH:MM:ss was something like dd Mmm YYYY HH:MM:ss? Couldn't it be possible to provide the match pattern externally instead of having to change it every time this would happen? Do I really have to use match() and then process that output to then feed mktime()? Doesn't gawk provide a more simple way to do this?

解决方案

Use ISO 8601 time format!

However, this seems to be quite a bit of work for something that should be more straight forward.

Yes, this should be straightforward, and the reason why it is not, is because the logs do not use ISO 8601. Application logs should use ISO format and UTC to display times, other settings should be considered broken and fixed.

Your request should be split in two parts. The first part canonise the logs, converting dates to the ISO format, the second performs a research:

awk '
match($0, /([0-9]+)\/([A-Z][a-z]{2})\/([0-9]{4}):([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}) ([+-][0-9]{4})/, a) {
  day=a[1]
  month=a[2];
  year=a[3]
  hour=a[4]
  min=a[5]
  sec=a[6]
  utc=a[7];
  month=sprintf("%02d", (match("JanFebMarAprMayJunJulAugSepOctNovDec",month)+2)/3);
  myisodate=sprintf("%4d-%2d-%2dT%2d:%2d:%2d%6s", year,month,day,hour,min,sec,utc);
 $1 = myisodate
 print
}' mylog

The nice thing about ISO 8601 dates – besides them being a standard – is that the chronological order coincide with lexicographic order, therefore, you can use the /…/,/…/ operator to extract the dates you are interested in. For instance to find what happened between 1 Oct 2015 18:00 +0200 and 1 Nov 2015 01:00 +0200, append the following filter to the previous, standardising filter:

awk '/2015-10-01:18:00:00+0200/,/2015-11-01:01:00:00+0200/'

这篇关于如何使用awk轻松过滤日志?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆