天天看點

多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,如需轉載請自行聯系原作者

繼續閱讀