根据日期范围过滤日志文件条目

Posted

技术标签:

【中文标题】根据日期范围过滤日志文件条目【英文标题】:Filter log file entries based on date range 【发布时间】:2011-12-04 02:24:52 【问题描述】:

我的服务器的 CPU 使用率异常高,我可以看到 Apache 使用了太多内存。 我有一种感觉,我正在被一个 IP DOS - 也许你能帮我找到攻击者?

我使用以下行来查找 10 个最“活跃”的 IP:

cat access.log | awk 'print $1' |sort  |uniq -c |sort -n |tail

前 5 个 IP 向服务器发出的请求大约是“普通”用户的 200 倍。但是,我无法确定这 5 个是否只是非常频繁的访问者,或者他们正在攻击服务器。

有没有办法,将上述搜索指定为一个时间间隔,例如。最后两个小时还是今天 10 点到 12 点之间?

干杯!

2011 年 10 月 23 日更新 - 我需要的命令:

获取最近 X 小时内的条目 [这里两个小时]

awk -vDate=`date -d'now-2 hours' +[%d/%b/%Y:%H:%M:%S` '  if ($4 > Date) print Date FS $4' access.log

在过去 X 小时 [这里两个小时] 内获取最活跃的 IPs

awk -vDate=`date -d'now-2 hours' +[%d/%b/%Y:%H:%M:%S` '  if ($4 > Date) print $1' access.log | sort  |uniq -c |sort -n | tail

获取相对时间跨度内的条目

awk -vDate=`date -d'now-4 hours' +[%d/%b/%Y:%H:%M:%S` -vDate2=`date -d'now-2 hours' +[%d/%b/%Y:%H:%M:%S` '  if ($4 > Date && $4 < Date2) print Date FS Date2 FS $4' access.log

获取绝对时间跨度内的条目

awk -vDate=`date -d '13:20' +[%d/%b/%Y:%H:%M:%S` -vDate2=`date -d'13:30' +[%d/%b/%Y:%H:%M:%S` '  if ($4 > Date && $4 < Date2) print $0' access.log 

在绝对时间跨度内获取最活跃的 IP

awk -vDate=`date -d '13:20' +[%d/%b/%Y:%H:%M:%S` -vDate2=`date -d'13:30' +[%d/%b/%Y:%H:%M:%S` '  if ($4 > Date && $4 < Date2) print $1' access.log | sort  |uniq -c |sort -n | tail

【问题讨论】:

我很懒;我会将日志复制到 Excel 中并创建一个数据透视表... @Ben “现在你有两个问题。” 【参考方案1】:

如果有人遇到awk: invalid -v option,这里有一个脚本可以在预定义的时间范围内获取最活跃的 IP:

cat <FILE_NAME> | awk '$4 >= "[04/Jul/2017:07:00:00" && $4 < "[04/Jul/2017:08:00:00"' | awk 'print $1' | sort -n | uniq -c | sort -nr | head -20

【讨论】:

cat is (still) useless. 同样,如果日期在不同的月份(例如,“May”>“Jun”),这可能会失败。有关将字符串转换为数字的方法,请参见我上面的评论。简而言之,monthnum=match("JanFebMarAprMayJunJulAugSepOctNovDec",monthstr)+2)/3【参考方案2】:

因为这是一个常见的 perl 任务

因为这与 extract last 10 minutes from logfile 不完全相同,因为它大约需要一段时间才能到达日志文件的末尾。

因为我需要它们,所以我(很快)写了这个:

#!/usr/bin/perl -ws
# This script parse logfiles for a specific period of time

sub usage 
    printf "Usage: %s -s=<start time> [-e=<end time>] <logfile>\n";
    die $_[0] if $_[0];
    exit 0;


use Date::Parse;

usage "No start time submited" unless $s;
my $startim=str2time($s) or die;

my $endtim=str2time($e) if $e;
$endtim=time() unless $e;

