it-swarm.cn

您是否有任何有用的awk和grep脚本来解析Apache日志?

我可以使用日志分析器,但通常我需要解析最近的Web日志以了解当前发生的情况。

有时我会做一些事情来弄清楚请求某个文件的前10个ip

cat foo.log | grep request_to_file_foo | awk '{print $1}' |  sort -n | uniq -c | sort -rn | head

您的工具箱中有什么?

70
deadprogrammer

仅使用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脚本以打印出其他信息,则后者将很有用。这完全取决于您要查找的内容。这些应该作为您感兴趣的任何内容的起点。

54
Mark

出于我无法想象的原因,我从未见过其他人做过的一件事,就是将Apache日志文件格式更改为更容易解析的版本,其中包含实际对您重要的信息。

例如,我们从不使用HTTP基本身份验证,因此我们不需要记录这些字段。我am对每个请求的处理时间感兴趣,因此我们将其加进去。对于一个项目,我们还想知道(在负载均衡器上)是否有任何服务器在处理请求的速度慢于其他,因此我们记录了要代理的服务器的名称。

这是一台服务器的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来完成我想要的数量级更快的速度?

24
Dan Udey

忘记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;
16
Vihang D

这是一个脚本,用于从最近的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

来源

6
anoopjohn

访问日志中的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

他们是我最喜欢的“一个班轮” :)

5
f4nt

在我的“ 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'

感受正则表达式的力量:-)

3
Kris

建立常见问题列表将是此问题答案的重要索引。我的常见问题是:

  • 为什么命中率发生变化?
  • 为什么整体响应时间会增加?”。

我通过监视服务器状态页面(通过mod_status)的命中率以及活动的和最近完成的请求的近似响应时间来注意到这样的更改(完全知道我错过了大量数据,但是样本足够了)。

我使用以下LogFormat指令(%T确实有用)

LogFormat "%h %l %u %t \"%r\" %>s %b 
    \"%{Referer}i\" \"%{User-Agent}i\" %T" custom

我正在寻找因果关系以及首先发生的事情...通常是关于日志中模式的特定子集,因此对于任何给定的模式/正则表达式,我需要了解以下内容:

  • 给定模式(IP地址或CGI字符串或参数等)的每个间隔(分钟或小时)的点击计数
  • 近似响应时间的直方图(使用%T参数)

我通常使用Perl,因为最终它变得足够复杂,值得使用。


非Perl的示例是非200状态代码的每分钟快速命中率:

tail -9000 access_log | grep -v '" 200 ' | cut -d: -f2,3 | uniq -c

是的,我正在欺骗该grep,假设quote-space-200-space仅匹配http状态代码..。可以使用awk或Perl隔离该字段,只是要记住这可能是不准确的。


Perl中一个更复杂的示例可能是可视化某个模式的命中率的变化。

下面的脚本中有很多内容可供您参考,特别是如果您不熟悉Perl。

  • 读取标准输入,因此您可以使用部分日志,使用尾巴(尤其是带有尾巴-f),带有或不带有抓钩和其他过滤功能...
  • 欺骗正则表达式和使用Date :: Manip的Epoch时间戳提取
  • 您可以对其进行少量修改以提取响应时间或其他任意数据

代码如下:

#!/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
    );
}

如果您只想处理标准指标,请结帐

  • “ mergelog”将所有日志汇总在一起(如果您在负载均衡器后面有多个apap)
  • webalizer(或awstats或其他常见分析器)。
3
ericslaw

谁在热链接您的图像:

awk -F\" '($2 ~ /\.(jpg|gif)/ && $4 !~ /^http:\/\/www\.mydomain\.com/){print $4}' access_log | sort | uniq -c | sort
2
rkthkr

我通过拖尾或整理文件来大量使用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>

2
Michael Steinfeld

虽然不sed或awk,但发现有两件事对处理Apache和icecast日志文件很有用。

AWStats 有一个非常有用的脚本,称为 logresolvemerge.pl ,它将组合多个压缩或未压缩的日志文件,删除重复文件并按时间戳排序。它还可以进行DNS查找并将其配置为运行多线程。当与awstats一起使用时,它特别有用,因为awstats无法添加时间戳比当前数据库旧的日志行,因此必须按顺序添加所有日志行,但这非常容易,因为您只需在logresolvemerge.pl和everything一切都很好弹出。

sed和awk在处理日期方面非常糟糕,因为它们通常将它们视为字符串。 awk具有一些时间和日期功能,但功能不尽人意。例如,如果文件中没有出现那些确切的时间戳(即使它们之间的值确实出现了),则很难提取出两个时间戳之间的行范围-克里斯的示例正是有这个问题。为了解决这个问题,我写了 a PHP script ,它报告日志文件的时间戳范围,还可以使用您喜欢的任何日期或时间格式按时间戳范围提取块(它不需要匹配日志文件的时间戳格式)。

为了使这一话题成为话题,这里有一些有用的警告:从Apache或icecast日志获取服务的字节总数:

cat access.log | awk '{ sum += $10 } END { print sum }'

从一个冰封日志中获取连接的总秒数:

cat access.log | awk '{ sum += $13 } END { print sum }'
1
Synchro

我通常大部分时间都会做的事情是根据时间读取日志的各个部分,因此我使用sed编写了以下脚本以提取我感兴趣的时间段,该脚本可用于我所访问的每个日志文件可以处理存档的日志。

#!/ bin/bash 
#此脚本应返回2个值之间的一组行,主要目的是在2次之间搜索日志文件
#脚本用法: logship.sh“开始”“停止”文件
 
#如果文件在日期范围内包含任何“ /”,则以下两行添加转义字符,以便可以针对那些字符执行搜索字符
 start = $(回显“ $ 1” | sed's/\ /// \\\ /// g')
 stop = $(回显“ $ 2” | sed的s/\//\\\//g')

zipped=$(echo“ $ 3” | grep -c“ gz $”)#确定文件是否压缩
 
 if [“ $ zipped” ==“ 1”];然后#如果文件已压缩,则在sed 
 zcat $ 3前将其通过zcat。 sed -n“ /$start/,/$stop/p";
else
 sed -n”/$ start /,/ $ stop/p“ $ 3; #如果未压缩,请运行sed 
 fi 
1
Chris

在放弃大日志文件的asql之后,恢复了这个旧线程,再次寻找解决方案g,也在serverfault中,我发现了wtop here 这是一个开源工具,能够进行实时监控或处理日志并获取统计信息(前N位),非常灵活且功能强大,官方位置是 此处

0
aseques