grep 特定日志条目大于特定时间

grep 特定日志条目大于特定时间

我正在尝试使用特定文本过滤大于当前日期的特定时间的日志。我已成功过滤日志中包含当前日期的文本。这是命令:

grep "$(date +"%d/%b/%Y")" test.log | grep -i "failed login"

这是示例日志:

[04/Dec/2019 02:05:13 -0800] access       WARNING  10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser

[04/Dec/2019 02:05:15 -0800] access       WARNING  10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser

[04/Dec/2019 02:04:59 -0800] access       INFO     10.126.49.92 ahmed.rao - "POST /notebook/api/check_status HTTP/1.1" returned in 759ms

[04/Dec/2019 02:05:00 -0800] base         INFO     Selected cluster 0e83a448-26c9-459b-a0f2-3478ecb119af {u'interface': u'impala', u'namespace': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'type': u'direct', u'id': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'name': u'0e83a448-26c9-459b-a0f2-3478ecb119af'} interface hiveserver2

[04/Dec/2019 03:05:00 -0800] access       INFO     10.126.49.92 ahmed.rao - "POST /notebook/api/close_statement HTTP/1.1" returned in 1345ms

[04/Dec/2019 03:05:00 -0800] base         INFO     Selected cluster 0e83a448-26c9-459b-a0f2-3478ecb119af {u'interface': u'impala', u'namespace': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'type': u'direct', u'id': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'name': u'0e83a448-26c9-459b-a0f2-3478ecb119af'} interface hiveserver2

[04/Dec/2019 03:05:18 -0800] access       WARNING  10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser

但是,我不知道如何强制执行大于特定时间的条件。

答案1

使用tsfrom moreutils,您可以轻松地将这些时间戳转换为更有用的格式:

ts -r %FT%T%z < file.log |
  awk '$0 > "[2019-12-04T02:50" && tolower($0) ~ /failed login/'

根据您的输入(以及时区America/Los_Angeles)给出:

[2019-12-04T03:05:18-0800] access       WARNING  10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser

tswith-r解析该时间戳并将其转换为指定的%FT%T%z strftime格式(在您的时区中)。

由于该YYYY-MM-DDTHH:MM:SS格式按词法和时间顺序排序相同,因此只需进行字符串比较即可awk查找晚于指定日期的条目。awk还可以做grep -i的工作。这里使用tolower()不区分大小写匹配的标准方法。使用 GNUawk你还可以这样做:

gawk -v IGNORECASE=1 '$0 > "[2019-12-04T02:50" && /failed login/'

如果您没有,您可以使用'smoreutils进行解析(是一个使用但与 ' 相反的perl 脚本,它不是 's 的核心模块之一,因此可能不会安装在您的系统上):perlTime::PiecetsDate::ParseTime::Pieceperl

CUT=2019-12-04T02:50:00-0800 perl -MTime::Piece -F'[][]' -ale '
  BEGIN{$cut = Time::Piece->strptime($ENV{CUT}, "%FT%T%z")}
  print if /failed login/i &&
           Time::Piece->strptime($F[1], "%d/%b/%Y %T %z") >= $cut' < file.log

1 如果我们忽略进行 DST 的时区中冬季/夏季时钟更改时间的光点

答案2

我会留下这个以防万一有人发现它有用,但只需使用这个答案反而。它更简单、更高效。


这是一个 perl 方法:

$ perl -lne 'if(/^\[([^]]+)/){$d=$1; chomp($dateThreshold=`date -d "04 Dec 2019" +%s`); $d=~s|/| |g; chomp($d=`date -d "$d" +%s`); print if $d >= $dateThreshold;} ' test.log 
[04/Dec/2019 02:05:13 -0800] access       WARNING  10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
[04/Dec/2019 02:05:15 -0800] access       WARNING  10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser
[04/Dec/2019 02:04:59 -0800] access       INFO     10.126.49.92 ahmed.rao - "POST /notebook/api/check_status HTTP/1.1" returned in 759ms
[04/Dec/2019 02:05:00 -0800] base         INFO     Selected cluster 0e83a448-26c9-459b-a0f2-3478ecb119af {u'interface': u'impala', u'namespace': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'type': u'direct', u'id': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'name': u'0e83a448-26c9-459b-a0f2-3478ecb119af'} interface hiveserver2
[04/Dec/2019 03:05:00 -0800] access       INFO     10.126.49.92 ahmed.rao - "POST /notebook/api/close_statement HTTP/1.1" returned in 1345ms
[04/Dec/2019 03:05:00 -0800] base         INFO     Selected cluster 0e83a448-26c9-459b-a0f2-3478ecb119af {u'interface': u'impala', u'namespace': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'type': u'direct', u'id': u'0e83a448-26c9-459b-a0f2-3478ecb119af', u'name': u'0e83a448-26c9-459b-a0f2-3478ecb119af'} interface hiveserver2
[04/Dec/2019 03:05:18 -0800] access       WARNING  10.126.49.92 -anon- - "POST /hue/accounts/login HTTP/1.1"-- Failed login for user: testuser

