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

我有时会做一些事情来弄清楚需要某个文件的前10个ips

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


您的工具箱中有什么? br />

评论

实际上,我有一个手工编写的美丽的正则表达式,用于将我所有的Apache自定义日志解析为单个字段,以提交到数据库中。我正在踢自己,我已经没有了。那是一个班轮。给您每个日志元素一个变量-然后我将其插入MySQL。如果找到它,我会在这里发布。

#1 楼

仅使用awk,您就可以使用apache日志文件执行几乎所有操作。 Apache日志文件基本上是用空格分隔的,您可以假装引号不存在,并可以按列号访问任何您感兴趣的信息。只有当您具有合并的日志格式并且对用户代理感兴趣时,这种情况才会出现问题,这时您必须使用引号(“)作为分隔符并运行单独的awk命令。以下内容将向您显示IP每个请求索引页的用户均按命中数排序:

awk -F'[ "]+' ' == "/" { ipcount[]++ }
    END { for (i in ipcount) {
        printf "%15s - %d\n", i, ipcount[i] } }' logfile.log


$ 7是请求的网址。您可以在开头添加任何条件。替换'$ 7 ==“ /”带有您想要的任何信息。

如果您将(ipcount [$ 1] ++)中的$ 1替换为,则可以按其他条件对结果进行分组。使用$ 7将显示访问了哪些页面以及访问了多久。当然,您首先需要更改条件,以下内容将显示用户从特定IP访问了哪些页面:

awk -F'[ "]+' ' == "1.2.3.4" { pagecount[]++ }
    END { for (i in pagecount) {
        printf "%15s - %d\n", i, pagecount[i] } }' logfile.log


您还可以通过管道将输出通过排序以按顺序获取结果,这既可以作为shell命令的一部分,也可以在awk脚本本身中进行:

awk -F'[ "]+' ' == "/" { ipcount[]++ }
    END { for (i in ipcount) {
        printf "%15s - %d\n", i, ipcount[i] | sort } }' logfile.log


如果您决定扩展awk脚本以输出其他信息,则后者将很有用。这完全取决于您要查找的内容。这些应该作为您感兴趣的任何内容的起点。

评论


是的,看到疯狂的长cat / grep / awk管道总是很奇怪。一旦您进入awk,通常就足够了。原始帖子的前三个子句可以简单地写为“ awk'/ request_to_file_foo / {print $ 1}'foo.log”。 awk可以将文件作为输入,并且可以使用正则表达式知道要关注的行。

–扎克·汤普森(Zac Thompson)
09年6月4日在6:06

优雅而简单。好。

–奥利维尔·杜拉克(Olivier Dulac)
2015年2月12日下午6:53

当心,在“ authuser”(第3个)字段中似乎允许使用空格,这会破坏所有内容,我个人认为应该禁止这样做,以允许我们这样做;-)

–曼达克
15年6月16日在9:31

#2 楼

出于我无法想象的原因,我从未见过别人做过的一件事,就是将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来完成我想要的数量级更快的事情?

评论


新格式的示例实际上仍然过于复杂-IP计数已减少-f 3 log | uniq -c |排序-n,用户代理剪切-f 8日志| uniq -c |排序-n。

–Creshal
2012-04-25 13:40



没错,这很简单。我已经更新了示例以反映这一点。

–丹·乌迪(Dan Udey)
2012年5月28日在22:15

“猫文件| grep字符串”是没有用的,为什么不“ grep字符串文件”呢?

–c4f4t0r
15年1月27日在13:28

我没有任何借口,并相应地更新了示例。

–丹·乌迪(Dan Udey)
15年2月27日在21:56

#3 楼

忘记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;


评论


有趣,但是如果您认为日志特别大,可能会遇到问题。还可以很好地处理自定义日志格式吗?

– Vagnerr
2009年6月5日13:39

我目前正在尝试,加载时间太慢(至少在0.9版中)。加载200Mb日志需要花费超过五分钟的时间。

–古迹
2011-12-15 11:17

我必须说,加载时间(花费了大约15分钟)之后,该程序的语法很棒,您可以对它进行排序,计数和分组。非常好。

–古迹
2011-12-15 17:19

Apache HTTPD提供了一种可以有效地将日志发送到数据库的方法。是的,写操作可能会花费很长时间,但是线程代理可能只是做中间的正确事情。无论如何,这将使查询类似语法的SQL的日志快得多。也不涉及加载-数据库服务器永久处于“ ON”状态。

–earearora
2012年5月28日23:16

我不知道这样的事情存在!对于已经非常熟悉SQL的Web开发人员,这是一个不错的选择。

– rinogo
20-09-21在20:20

#4 楼

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

if [ "" == "" ]; then
  log_file="/var/log/httpd/example.com-access_log"
else
  log_file=""
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 }'| sort -n | uniq -c | sort -n
elif [ "$type" = "url" ]; then
  tail -n $length $log_file | awk -F\" '{print }'| sort -n | uniq -c | sort -n
fi




#5 楼

访问日志中的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 }' | 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 }'\`;do echo -n "$i|"| sed 's/\./\\./g;';done`127\.|0\.0\.0)" | sort -n | uniq -c | sort -n


它们是我最喜欢的“一个衬里” :)

#6 楼

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


为什么命中率发生变化?
为什么总响应时间在增加?'。

我通过监视发现这种变化服务器状态页(通过mod_status),用于
命中率和活动请求和最近完成的请求的近似响应时间
(知道我很想念大量数据,但是样本足够好)。

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

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


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


