我可以使用日志分析器,但通常我需要解析最近的Web日志以了解当前发生的情况。
有时我会做一些事情来弄清楚请求某个文件的前10个ip
cat foo.log | grep request_to_file_foo | awk '{print $1}' | sort -n | uniq -c | sort -rn | head
您的工具箱中有什么?
我可以使用日志分析器,但通常我需要解析最近的Web日志以了解当前发生的情况。
有时我会做一些事情来弄清楚请求某个文件的前10个ip
cat foo.log | grep request_to_file_foo | awk '{print $1}' | sort -n | uniq -c | sort -rn | head
您的工具箱中有什么?
Answers:
仅使用awk,您就可以使用apache日志文件执行几乎所有操作。Apache日志文件基本上是用空格分隔的,您可以假装引号不存在,并可以按列号访问任何您感兴趣的信息。只有当您具有合并的日志格式并且对用户代理感兴趣时,这种情况才出现问题,这时您必须使用引号(“)作为分隔符并运行单独的awk命令。以下内容将向您显示IP每个请求索引页的用户均按匹配数排序:
awk -F'[ "]+' '$7 == "/" { ipcount[$1]++ }
END { for (i in ipcount) {
printf "%15s - %d\n", i, ipcount[i] } }' logfile.log
$ 7是请求的网址。您可以在开始时添加任何条件。用所需的任何信息替换'$ 7 ==“ /”。
如果将$ 1替换为(ipcount [$ 1] ++),则可以按其他条件对结果进行分组。使用$ 7将显示访问了哪些页面以及访问的频率。当然,那么您将需要在开始时更改条件。下面将显示用户从特定IP访问了哪些页面:
awk -F'[ "]+' '$1 == "1.2.3.4" { pagecount[$7]++ }
END { for (i in pagecount) {
printf "%15s - %d\n", i, pagecount[i] } }' logfile.log
您还可以通过排序通过管道传递输出以按顺序获取结果,这既可以作为shell命令的一部分,也可以在awk脚本本身中进行:
awk -F'[ "]+' '$7 == "/" { ipcount[$1]++ }
END { for (i in ipcount) {
printf "%15s - %d\n", i, ipcount[i] | sort } }' logfile.log
如果您决定扩展awk脚本以打印出其他信息,则后者将很有用。这完全取决于您要查找的内容。这些应该作为您感兴趣的任何内容的起点。
出于我无法想象的原因,我从未见过其他人做过的一件事,就是将Apache日志文件格式更改为更容易解析的版本,其中包含实际对您重要的信息。
例如,我们从不使用HTTP基本身份验证,因此我们不需要记录这些字段。我很感兴趣的每个请求需要多长时间来服务,所以我们要补充的是,对于一个项目,我们也想知道(我们的负载均衡器),如果任何服务器比别人慢服务的要求,所以我们的日志的名称我们要代理的服务器的数量。
这是一台服务器的apache配置的摘录:
# We don't want to log bots, they're our friends
BrowserMatch Pingdom.com robot
# Custom log format, for testing
#
# date proto ipaddr status time req referer user-agent
LogFormat "%{%F %T}t %p %a %>s %D %r %{Referer}i %{User-agent}i" standard
CustomLog /var/log/apache2/access.log standard env=!robot
从中您不能真正看出的是,每个字段之间是一个文字制表符(\ t)。这意味着如果我想用Python进行一些分析,例如显示非200状态,我可以这样做:
for line in file("access.log"):
line = line.split("\t")
if line[3] != "200":
print line
或者,如果我想做“谁在热链接图像?” 这将是
if line[6] in ("","-") and "/images" in line[5]:
对于访问日志中的IP计数,上一个示例:
grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" logfile | sort -n | uniq -c | sort -n
变成这样的东西:
cut -f 3 log | uniq -c | sort -n
易于阅读和理解,并且计算量大为减少(无需使用正则表达式),在9 GB的日志上,花费的时间差异很大。当这变得很整洁时,就是要对User-agent做同样的事情。如果日志是用空格分隔的,则必须手动进行一些正则表达式匹配或字符串搜索。使用这种格式,很简单:
cut -f 8 log | uniq -c | sort -n
与上述完全相同。实际上,您要执行的任何摘要基本上都是相同的。
为什么在切割时我会在awk和grep上花我系统的CPU来达到我想要的数量级更快的速度?
cut -f 3 log | uniq -c | sort -n
用户代理cut -f 8 log | uniq -c | sort -n
。
忘记awk和grep。签出asql。当您可以使用类似sql的语法查询日志文件时,为什么还要编写不可读的脚本。例如。
asql v0.6 - type 'help' for help.
asql> load /home/skx/hg/engaging/logs/access.log
Loading: /home/skx/hg/engaging/logs/access.log
sasql> select COUNT(id) FROM logs
46
asql> alias hits SELECT COUNT(id) FROM logs
ALIAS hits SELECT COUNT(id) FROM logs
asql> alias ips SELECT DISTINCT(source) FROM logs;
ALIAS ips SELECT DISTINCT(source) FROM logs;
asql> hits
46
asql> alias
ALIAS hits SELECT COUNT(id) FROM logs
ALIAS ips SELECT DISTINCT(source) FROM logs;
这是一个脚本,用于从最近的N个日志条目中查找顶部URL,顶部引荐来源网址和顶部用户代理
#!/bin/bash
# Usage
# ls-httpd type count
# Eg:
# ls-httpd url 1000
# will find top URLs in the last 1000 access log entries
# ls-httpd ip 1000
# will find top IPs in the last 1000 access log entries
# ls-httpd agent 1000
# will find top user agents in the last 1000 access log entries
type=$1
length=$2
if [ "$3" == "" ]; then
log_file="/var/log/httpd/example.com-access_log"
else
log_file="$3"
fi
if [ "$type" = "ip" ]; then
tail -n $length $log_file | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | sort -n | uniq -c | sort -n
elif [ "$type" = "agent" ]; then
tail -n $length $log_file | awk -F\" '{print $6}'| sort -n | uniq -c | sort -n
elif [ "$type" = "url" ]; then
tail -n $length $log_file | awk -F\" '{print $2}'| sort -n | uniq -c | sort -n
fi
访问日志中的IP计数:
cat log | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | sort -n | uniq -c | sort -n
这有点丑陋,但可以。我还将以下内容与netstat一起使用(以查看活动的连接):
netstat -an | awk '{print $5}' | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | egrep -v "(`for i in \`ip addr | grep inet |grep eth0 | cut -d/ -f1 | awk '{print $2}'\`;do echo -n "$i|"| sed 's/\./\\\./g;';done`127\.|0\.0\.0)" | sort -n | uniq -c | sort -n
他们是我最喜欢的“一个班轮” :)
建立常见问题列表将是此问题答案的重要索引。我的常见问题是:
我通过监视服务器状态页面(通过mod_status)的命中率以及活动的和最近完成的请求的近似响应时间来注意到这样的更改(完全知道我错过了大量数据,但是样本足够了)。
我使用以下LogFormat指令(%T确实有用)
LogFormat "%h %l %u %t \"%r\" %>s %b
\"%{Referer}i\" \"%{User-Agent}i\" %T" custom
我正在寻找因果关系以及首先发生的事情...通常是关于日志中模式的特定子集,因此对于任何给定的模式/正则表达式,我需要了解以下内容:
我通常使用perl,因为最终它变得足够复杂,值得使用。
一个非perl的示例是非200状态代码的每分钟快速命中率:
tail -9000 access_log | grep -v '" 200 ' | cut -d: -f2,3 | uniq -c
是的,我正在用那个grep作弊,假设quote-space-200-space只匹配http状态代码。
在perl中,一个更复杂的示例可能是可视化某个模式的命中率的变化。
下面的脚本中有很多内容可供您参考,特别是如果您不熟悉perl。
代码如下:
#!/usr/bin/perl
# script to show changes in hitrates for any regex pattern
# results displayed with arbitrary intervals
# and ascii indication of frequency
# gaps are also displayed properly
use Date::Manip;
use POSIX qw(strftime);
$pattern=shift || ".";
$ival=shift || 60;
$tick=shift || 10;
$minb=undef;
while (<>){
next unless /$pattern/;
$stamp="$1 $2" if m[(../.../....):(..:..:..)];
$epoch = UnixDate(ParseDate($stamp),"%s");
$bucket= int($epoch/$ival)*$ival;
$minb=$bucket if $bucket<$minb || !defined($minb);
$maxb=$bucket if $bucket>$maxb;
$count{$bucket}++;
}
# loop thru the min/max range to expose any gaps
for($t=$minb;$t<=$maxb;$t+=$ival){
printf "%s %s %4d %s\n",
$t,
strftime("%m/%d/%Y %H:%M:%S",localtime($t)),
$count{$t}+0,
substr("x"x100,0,$count{$t}/$tick
);
}
如果您只想处理标准指标,请结帐
在我的“ sed”示例中,它读取apache日志的默认格式,并将其转换为更便于自动处理的格式。整行定义为正则表达式,变量被保存并以'#'作为分隔符写入输出。
输入的简化符号为:%s%s%s [%s]“%s”%s%s“%s”“%s”
输入行示例:xx.xx.xx.xx--[29 / Mar / 2011:12:33:02 +0200]“ GET /index.html HTTP / 1.0” 200 9443“-”“ Mozilla / 4.0”
输出行示例:xx.xx.xx.xx#-#-#29 / Mar / 2011:12:33:02 + 0200#GET /index.html HTTP / 1.0#200#9443#-#Mozilla / 4.0
cat access.log | \
sed 's/^\(.*\) \(.*\) \(.*\) \[\(.*\)\] \"\(.*\)\" \(.*\) \(.*\) \"\(.*\)\" \"\(.*\)\"$/\1#\2#\3#\4#\5#\6#\7#\8#\9/g'
感受正则表达式的力量:-)
我经常通过尾随或整理文件来使用awk。每天晚上,我都会为每个服务器提供一份网络报告。根据您的日志文件和LogFormat,您需要编辑一些衬板才能为您工作。
这是一个简单的例子:
如果我只想为服务器上的日志添加404/500状态代码,则可以这样做:
# $6 is the status code in my log file
tail -f ${APACHE_LOG} | awk '$8 ~ /(404|500)/ {print $6}'
<片段>
echo ""
#echo "Hits by source IP:"
echo "======================================================================"
awk '{print $2}' "$1" | grep -ivE "(127.0.0.1|192.168.100.)" | sort | uniq -c | sort -rn | head -25
echo ""
echo ""
#echo "The 25 most popular pages:"
echo "======================================================================"
awk '{print $6}' "$1" | grep -ivE '(mod_status|favico|crossdomain|alive.txt)' | grep -ivE '(.gif|.jpg|.png)' | \
sed 's/\/$//g' | sort | \
uniq -c | sort -rn | head -25
echo ""
echo ""
echo "The 25 most popular pages (no js or css):"
echo "======================================================================"
awk '{print $6}' "$1" | grep -ivE '(mod_status|favico|crossdomain|alive.txt)' | grep -ivE '(.gif|.jpg|.png|.js|.css)' | \
sed 's/\/$//g' | sort | \
uniq -c | sort -rn | head -25
echo ""
#echo "The 25 most common referrer URLs:"
echo "======================================================================"
awk '{print $11}' "$1" | \
grep -vE "(^"-"$|/www.$host|/$host)" | \
sort | uniq -c | sort -rn | head -25
echo ""
#echo "Longest running requests"
echo "======================================================================"
awk '{print $10,$6}' "$1" | grep -ivE '(.gif|.jpg|.png|.css|.js)' | awk '{secs=0.000001*$1;req=$2;printf("%.2f minutes req time for %s\n", secs / 60,req )}' | sort -rn | head -50
exit 0
</ snip>
我通常大部分时间都会做的事情是根据时间读取日志的各个部分,因此我使用sed编写了以下脚本以提取我感兴趣的时间段,该脚本可用于我所访问的每个日志文件可以处理存档的日志。
#!/ bin / bash #此脚本应在2个值之间返回一组行,主要目的是在2次之间搜索日志文件 #脚本用法:logship.sh“开始”“停止”文件 #如果文件在日期范围内包含任何“ /”,则以下两行添加转义字符,以便可以搜索这些字符 start = $(echo“ $ 1” | sed's / \ // \\\\ // g') stop = $(echo“ $ 2” | sed's / \ // \\\ /// g') zipped = $(echo“ $ 3” | grep -c“ gz $”)#确定文件是否压缩 如果[“ $ zipped” ==“ 1”]; 然后#如果文件已压缩,则在sed前将其通过zcat zcat $ 3 | sed -n“ / $ start /,/ $ stop / p”; 其他 sed -n“ / $ start /,/ $ stop / p” $ 3; #如果未压缩,请运行sed 科幻
虽然没有sed或awk,但我发现有两件事对处理apache和icecast日志文件很有用。
AWStats有一个非常有用的脚本,称为logresolvemerge.pl,它将结合多个压缩或未压缩的日志文件,剥离重复并按时间戳排序。它还可以进行DNS查找,并配置为运行多线程。当与awstats一起使用时,这特别有用,因为awstats不能添加时间戳比当前数据库旧的日志行,因此必须按顺序添加所有日志行,但这非常容易,因为您只需将所有内容都放在logresolvemerge.pl中,并且所有内容都会弹出。
sed和awk在处理日期方面非常糟糕,因为它们通常将它们视为字符串。awk具有一些时间和日期功能,但功能不尽人意。例如,如果文件中没有出现那些确切的时间戳(即使它们之间的值确实出现了),则很难提取出两个时间戳之间的行范围-克里斯的示例正是有这个问题。为了解决这个问题,我编写了一个PHP脚本,该脚本报告日志文件时间戳范围,并且还可以使用您喜欢的任何日期或时间格式(根据日志文件的时间戳格式)按时间戳范围提取一块块。
为了使这个话题成为话题,这里有一些有用的警告:从apache或icecast日志中获取服务的字节总数:
cat access.log | awk '{ sum += $10 } END { print sum }'
从一个冰封日志中获取连接的总秒数:
cat access.log | awk '{ sum += $13 } END { print sum }'