usage "Logfile not submited" unless $ARGV[0];
open my $in, "<" . $ARGV[0] or usage "Can't open '$ARGV[0]' for reading";
$_=<$in>;
exit unless $_; # empty file
# Determining regular expression, depending on log format
my $logre=qr^(\S3\s+\d1,2\s+(\d2:)2\d+);
$logre=qr^[^\[]*\[(\d+/\S+/(\d+:)3\d+\s\+\d+)\] unless /$logre/;

while (<$in>) 
    /$logre/ && do 
        my $ltim=str2time($1);
        print if $endtim >= $ltim && $ltim >= $startim;
    ;
;

这可以像这样使用:

./timelapsinlog.pl -s=09:18 -e=09:24 /path/to/logfile

用于打印 09h18 和 09h24 之间的日志。

./timelapsinlog.pl -s='2017/01/23 09:18:12' /path/to/logfile

用于从january 23th, 9h18'12"现在打印。

为了减少 perl 代码,我使用了-s 开关来允许从命令行自动分配变量:-s=09:18 将填充一个变量$s,其中将包含09:18。注意不要错过等号 = 和空格!

注意: 这为两种不同的日志标准保存了两种不同的 regex。如果您需要不同的日期/时间格式解析,请发布您自己的正则表达式或从您的日志文件中发布格式化日期的示例

^(\S3\s+\d1,2\s+(\d2:)2\d+)         # ^Jan  1 01:23:45
^[^\[]*\[(\d+/\S+/(\d+:)3\d+\s\+\d+)\]    # ^... [01/Jan/2017:01:23:45 +0000]

【讨论】:

非常好的回复,我将此添加到循环中,我可以轻松调查服务器上发生的情况。【参考方案3】:

是的,有多种方法可以做到这一点。这就是我将如何去做。对于初学者,无需管道输出 cat,只需使用 awk 打开日志文件即可。

awk -vDate=`date -d'now-2 hours' +[%d/%b/%Y:%H:%M:%S` '$4 > Date print Date, $0' access_log

假设您的日志看起来像我的(它们是可配置的)而不是日期存储在字段 4. 中并且被括起来。我在上面所做的是在过去 2 小时内找到所有内容。 Note the -d'now-2 hours' 或直译为现在减去 2 小时,对我来说看起来像这样:[10/Oct/2011:08:55:23

所以我正在做的是存储两小时前的格式化值并与字段 4 进行比较。条件表达式应该是直截了当的。然后我打印日期,然后是输出字段分隔符(OFS - 在这种情况下是空格),然后是整行 $0。您可以使用之前的表达式并打印 $1(IP 地址)

awk -vDate=`date -d'now-2 hours' +[%d/%b/%Y:%H:%M:%S` '$4 > Date print $1' | sort  |uniq -c |sort -n | tail

如果您想使用一个范围,请指定两个日期变量并适当地构造您的表达式。

所以如果你想在 2-4 小时前找到一些东西,你的表情可能看起来像这样

awk -vDate=`date -d'now-4 hours' +[%d/%b/%Y:%H:%M:%S` -vDate2=`date -d'now-2 hours' +[%d/%b/%Y:%H:%M:%S` '$4 > Date && $4 < Date2 print Date, Date2, $4 access_log'

这是我回答的关于 bash 中的日期的问题,您可能会觉得有帮助。 Print date for the monday of the current week (in bash)

【讨论】:

谢谢你!很好的例子,很好的解释。我已针对我的特定需求详细说明了您的代码,并将其添加到原始问题中,以供我自己和有需要的其他人将来参考。 很高兴能帮上忙。 最后一件事。如何搜索多个日志文件?我正在尝试使用 find 和 xargs 但仍然没有运气: find -name 'access.log' | awk -vDate=date -d '13:20' +[%d/%b/%Y:%H:%M:%S -vDate2=date -d'13:40' +[%d/%b/%Y:%H:%M:%S ' if ($4 > Date && $4 awk 是否聪明到足以猜出你在比较日期?因为我会说它只是比较字符串,并且由于日期的排序与字符串不同(在您使用的默认 nginx 格式中)......好吧,我做了一些快速测试,过去一个月的结果比过去的一天,所以它似乎有点坏了 @matchew 很抱歉恢复了这一点,但我很确定我的观点不依赖于awk 的版本,而且确实 2018 年的 nginx 似乎使用与 apache 相同的日期格式2011. 问题是如何处理 [01/Feb/20XX [02/Feb/20XX [31/Jan/20XX ?

以上是关于根据日期范围过滤日志文件条目的主要内容,如果未能解决你的问题,请参考以下文章

搜索日志文件以获取 2 个纪元时间之间的条目范围

基于感兴趣的日期范围作为参数输入限制在 Pig Latin 中加载日志文件

使用 hadoop mapreduce 作业从日志文件分析时间范围内的总错误条目发生率

如何使用 cron 为日志输出条目添加时间戳?

查看和分析 JSON 日志文件

Powershell - 如何处理非时间戳日志文件条目,包括空行