如何使用awk轻松过滤日志?

时间:2015-12-16 11:43:08

标签: regex date awk timestamp gawk

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

[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

我希望在10月1日18:00和11月1日之间发现这些行。也就是说,预期的输出是:

[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

我已设法使用match()然后mktime()将时间转换为时间戳。第一个找到指定的模式,它存储在数组a[]中,因此可以访问它(有趣的是看到glenn jackman对access captured group from line pattern的回答,这是一个很好的例子)。由于mktime需要格式为YYYY MM DD HH MM SS[ DST],因此我还必须将Xxx格式的月份转换为数字,我使用an answer by Ed Morton to "convert month from Aaa to xx"awk '{printf "%02d\n",(match("JanFebMarAprMayJunJulAugSepOctNovDec",$0)+2)/3}'

总之,最后我在变量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

返回:

1443708776
1443712376
1443715676

所以现在我准备好转换给定的日期。由于awk需要花费很多时间来处理这种格式,我更喜欢通过外部shell变量提供它们,使用date -d"my date" +"%s"来打印时间戳:

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

总之,这有效:

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

然而,对于应该更直接的事情,这似乎是相当多的工作。尽管如此,引入了&#34;时间功能&#34; man gawk中的部分是

  

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

所以我想知道:有没有更好的方法呢?例如,如果格式而不是dd/Mmm/YYYY:HH:MM:ss类似dd Mmm YYYY HH:MM:ss,该怎么办?无法在外部提供匹配模式,而不是每次都发生这种情况时都需要更改匹配模式吗?我是否真的必须使用match(),然后处理该输出然后输入mktime()?不是gawk提供了一种更简单的方法吗?

3 个答案:

答案 0 :(得分:2)

使用ISO 8601时间格式!

  

然而,对于一些应该更直接的事情来说,这似乎是相当有用的工作。

是的,这应该是直截了当的,原因不是,因为日志不使用ISO 8601。应用程序日志应使用ISO格式和UTC来显示时间,其他设置应被视为已破坏和修复。

您的请求应分为两部分。第一部分规范日志,将日期转换为ISO格式,第二部分进行研究:

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

ISO 8601日期的好处 - 除了它们是标准 - 是按时间顺序与词典顺序一致,因此,您可以使用/…/,/…/运算符来提取日期您有兴趣。例如,要找到 2015年10月1日18:00 +0200 2015年11月1日01:00 +0200 之间发生的事情,请将以下过滤器添加到以前,标准化过滤器:

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

答案 1 :(得分:0)

没有进入时间格式(假设所有记录的格式相同),您可以使用sort | awk组合轻松实现相同目的。

这假定日志不是根据您的格式和特殊排序选项排序的,用于排序月份(M)和awk以选择感兴趣的范围。排序基于该顺序的年,月和日。

$ sort -k1.9,1.12 -k1.5,1.7M -k1.2,1.3 log | awk '/01\/Oct\/2015/,/01\/Nov\/2015/'

如果文件已经排序,您也可以轻松扩展到包含时间并删除排序。

以下还有时间限制

awk -F: '/01\/Oct\/2015/ && $2>=18{p=1} 
         /01\/Nov\/2015/ && $2>=1 {p=0} p'

答案 2 :(得分:0)

我会在awk内使用awk -F "[][]" -v start="$(date -d"1 Oct 2015 18:00 +0200" +"%s")" -v end="$(date -d"1 Nov 2015 01:00 +0200" +"%s")" '{ gsub(/\//,"-",$2);sub(/:/," ",$2); cmd="date -d\""$2"\" +%s" ; cmd|getline mytimestamp; close(cmd); if (start<=mytimestamp && mytimestamp<=end) print }' mylog 命令来实现这一点,但不知道这对大型日志文件的执行方式。

// Query the Core Reporting API from Google Analytics with the defined variables

function requestResponse() {

  gapi.client.analytics.data.ga.get({
    'ids': 'ga:' + $('input[name="hiddenProfileId"]').val(),
    'start-date': $('input[name="hiddenStartDate"]').val(),
    'end-date': $('input[name="hiddenEndDate"]').val(),
    'metrics': metricsArrayOutput.join(","),
    'dimensions': dimensionsArrayOutput.join(",")

// If response isn't an error, then change div background to pale green and show a "Tree View" of the JSON obtained
  }).then(function(response) {
    $("#responseOutput").empty();
    $("#responseOutput").JSONView(response.result, {collapsed: true});
    $("#responseOutput").css("background-color", "#bef7be");

// Handle error: change div background to pale red and show the error Objects of the JSON obtained
  }, function(errorResponse) {
    $("#responseOutput").empty();
    $("#responseOutput").JSONView(errorResponse.result.error, {collapsed: false});
    $("#responseOutput").css("background-color", "#FBDDDD");
    alert("bad things happen to everyone");
  })
}