天天看点

多server多站点情况下awstats日志分析

情景描述:

    公司web服务器分为三个集群:前台(包括www、news、m等站点);图片(包括img、static等站点);后台(包括user、interface等站点)。这种情况下如何使用awstats进行日志分析呢?

这个需求我们很容易想到其中的几个“关键点”

    1、如何将各台web server上的站点日志拷贝到awstats server上的合适位置以备处理呢?这好说,写脚本用scp或者rsync嘛!ok,再想一步,如何在server数量或者server上站点发生变化时,脚本依然能够正确的完成任务呢?这就要各server上关于日志名称、日志及其备份目录、日志轮转等方面做统一约定了,脚本按照这个约定才能“以不变应万变”。

    2、一个站点的访问日志势必会有多份,awstats如何处理呢?还好这一点awstats已经帮我们想好了,在awstats配置文件里关于“LogFile=”部分的注释已经写得很清楚。

    3、这么多站点,最好有一个统一的入口页面,而且增减站点入口要简单,用着才方便。

基于以上几点,本文分享一种思路,若有不当之处,还请不吝指教。

文章分四个部分:前提及约定;awstats配置要点和FastCGI方式配置;集中展示页面(JavaScript实现);日志收集处理脚本

一、前提及约定

先看下我的约定(其实某些约定即使在本文之外也是很有必要的,例如1、2。脚本的正常运行依赖于这些约定):

    1、日志命名:如www.abc.com访问日志命名为www.access.log;日志存放位置:当前的日志存在于/nginx_log目录,日志备份存储于/nginx_log/backup。看下图就一目了然了

<a href="http://s1.51cto.com/wyfs02/M01/77/06/wKioL1ZhOQDQL4DyAAEZdBm5CRI547.png" target="_blank"></a>

    2、日志轮转:我的web server为nginx,通过日志切割脚本自动在0点重新生成日志,并将旧日志文件以前一天的日期为后缀命名(例如“images.access.log.2015-12-02”)移动到日志备份目录。

看下日志切割脚本(在每台web server定时0点执行)

cat rotate_nginx_logs.sh

1

2

3

4

5

6

7

8

9

10

11

12

<code>#!/bin/sh</code>

<code>logs_path=</code><code>"/nginx_log"</code>    <code>###日志存放路径</code>

<code>logs_bak_dir=</code><code>"/nginx_log/backup"</code>    <code>###日志备份目录</code>

<code>yesterday=`</code><code>date</code> <code>-d </code><code>"1 day ago"</code> <code>+%`</code>

<code>pid_file=`</code><code>cat</code> <code>/usr/local/nginx/nginx</code><code>.pid`    </code><code>###nginx pid 文件</code>

<code>cd</code> <code>$logs_path</code>

<code>for</code> <code>log </code><code>in</code> <code>`</code><code>ls</code> <code>*.log`;</code><code>do</code> <code>mv</code> <code>$log $logs_bak_dir/$log.$yesterday;</code><code>done</code>    <code>###重命名日志文件</code>

<code>kill</code> <code>-USR1 $pid_file    </code><code>###告知nginx重新生成日志</code>

<code>cd</code> <code>$logs_bak_dir</code>

<code>find</code> <code>. -mtime +5|</code><code>xargs</code> <code>rm</code> <code>-f    </code><code>###删除backup目录下超过5天的备份</code>

    3、每天凌晨自动完成这一系列任务, 页面最新可显示昨日的分析结果

    4、各站点日志文件拉取到awstats server后的存储形式

<a href="http://s1.51cto.com/wyfs02/M00/76/FD/wKiom1ZgE1jznJD3AAA0IxZl6HI430.png" target="_blank"></a>

    5、awstats server设置对所有相关web server的免秘钥登陆,以便拉取日志

