我可以使用日志分析器,但是我经常需要parsing最近的Web日志来看看目前发生了什么。
我有时会做一些事情,比如找出要求某个文件的前10位
cat foo.log | grep request_to_file_foo | awk '{print $1}' | sort -n | uniq -c | sort -rn | head
你在工具箱里有什么?
你可以用awk单独完成apache日志文件的任何操作。 Apache日志文件基本上是空白分隔的,你可以假装引号不存在,并按列号访问你感兴趣的任何信息。 唯一的一次是,如果你有组合的日志格式,并对用户代理感兴趣,那么你必须使用引号(“)作为分隔符并运行一个单独的awk命令。下面将显示IP每个请求索引页面的用户按访问次数sorting:
awk -F'[ "]+' '$7 == "/" { ipcount[$1]++ } END { for (i in ipcount) { printf "%15s - %d\n", i, ipcount[i] } }' logfile.log
$ 7是请求的url。 你可以在开始时添加你想要的任何条件。 用你想要的任何信息replace“$ 7 ==”/“”。
如果您replace(ipcount [$ 1] ++)中的$ 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
您还可以通过sorting来pipe道输出,以便按顺序获得结果,既可以作为shell命令的一部分,也可以作为awk脚本本身:
awk -F'[ "]+' '$7 == "/" { ipcount[$1]++ } END { for (i in ipcount) { printf "%15s - %d\n", i, ipcount[i] | sort } }' logfile.log
如果您决定扩展awk脚本以打印出其他信息,后者将很有用。 这一切都是你想知道的。 无论你感兴趣的是什么,这都应该成为一个起点。
有一件事我从来没有见过其他人做,因为我无法想象的原因是将Apache日志文件格式更改为一个更容易parsing的版本,其中包含的信息对您来说确实很重要。
例如,我们从来不使用HTTP基本authentication,所以我们不需要logging这些字段。 我感兴趣的是每个请求需要多长时间才能提供服务,所以我们将其添加进来。对于一个项目,我们也想知道(在我们的负载均衡器上)是否有服务器比其他服务器提供的请求慢,所以我们logging名称我们正在代理的服务器。
以下是一个服务器的apacheconfiguration摘录:
# 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的日志上计算花费less得多(没有正则expression式),在多长时间内会产生巨大的差异。 如果你想为User-Agents做同样的事情,这真的很简单。 如果您的日志是空格分隔的,则必须手工进行一些正则expression式匹配或stringsearch。 采用这种格式,很简单:
cut -f 8 log | uniq -c | sort -n
与上面完全一样。 事实上,你想要做的任何总结基本上是完全一样的。
为什么我会花费我的系统的CPU在awk和grep时,剪切将完成我想要的数量级更快?
忘了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个日志条目中find顶级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
他们是我最喜欢的“一条线”:)
build立一个常见的问题列表将是这个问题的答案的一个很好的索引。 我常见的问题是:
我通过监视服务器状态页面(通过mod_status)获得命中率以及近似完成请求的近似响应时间(完全知道我错过了一大堆数据,但是样本足够好),我注意到了这些变化。
我使用下面的LogFormat指令(%T是非常有用的)
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %T" custom
我正在寻找因果关系,首先发生了什么…通常关于日志中特定的模式子集,所以我需要知道任何给定模式/正则expression式的以下内容:
我通常使用Perl,因为最终它变得足够复杂,是值得的。
对于非200状态代码,非perl示例将是每分钟快速命中率:
tail -9000 access_log | grep -v '" 200 ' | cut -d: -f2,3 | uniq -c
是的,我用这个grep作弊,假定一个引用空间200的空间只匹配http状态码….可以使用awk或者perl隔离这个字段,只要记住它可能是不准确的。
在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日志的默认格式并将其转换为更方便的自动处理。 整行被定义为正则expression式,variables被保存并写入输出,用'#'作为分隔符。
input的简化符号为:%s%s%s [%s]“%s”%s%s“%s”“%s”
示例input行: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'
感受正则expression式的力量:-)
我通过拖拽或抓取文件来使用awk。 每个晚上我都会为每个服务器提供一个networking报告。 根据你的日志文件和你的LogFormat,你需要编辑一些内衬来为你工作。
这是一个简单的例子:
如果我想在我的服务器上只logging404/500状态码的日志,我会这样做:
# $6 is the status code in my log file tail -f ${APACHE_LOG} | awk '$8 ~ /(404|500)/ {print $6}'
<snip>
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>
谁热链接你的图片:
awk -F\" '($2 ~ /\.(jpg|gif)/ && $4 !~ /^http:\/\/www\.mydomain\.com/){print $4}' access_log | sort | uniq -c | sort
虽然不是sed或awk,但是有两件事对于处理apache和icecast日志文件非常有用。
AWStats有一个名为logresolvemerge.pl的非常有用的脚本,它将合并多个压缩或未压缩的日志文件,stripdupe和按时间戳sorting。 它也可以做DNS查找并configuration为运行multithreading。 当使用awstats时,这是非常有用的,因为awstats不能添加比当前数据库更早的时间戳的日志行,所以必须按顺序添加所有日志行,但是这很容易,因为您只需将所有内容都放在logresolvemerge.pl中,并且它们都可以很好地popup。
sed和awk在处理date方面相当糟糕,因为它们通常将它们视为string。 awk有一些时间和date的function,但是它们不怎么样。 例如,如果文件中没有出现这些确切的时间戳(即使它们之间存在值),提取两个时间戳之间的一系列行也很困难 – Chris的例子恰好是这个问题。 为了解决这个问题,我编写了一个PHP脚本 ,用于报告日志文件时间戳范围,也可以使用任意date或时间格式(不需要与日志文件的时间戳格式相匹配)按时间戳范围提取块。
为了保持这个话题,这里有几个有用的awkisms:获取来自apache或icecast日志的总字节数:
cat access.log | awk '{ sum += $10 } END { print sum }'
获取从icecast日志连接的总秒数:
cat access.log | awk '{ sum += $13 } END { print sum }'
我倾向于大部分时间做的事情是根据时间阅读日志的部分,所以我写了下面的脚本使用sed来拉出我感兴趣的时期,它适用于我所来的每个日志文件也可以处理归档日志。
#!/斌/庆典 #此脚本应该返回2个值之间的一组行,主要目的是在2次之间search日志文件 #Script用法:logship.sh“开始”“停止”文件 #如果文件在date范围中包含任何“/”,则以下两行添加转义字符,以便可以对这些字符执行search start = $(echo“$ 1”| sed's / \ // \\\ // g') stop = $(echo“$ 2”| sed's / \ // \\\ // g') zipped = $(echo“$ 3”| grep -c“gz $”)#如果文件被压缩或不压缩 如果[“$ zip”==“1”]; 那么#如果文件被压缩,然后在sed之前通过zcat zcat $ 3 | sed -n“/ $ start /,/ $ stop / p”; 其他 sed -n“/ $ start /,/ $ stop / p”$ 3; #如果没有压缩就运行sed 科幻
恢复这个旧的线程,放弃大日志文件的asql后,寻找一个解决schemeagaing,也是在serverfault,我发现这里是一个开源工具,它能够做实时监控或进程日志和获取统计信息(top N),非常灵活和强大,官方的地方在这里