hitcounts给定模式(ip地址或cgi字符串或参数等)的每个间隔(分钟或小时)
近似响应时间的直方图(使用%T参数)

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


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

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


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


perl中更复杂的示例可能是可视化一个

下面的脚本有很多东西值得咀嚼,特别是如果您不熟悉perl的话。


读stdin以便您可以使用部分日志,使用尾部(尤其是带有尾部-f的尾部),
带有或不带有毛刺以及其他过滤功能...
欺骗时期的时间戳记,并带有正则表达式并使用Date :: Manip
您只能对其进行少量修改以提取响应时间或其他任意数据

代码如下:

#!/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=" " 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”以将所有日志(如果您在负载均衡器后面有多个补丁)放在一起,并
webalizer(或awstats或其他常见分析器)。


#7 楼

在我的“ sed”示例中,它读取apache日志的默认格式,并将其转换为更便于自动处理的格式。整行定义为正则表达式,变量保存并写入输出,并以'#'作为分隔符。

输入的简化表示法是:
%s%s%s [ %s]“%s”%s%s“%s”“%s”

示例输入行:
xx.xx.xx.xx--[2011年3月29日: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/^\(.*\) \(.*\) \(.*\) \[\(.*\)\] \"\(.*\)\" \(.*\) \(.*\) \"\(.*\)\" \"\(.*\)\"$/########/g'


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

评论


这使得使用AWK进行处理变得轻而易举。正在寻找一种设置通用分隔符的快速方法,因此非常有用。

– Citricguy
2014年6月12日在3:07

我已经感受到了正则表达式的强大功能,只是想通过我自己的调整来删除“ HTML / 1.1”,并将协议(可能是非标准的兼容方式)分成自己的领域。享受:```cat access.log | sed's /^(.*)(。*)(。*)[(。*)] \“([[:::)] \ +)(。*)HTTP \ / 1 \ .1 \”( 。*)(。*)\“(。*)\” \“(。*)\” $ / \ 1#\ 2#\ 3#\ 4#\ 5#\ 6#\ 7#\ 8#\ 9#\ 10 / g'```

–乔什·朗姆布(Josh Rumbut)
2015年10月6日在18:30

#8 楼

我通过拖尾或整理文件来大量使用awk。每天晚上,我都会为每个服务器提供一份网络报告。根据您的日志文件和LogFormat,您需要编辑一些衬板才能使用。..

这是一个简单的示例:

如果我想要仅将404/500状态代码记录在服务器上的日志中,我将这样做:

#  is the status code in my log file

tail -f ${APACHE_LOG} |  awk  ' ~ /(404|500)/ {print }'




echo ""
#echo  "Hits by source IP:"
echo "======================================================================"

awk '{print }' "" | 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 }' "" | 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 }' "" | 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 }' "" | \
 grep -vE "(^"-"$|/www.$host|/$host)" | \
 sort | uniq -c | sort -rn | head -25

echo ""

#echo "Longest running requests"
echo "======================================================================"

awk  '{print ,}' "" | grep -ivE '(.gif|.jpg|.png|.css|.js)'  | awk '{secs=0.000001*;req=;printf("%.2f minutes req time for %s\n", secs / 60,req )}' | sort -rn | head -50

exit 0




#9 楼

谁在热链接您的图像:

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


#10 楼

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

#!/bin/bash
#This script should return a set of lines between 2 values, the main purpose is for searching a log file between 2 times
#Script usage: logship.sh "start" "stop" file

#If the file contains any "/" in the date range the following 2 lines add the escape character so that the search can be performed for those characters
start=$(echo "" | sed 's/\//\\//g')
stop=$(echo "" | sed 's/\//\\//g')

zipped=$(echo "" | grep -c "gz$")     #figures out if the file is zipped or not

if [ "$zipped" ==  "1" ]; then          #If the file is zipped then pass it through zcat before sed
        zcat  | sed -n "/$start/,/$stop/p";
else
        sed -n "/$start/,/$stop/p" ;  #if it's not zipped just run sed
fi


#11 楼

虽然不sed或awk,但我发现有两件事可用于处理apache和icecast日志文件。

AWStats有一个非常有用的脚本,称为logresolvemerge.pl,它将组合多个压缩或未压缩的日志文件。 ,剥离重复并按时间戳排序。它还可以进行DNS查找,并配置为运行多线程。当与awstats一起使用时,它特别有用,因为awstats不能添加时间戳比当前数据库旧的日志行,因此必须按顺序添加所有日志行,但这非常容易,因为您只需将所有内容都放在logresolvemerge.pl中,然后所有内容就会弹出。 sed和awk在处理日期方面非常糟糕,因为它们通常将它们视为字符串。 awk具有一些时间和日期功能,但它们的功能并不完善。例如,如果文件中没有出现那些确切的时间戳(即使它们之间的值确实存在),则很难提取出两个时间戳之间的行范围-克里斯的示例正是有这个问题。为了解决这个问题,我编写了一个PHP脚本,该脚本报告日志文件的时间戳范围,并且还可以使用您喜欢的任何日期或时间格式(无需与日志文件的时间戳格式匹配)按时间戳范围提取块。 br />
要保持这一主题,这里有一些有用的警告:
从apache或icecast日志中获取服务的字节总数:

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


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

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


评论


+1用于使用awk的简单字节求和Apache日志

– rymo
13年5月31日下午6:54

#12 楼

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