二、awstats配置要点和FastCGI方式配置

    这里不展开详细的安装步骤,只说明一些配置选项以及如何配置动态模式。

    awstats有两种方式展示分析结果:一种是命令行生成html格式的分析页面;另一种是在浏览器通过向CGI网关传递参数来动态生成分析结果。

    其实在原理上,这两种形式都需要“perl  awstats.pl  -config=mysite.conf  -update”命令定时更新生成指定站点的“数据文件”(即经过awstats.pl分析日志生成的中间文件),然后可以分两种方法(对中间文件进行处理)生成我们看到的分析页面:

    1、通过“perlawstats.pl  -config=www.conf  -output -staticlinks &gt; www.html”命令生成html文件用于展示 

    2、web服务器通过FastGCI调用类似“http://awstats.abc.com/cgi-bin/awstats.pl?config=/usr/local/services/awstats-7.4/etc/www.conf”的url在访问时动态生成分析页面。

    两种方式各有优缺:第一种定时生成html文件,访问时页面加载快,但是页面布局使用起来不方便也不美观;第二种在打开页面时需要临时处理“数据文件”生成html,页面加载速度根据“数据文件”大小不同会有不同程度延迟,但是动态形式的页面布局看着舒服用着顺手(所以我选择fastcgi模式)。

fastcgi模式页面如下图所示

<a href="http://s5.51cto.com/wyfs02/M00/76/F9/wKiom1Zf5rvA7T-5AADsbbDQHj4480.png" target="_blank"></a>

来看下我的awstats目录,其中我增加了三个目录,如下图

然后来看下配置文件里需要注意的几个地方:

<code>###因为我们每一个站点的日志文件不止一份,所以LogFile必须按照以下模式配置</code>

<code>LogFile=</code><code>"/usr/local/services/awstats-7.4/tools/logresolvemerge.pl /nginx_log/log_analyze/www/*.log* |"</code>

<code>###DirData按照上图所示,存储于result下,例如 result/www  result/img等</code>

<code>DirData=</code><code>"/usr/local/services/awstats-7.4/result/www"</code>

<code>###DirCgi="/awstats"    将此行注释掉</code>

<code>LoadPlugin=</code><code>"decodeutfkeys"</code>    <code>###将此行的注释打开。否则页面“搜索关键字”可能乱码</code>

<code>同时yum </code><code>install</code> <code>perl-String-Escape perl-URI-Encode 安装perl字符及URL解码模块</code>

然后再看“配置文件”的生成管理:

各站点的配置文件存放于awstats/etc下,如www.conf,img.conf等。面对如此多的配置文件,这里写了个简单的小脚本来批量修改/生成配置文件。我们只需修改好一份配置文件,再运行该脚本即可(如,以www.conf为模板, 则执行"sh awstats/shells/batch_conf_file.sh www")

cat awstats/shells/batch_conf_file.sh

<code>###批量建数据目录,批量 创建各域名配置文件</code>

<code>cd</code> <code>/usr/local/services/awstats-7</code><code>.4</code>

<code>for</code> <code>i </code><code>in</code> <code>`</code><code>ls</code> <code>result`    </code><code>#这一步需下文的日志收集脚本执行完,在result目录下生成了各站点目录才能正确执行</code>

<code>do</code>

<code>    </code><code>if</code> <code>[ $1 != $i ];</code><code>then</code>

<code>        </code><code>echo</code> <code>-e </code><code>"\n------ create $i.conf -------"</code>

<code>        </code><code>rsync</code> <code>-a etc/$1.conf etc/$i.conf</code>

<code>        </code><code>echo</code> <code>-e </code><code>"------replace $1 by $i -------"</code>

<code>        </code><code>sed</code> <code>-i </code><code>"/^[^#].*/ s/$1/$i/g"</code> <code>etc/$i.conf</code>

<code>    </code><code>fi</code>

<code>done</code>

最后来看fastcgi模式的配置

1、在tools/nginx/目录下有相关文件

2、将tools/nginx/awstats-fcgi.php 拷贝至wwwroot/cgi-bin/fcgi.php

3、修改tools/nginx/awstats-nginx.conf 文件相关配置,并在nginx主配置文件里面引用该文件

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