而且,更清楚一点:

perl -lne 'if(/^\[([^]]+)/){ ## skip lines that do not match
            ## Save the date of the current line as $d
            $d=$1; 
            ## Replace all slashes with pipes so the 'date' command
            ## can read this as a date.
            $d=~s|/| |g; 
            ## Now, translate $d into seconds since the epoch
            chomp($d=`date -d "$d" +%s`); 
            ## Set the threshold date in seconds since the epoch.
            chomp($dateThreshold=`date -d "04 Dec 2019" +%s`); 
            ## Print this line if its date is greater than or equal to the threshold
            print if $d >= $dateThreshold;
           } ' test.log 

最后,您可以通过将设置阈值的步骤移动到一个块中来提高效率BEGIN,这样它在脚本启动时只运行一次:

perl -lne 'BEGIN{chomp($dateThreshold=`date -d "04 Dec 2019" +%s`); } if(/^\[([^]]+)/){$d=$1; $d=~s|/| |g; chomp($d=`date -d "$d" +%s`); print if $d >= $dateThreshold;} ' test.log 

答案3

这是使用 GNU 的另一个答案awk,它诉诸于调用 GNUdate命令。

awk程序(我们称之为find_after_timestamp.awk)如下所示:

BEGIN{
    gsub("/"," ",start_datetime)
    extcmd=sprintf("date -d \"%s\" +\"%%Y %%m %%d %%H %%M %%S\"",start_datetime)
    extcmd | getline startstring
    close(extcmd)
    start_ts=mktime(startstring)
    print "Lines will be matched starting with timestamp",start_ts

    printf("Will look for: \"%s\"\n",searchpat)
}


{
    if (match($0,/^\[([[:print:]]*)\][[:print:]]*$/,line_datetime)==0) next
    gsub("/"," ",line_datetime[1])
    extcmd=sprintf("date -d \"%s\" +\"%%Y %%m %%d %%H %%M %%S\"",line_datetime[1])
    extcmd | getline line_dtstring
    close(extcmd)
    line_ts=mktime(line_dtstring)
    if (line_ts > start_ts && $0 ~ searchpat) print
}

你会称其为

awk -v start_datetime="04/Dec/2019 02:05:21 -0800" -v searchpat="[Ff]ailed login" -f find_after_timestamp.awk test.log

其中变量start_datetime将是搜索范围的开始,即日期/时间等于或晚于该时间点的所有条目都将被考虑。的值start_datetime必须与日志文件中的格式相同,但除此之外是任意的,并且不需要是文件中实际存在的值。该变量searchpat将包含您正在寻找的模式。

解释

  • 该构造围绕着将您的(相当“非标准”)日期/时间规范转换DD/MONTH/YYYY HH:MM:SS TIMEZONE为 GNU 可以理解的内容,通过使用 . 将日期部分中的date替换为空格。/gsub

  • 然后,它通过在 shell 中date执行字符串并将结果读入字符串变量(在设置阶段、文件解析阶段)来调用外部命令,该变量现在已格式化,以便内置函数可以解析 ist。extcmdstartstringline_dtstringawkmktime

  • mktime命令将人类可读的日期/时间规范转换为纯数字的 UNIX 时间,可以使用算术比较来进行比较。

  • BEGIN阶段中,这样做是为了转换开始日期规范,在主体中,这样做是为了转换与当前行关联的时间戳。没有时间戳的行将被忽略 ( if (match(...)==0) next)。

  • 如果当前行的时间戳大于(=晚于)参考开始时间戳,并且searchpat在该行上找到 ,则将打印该行。

我知道在awk程序中求助于外部程序有点令人不悦,但这可以使用几乎任何安装中可用的基本工具来完成这项工作。

答案4

使用 sed:

sed -n "/$(date +'%d\/%b\/%Y')/,/*/p" test.log | grep -i "failed login"
  • 打印比赛(日期)后的所有内容。

注意:当前日期必须在日志文件中可用。

相关内容