分解 Apache 日志文件以进行分析
Dave 探讨了对难看的 Apache Web 服务器日志的分析。
我知道,在我的上一篇文章中,我承诺我会回到我之前开始构建的邮件合并程序。但是,由于我的 AskDaveTaylor.com Web 服务器遇到了一些小问题,我将行使编辑特权,再次推迟它。
我需要做的是能够处理 Apache 日志文件,并隔离遇到的特定问题和故障——这是 shell 脚本的完美用例。 事实上,我在 O'Reilly 出版的 Wicked Cool Shell Scripts 一书中有一个提供基本分析的类似脚本,但这一个更具体。
那些难看的日志文件首先,让我们看一下该站点最新日志文件中的几行
$ head sslaccesslog_askdavetaylor.com_3_8_2019
18.144.59.52 - - [08/Mar/2019:06:10:09 -0600] "GET /wp-content/
↪themes/jumpstart/framework/assets/js/nivo.min.js?ver=3.2
↪HTTP/1.1" 200 3074
"https://www.askdavetaylor.com/how-to-play-dvd-free-windows-
↪10-win10/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)
↪AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
↪64.0.3282.140 Safari/537.36 Edge/18.17763 X-Middleton/1"
↪52.53.151.37 - - [08/Mar/2019:06:10:09 -0600] "GET
↪/wp-includes/js/jquery/jquery.js?ver=1.12.4 HTTP/1.1"
↪200 33766 "https://www.askdavetaylor.com/how-to-play
↪-dvd-free-windows-10-win10/" "Mozilla/5.0 (Windows NT
↪10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
↪Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763
↪X-Middleton/1" 18.144.59.52 - - [08/Mar/2019:06:10:09
↪-0600] "GET /wp-content/plugins/google-analytics-for-
↪wordpress/assets/js/frontend.min.js?ver=7.4.2 HTTP/1.1"
↪200 2544 "https://www.askdavetaylor.com/how-to-play
↪-dvd-free-windows-10-win10/"
↪"Mozilla/5.0 (Windows NT 10.0; Win64; x64)
↪AppleWebKit/537.36 (KHTML, like Gecko)
↪Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763
↪X-Middleton/1"
它又大又难看,对吧? 好的,那么让我们只隔离一个条目,看看它的结构
18.144.59.52 - - [08/Mar/2019:06:10:09 -0600] "GET
↪/wp-content/themes/jumpstart/framework/assets/js/
↪nivo.min.js?ver=3.2 HTTP/1.1" 200 3074
"https://www.askdavetaylor.com/how-to-play-dvd-free-windows-
↪10-win10/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140
↪Safari/537.36 Edge/18.17763 X-Middleton/1"
这仍然足够晦涩难懂,让人头疼!
幸运的是,Apache 网站对我的服务器上使用的自定义日志文件格式有所谓的更清晰的解释。 当然,它的描述方式只有程序员才会喜欢
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\"
↪'\"%{User-agent}i\""
这些信息足以帮助解码日志条目。 我将在讲解过程中定义每个百分比格式序列,以便您也能了解它们是如何联系在一起的
%h = IP Address = 18.144.59.52
%l = ID of client = -
%u = UserID of client = -
%t = Time of request = [08/Mar/2019:06:10:09 -0600]
%r = Request = "GET /wp-content/themes/jumpstart/framework/
↪assets/js/nivo.min.js?ver=3.2 HTTP/1.1"
%>s = Status code = 200
%b = Size of request = 3074
Referrer = "https://www.askdavetaylor.com/how-to-play-dvd-
↪free-windows-10-win10/"
User Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)
↪AppleWebKit/537.36 (KHTML, like Gecko)
↪Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763
↪X-Middleton/1"
或者,为了让它更清晰一些
IP - - TIMESTAMP REQUEST STATUS SIZE REFERRER USERAGENT
这变得难以解析,因为有两种不同的字段分隔符:每个主要字段使用空格,但由于某些值可能包含空格,因此使用引号来分隔字段“Request”、“Referrer”和“User Agent”的开始/结束。
一般来说,shell 实用程序不太擅长处理这种混合字段分隔符,因此现在是时候进行一些跳出框框的思考了!
使用不同的分隔符分解字段确实,字段是用不同的分隔符分隔的(快速说十遍),但您可以分阶段处理信息。 您可以使用这个笨拙的代码块,仅处理引号分隔符来检查行
while read logentry
do
echo "f1 = $(echo "$logentry" | cut -d\" -f1)"
echo "f2 = $(echo "$logentry" | cut -d\" -f2)"
echo "f3 = $(echo "$logentry" | cut -d\" -f3)"
echo "f4 = $(echo "$logentry" | cut -d\" -f4)"
echo "f5 = $(echo "$logentry" | cut -d\" -f5)"
echo "f6 = $(echo "$logentry" | cut -d\" -f6)"
done < $accesslog
由于这只是最终 shell 脚本开发过程中的一个临时步骤,因此我不打算费心清理它或使其更高效。
针对 accesslog
的第一行运行此代码,结果如下
f1 = 18.144.59.52 - - [08/Mar/2019:06:10:09 -0600]
f2 = GET /wp-content/themes/jumpstart/framework/assets/
↪js/nivo.min.js?ver=3.2 HTTP/1.1
f3 = 200 3074
f4 = https://www.askdavetaylor.com/how-to-play-dvd-free-
↪windows-10-win10/
f5 =
f6 = Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140
Safari/537.36 Edge/18.17763 X-Middleton/1
这里需要注意的是字段 3。 字段 3 (f3
) 既有返回代码——在本例中为 200
——也有此事务中的总字节数 3074
。
这意味着,如果 f3
然后被空格分隔符分隔,您可以很容易地识别出返回代码和字节
f3=$(echo "$logentry" | cut -d\" -f3)
returncode="$(echo $f3 | cut -f1 -d\ )"
bytes="$(echo $f3 | cut -f2 -d\ )"
正如您所见,使用空格作为分隔符会产生看起来很奇怪的命令行,但是 \ 强制将紧随其后的字符解释为指定的值,首先是双引号,然后是空格字符。
仅提取错误现在,您可以浏览整个日志文件并仅提取错误代码吗? 当然可以,只需简化和调整 while
循环即可
while read logentry
do
f3=$(echo "$logentry" | cut -d\" -f3)
returncode="$(echo $f3 | cut -f1 -d\ )"
bytes="$(echo $f3 | cut -f2 -d\ )"
echo "$entry: returncode = $returncode, bytes = $bytes"
entry=$(( $entry + 1 ))
done < $accesslog
由于返回代码 200
表示成功,因此很容易使用 grep -v
查看日志文件中显示的其他返回代码
$ sh breakdown.sh | grep -v 200
113: returncode = 405, bytes = 42
138: returncode = 405, bytes = 42
177: returncode = 301, bytes = -
183: returncode = 301, bytes = -
186: returncode = 405, bytes = 42
187: returncode = 404, bytes = 11787
220: returncode = 404, bytes = 11795
279: returncode = 405, bytes = 42
397: returncode = 301, bytes = -
错误 405 是(根据 W3 Web 标准站点)“Method Not Allowed”(方法不允许),而 301 是“Moved Permanently”(永久移动),404 是标准的“Not Found”(未找到)错误,当有人请求服务器找不到的资源时会发生这种情况。
这很有用,但让我们进行下一步。 对于每个返回代码不是 200
“OK”状态的查询,让我们显示有问题的原始日志文件行。 这次,让我们修改脚本以进行 200
过滤
while read logentry
do
f3=$(echo "$logentry" | cut -d\" -f3)
returncode="$(echo $f3 | cut -f1 -d\ )"
bytes="$(echo $f3 | cut -f2 -d\ )"
if [ $returncode != "200" ] ; then
echo "$returncode ($entry): $logentry"
fi
entry=$(( $entry + 1 ))
done < $accesslog
结果如下所示
$ sh breakdown.sh
405 (113): 3.122.179.106 - - [08/Mar/2019:06:10:11 -0600]
"GET /xmlrpc.php HTTP/1.1" 405 42 "-" "Mozilla/5.0 (X11;
Linux i686; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
↪X-Middleton/1"
405 (138): 35.180.37.73 - - [08/Mar/2019:06:10:21 -0600]
"GET /xmlrpc.php HTTP/1.1" 405 42 "-" "Mozilla/5.0 (X11;
Linux i686; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
↪X-Middleton/1"
301 (177): 34.239.180.102 - - [08/Mar/2019:06:10:30 -0600]
"GET /how_do_i_restructure_my_wordpress_blog_without_losing_seo
↪HTTP/1.1" 301 - "-" "Mozilla/5.0 (Windows NT 6.1;
↪WOW64; rv:29.0) Gecko/20120101 Firefox/29.0
↪X-Middleton/1"
能够查看日志文件条目行、返回错误代码和完整的日志文件条目行很有用。 是否有模式? 它们是否都具有相同的用户代理(例如,机器人)? 它们是否来自相同的 IP 地址? 基于一天中时间的模式?
通过明智地使用 wc
,我还确定此特定日志文件包含 99,309 次总点击,其中 4,393 次(4.4%)是非 200 结果。
此脚本的另一个有用功能是自动创建多个输出文件,每个错误代码一个文件。 然而,我将把它作为给亲爱的读者的练习!
而且,在我的下一篇文章中,我将回到那个邮件合并脚本!