<code>server {</code>

<code>        </code><code>listen 80;</code>

<code>        </code><code>server_name awstats.abc.com;</code>

<code>        </code><code>root </code><code>/usr/local/services/awstats-7</code><code>.4</code><code>/wwwroot</code><code>;</code>

<code>       </code><code>#charset utf8;</code>

<code>        </code><code>index index.html;</code>

<code>        </code> 

<code>        </code><code># Static awstats files: HTML files stored in DOCUMENT_ROOT/awstats/</code>

<code>        </code><code>location </code><code>/awstats/classes/</code> <code>{</code>

<code>                </code><code>alias</code> <code>/usr/local/services/awstats-7</code><code>.4</code><code>/wwwroot/classes/</code><code>;</code>

<code>        </code><code>}</code>

<code>        </code><code>location </code><code>/awstats/css/</code> <code>{</code>

<code>                </code><code>alias</code> <code>/usr/local/services/awstats-7</code><code>.4</code><code>/wwwroot/css/</code><code>;</code>

<code>        </code><code>location </code><code>/awstats/icon/</code> <code>{</code>

<code>                </code><code>alias</code> <code>/usr/local/services/awstats-7</code><code>.4</code><code>/wwwroot/icon/</code><code>;</code>

<code>        </code><code>location </code><code>/awstats-icon/</code> <code>{</code>

<code>        </code><code>location </code><code>/awstats/js/</code> <code>{</code>

<code>                </code><code>alias</code> <code>/usr/local/services/awstats-7</code><code>.4</code><code>/wwwroot/js/</code><code>;</code>

<code>        </code><code># Dynamic stats.</code>

<code>        </code><code>location ~ ^</code><code>/cgi-bin/</code><code>(awredir|awstats)\.pl {</code>

<code>                </code><code>gzip</code> <code>off;</code>

<code>                </code><code>fastcgi_pass 127.0.0.1:9000;</code>

<code>                </code><code>fastcgi_param SCRIPT_FILENAME $document_root</code><code>/cgi-bin/fcgi</code><code>.php;</code>

<code>                </code><code>fastcgi_param X_SCRIPT_FILENAME $document_root$fastcgi_script_name;</code>

<code>                </code><code>fastcgi_param X_SCRIPT_NAME $fastcgi_script_name;</code>

<code>                </code><code>include fastcgi_params;</code>

<code>                </code><code>#下面这三行酌情设置。太小可能会导致页面“链接被重置”</code>

<code>                </code><code>fastcgi_connect_timeout 300;</code>

<code>                </code><code>fastcgi_send_timeout 300;</code>

<code>                </code><code>fastcgi_read_timeout 300;</code>

<code>}</code>

三、集中展示页面

如果有多个域名的话,每次查看都需要输入对应的一串url肯定是很麻烦的。这里用html和JavaScript写了一个小页面,把所有域名的入口集中到一起,如下图所示

index.html文件内容如下(如有站点的增减,只需在js的vhost数组对应增减即可)

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

<code>&lt;</code><code>html</code><code>&gt;</code>

<code>&lt;</code><code>head</code><code>&gt;</code>

<code>&lt;</code><code>title</code><code>&gt;源站日志分析&lt;/</code><code>title</code><code>&gt;</code>

<code>&lt;</code><code>style</code> <code>type</code><code>=</code><code>"text/css"</code><code>&gt; </code>

<code>    </code><code>.wrap{</code>

<code>        </code><code>width:1200px;</code>

<code>        </code><code>margin:5px auto;</code>

<code>        </code><code>border:0 solid #000;</code>

<code>    </code><code>}</code>

<code>    </code><code>table {</code>

<code>        </code><code>width:660px;</code>

<code>        </code><code>border:2px solid #E0E0E0;</code>

<code>        </code><code>background-color:#F0F0F0;</code>

<code>        </code><code>border-collapse:collapse;</code>

<code>    </code><code>td {</code>

<code>        </code><code>width:220px;</code>

<code>        </code><code>height:45px;</code>

<code>        </code><code>font-size:1.1em;</code>

<code>        </code><code>text-align:center;</code>

<code>        </code><code>border:0 solid #2894FF;</code>

<code>    </code><code>h1 {text-align:center;color:#0072E3;font-family:"Microsoft YaHei",SimSun,BiauKai;}</code>

<code>    </code><code>a {text-decoration:none;color:#0072E3;}</code>

<code>    </code><code>a:hover{color:#FF5809}</code>

<code>&lt;/</code><code>style</code><code>&gt;</code>

<code>&lt;/</code><code>head</code><code>&gt;</code>

<code>&lt;</code><code>body</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>div</code> <code>class</code><code>=</code><code>"wrap"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>h1</code><code>&gt;源站日志分析平台&lt;/</code><code>h2</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>script</code> <code>type</code><code>=</code><code>"text/javascript"</code><code>&gt;</code>

<code>            </code><code>//域名数组,需要添加或删除域名只需在这里修改即可,会自动为其设置对应的超链接  </code>

<code>            </code><code>var vhost=[</code>

<code>                </code><code>"www.abc.com",</code>

<code>                </code><code>"m.abc.com",</code>

<code>                </code><code>"news.abc.com",</code>

<code>                </code><code>"static.abc.com",</code>

<code>                </code><code>"images.abc.com",</code>

<code>                </code><code>"bbs.abc.com",</code>

<code>                </code><code>"i.abc.com",</code>

<code>                </code><code>"interface.abc.com",</code>

<code>                </code><code>"open.abc.com",               </code>

<code>            </code><code>];</code>

<code>            </code><code>var num=vhost.length;    //域名数量</code>

<code>            </code><code>var j=101;    //j为行id,100为了避免跟单元格id重复而随意指定           </code>

<code>            </code><code>//利用JavaScript创建表格</code>

<code>            </code><code>for (var i=1;i&lt;=num;i++) {</code>

<code>                </code><code>if (i==1) {</code>

<code>                    </code><code>document.write("&lt;</code><code>table</code><code>&gt;&lt;</code><code>tr</code> <code>id=\"101\"&gt;&lt;</code><code>td</code> <code>id=\"1\" onmouseover=\"chg_td_bgcolor()\"&gt;");</code>

<code>                </code><code>}            </code>

<code>                </code><code>else if (i==num) {document.write("&lt;</code><code>td</code> <code>id=\"" + num + "\" onmouseover=\"chg_td_bgcolor()\"&gt;&lt;/</code><code>td</code><code>&gt;&lt;/</code><code>tr</code><code>&gt;&lt;/</code><code>table</code><code>&gt;")} </code>

<code>                </code><code>else if (i%3==0) {</code>

<code>                    </code><code>j++;    //每三个单元格一个新行,行号加一</code>

<code>                    </code><code>document.write("&lt;</code><code>td</code> <code>id=\""+ i +"\" onmouseover=\"chg_td_bgcolor()\"&gt;"+ i +"&lt;/</code><code>td</code><code>&gt;");</code>

<code>                    </code><code>document.write("&lt;/</code><code>tr</code><code>&gt;&lt;</code><code>tr</code> <code>id=\""+j +"\"&gt;");</code>

<code>                </code><code>}                </code>

<code>                </code><code>else {document.write("&lt;</code><code>td</code> <code>id=\""+ i +"\" onmouseover=\"chg_td_bgcolor()\"&gt;"+ i +"&lt;/</code><code>td</code><code>&gt;");}</code>

<code>            </code><code>} </code>

<code>            </code><code>//向表格填充内容</code>

<code>            </code><code>for (var tdid=0;tdid&lt;</code><code>num</code><code>;tdid++) {</code>

<code>                </code><code>//依顺序获取各td元素</code>

<code>                </code><code>var </code><code>tdnode</code><code>=</code><code>document</code><code>.getElementById(tdid+1);</code>

<code>                </code><code>//取出每个域名里的主机名,服务器端awstats该站点配置文件命名方式为 “主机名.conf”</code>

<code>                </code><code>var </code><code>hostname</code><code>=</code><code>vhost</code><code>[tdid].split(".abc",1);</code>

<code>                </code><code>//向表格插入域名并且设置超链接,config文件的位置各位自行设置</code>

<code>                </code><code>tdnode.innerHTML</code><code>=</code><code>"&lt;a href=\"</code><code>cgi-bin/awstats.pl?config=/usr/local/services/awstats-7.4/etc/"+hostname+ ".conf\"&gt;" +vhost[tdid] +"&lt;/</code><code>a</code><code>&gt;";</code>

<code>            </code><code>}</code>

<code>            </code> 

<code>            </code><code>//设置table行背景色</code>

<code>            </code><code>for (var x=101;x&lt;=j;x++){</code>

<code>                </code><code>var row=document.getElementById(x);</code>

<code>                </code><code>if (x%2==0) {</code>

<code>                    </code><code>row.style.background="#E0E0E0";</code>

<code>                </code><code>}</code>

<code>            </code><code>//连接在新窗口打开</code>

<code>            </code><code>var allLinks=document.getElementsByTagName("a");</code>

<code>            </code><code>for(var i=0;i!=allLinks.length; i++){</code>

<code>                </code><code>allLinks[i].target="_blank";</code>

<code>        </code><code>&lt;/</code><code>script</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>div</code><code>&gt;</code>

<code>&lt;/</code><code>body</code><code>&gt;</code>

<code>&lt;/</code><code>html</code><code>&gt;</code>

四、日志收集、处理

这里我在awstats server上写了一套shell脚本来实现无论web server上运行哪些站点,只要告诉脚本该server的ip即可将该server上所有站点日志拷贝至本机的合适位置以待分析。各server的ip信息记录于"/root/shells/ips.txt",每条记录一行,如“web1-192.168.1.1”

来看脚本内容,脚本内注释应该已经比较详细了

cat awstats/shells/cp_logs_for_awstats.sh

<code>log_dir_s=</code><code>/nginx_log/backup</code>                <code>#各台server日志文件源目录</code>

<code>log_dir_d=</code><code>/nginx_log/log_analyze</code>           <code>#定义将日志在本地后的最终存放路径</code>

<code>time1=`</code><code>date</code> <code>-d </code><code>"1 day ago"</code> <code>+%F`            </code><code>#获取一天前的日期</code>

<code>time2=`</code><code>date</code> <code>-d </code><code>"2 day ago"</code> <code>+%F`</code>

<code>basedir=</code><code>/usr/local/services/awstats-7</code><code>.4    </code><code>#awstats目录</code>

<code>echo</code> <code>-e </code><code>"`date`  开始拉取日志\n"</code> <code>&gt;$basedir</code><code>/logs/cron</code><code>.log</code>

<code>###ips.txt记录需要统计日志的所有server的hostname和ip.若增加web server,只需在该文件添加新server的记录如“web1-192.168.1.1”即可</code>

<code>for</code> <code>server_ip </code><code>in</code> <code>`</code><code>cat</code> <code>/root/shells/ips</code><code>.txt`; </code><code>do</code>

<code>    </code><code>ip=`</code><code>echo</code> <code>$server_ip|</code><code>awk</code> <code>-F </code><code>'-'</code> <code>'{print $2}'</code><code>`   </code><code>#获取各server IP</code>

<code>    </code> 

<code>    </code><code>echo</code> <code>-e </code><code>"\e[1;32m \nrsync logs from $ip\e[0m"</code> <code>&gt;&gt;$basedir</code><code>/logs/cron</code><code>.log</code>

<code>    </code><code>###将该ip下的所有前一天的access日志拷贝至本地/tmp下</code>

<code>    </code><code>rsync</code> <code>-avz --compress-level=6 --exclude=</code><code>"default.*"</code> <code>--min-size=1k $ip:$log_dir_s/*access*</code><code>"$time1"</code> <code>/tmp/</code>

<code>    </code><code>if</code> <code>[ $? != 0 ];</code><code>then</code>    <code>#检查rsync命令执行成功与否 </code>

<code>        </code><code>echo</code> <code>-e </code><code>"\e[1;31m \nrsync logs from $ip seems wrong!\e[0m"</code> <code>&gt;&gt;$basedir</code><code>/logs/cron</code><code>.log</code>

<code>    </code><code>###取得上一天的日志备份文件名 log_name</code>

<code>    </code><code>for</code> <code>log_name </code><code>in</code> <code>`</code><code>cd</code> <code>/tmp/</code><code>;</code><code>ls</code> <code>*.access.log*`; </code><code>do</code>

<code>        </code><code>vhost_name=`</code><code>echo</code> <code>$log_name|</code><code>awk</code> <code>-F.access </code><code>'{print $1}'</code><code>`    </code><code>#取得web server的vhost名</code>

<code>        </code><code>echo</code> <code>-e </code><code>"\e[1;32m ----------处理$vhost_name日志----------\e[0m"</code>

<code>        </code><code>###在本地建立目录分类存放cp过来的日志</code>

<code>        </code><code>if</code> <code>[ ! -d </code><code>"$log_dir_d/$vhost_name"</code> <code>];</code><code>then</code>

<code>            </code><code>mkdir</code> <code>$log_dir_d/$vhost_name</code>

<code>        </code><code>fi</code>

<code>        </code><code>rm</code> <code>-f $log_dir_d/$vhost_name/*</code><code>"$time2"</code><code>*</code>

<code>        </code><code>###从tmp下依次mv日志文件到目标目录,以日志所在server的ip为后缀</code>

<code>        </code><code>mv</code> <code>/tmp/</code><code>$log_name  $log_dir_d/$vhost_name/$log_name.$ip      </code>

<code>        </code><code>###在swstats目录下建立目录用于存放awstats.pl处理日志生成的“中间文件”</code>

<code>        </code><code>if</code> <code>[ ! -d </code><code>"$basedir/result/$vhost_name"</code> <code>];</code><code>then</code>

<code>            </code><code>mkdir</code> <code>$basedir</code><code>/result/</code><code>$vhost_name</code>

<code>        </code><code>fi</code>        

<code>    </code><code>done</code>

<code>echo</code> <code>-e </code><code>"`date`  拉取日志完成\n"</code> <code>&gt;&gt;$basedir</code><code>/logs/cron</code><code>.log</code>

上面的shell脚本只是将分散在各web server的日志文件按规则拉取到了本地特定目录,接下来的shell脚本用来调用awstats.pl循环处理各站点日志

cat awstats/shells/cron_awstats_update.sh

<code>#awstats日志分析</code>

<code>basedir=</code><code>/usr/local/services/awstats-7</code><code>.4</code>

<code>cd</code> <code>$basedir</code>

<code>#循环更新所有站点日志统计信息</code>

<code>echo</code> <code>-e </code><code>"\e[1;31m-------`date "</code><code>+%F %T</code><code>"`    开始处理---------\n\e[0m"</code> <code>&gt;&gt;logs</code><code>/cron</code><code>.log</code>

<code>for</code> <code>i </code><code>in</code> <code>`</code><code>ls</code> <code>result/`</code>

<code>   </code><code>echo</code> <code>-e </code><code>"\e[1;32m -----`date "</code><code>+%F %T</code><code>"`  处理 $i 日志-----\e[0m"</code> <code>&gt;&gt;logs</code><code>/cron</code><code>.log</code>

<code>   </code><code>perl wwwroot</code><code>/cgi-bin/awstats</code><code>.pl -config=etc/$i.conf -lang=cn -update &amp;&gt;&gt;logs</code><code>/cron</code><code>.log</code>

<code>echo</code> <code>-e </code><code>"\e[1;31m\n-------`date "</code><code>+%F %T</code><code>"`  处理完成---------\e[0m"</code> <code>&gt;&gt;logs</code><code>/cron</code><code>.log</code>

这两个脚本作为计划任务在awstats server运行,我的crontab如下

<code>#日志分析平台</code>

<code>5 0 * * * </code><code>/bin/bash</code> <code>/root/shells/cp_logs_to_img2_for_awstats</code><code>.sh ; </code><code>/bin/bash</code> <code>/root/shells/cron_awstats_update</code><code>.sh</code>

至此,这么一套东西就算完成了。

写了这么多,希望我说清楚了。看到此处的伙伴,希望能对你们有些许帮助吧!

     本文转自kai404 51CTO博客,原文链接:http://blog.51cto.com/kaifly/1719248,如需转载请自行联系原作者

继续阅读