1. 程式人生 > 其它 >日誌歸檔與資料探勘

日誌歸檔與資料探勘

日誌歸檔與資料探勘

摘要

2013-03-19 第一版

2014-12-16 第二版

我的系列文件

Netkiller Architect 手札

Netkiller Developer 手札

Netkiller PHP 手札

Netkiller Python 手札

Netkiller Testing 手札

Netkiller Cryptography 手札

Netkiller Linux 手札

Netkiller Debian 手札

Netkiller CentOS 手札

Netkiller FreeBSD 手札

Netkiller Shell 手札

Netkiller Security 手札

Netkiller Web 手札

Netkiller Monitoring 手札

Netkiller Storage 手札

Netkiller Mail 手札

Netkiller Docbook 手札

Netkiller Project 手札

Netkiller Database 手札

Netkiller PostgreSQL 手札

Netkiller MySQL 手札

Netkiller NoSQL 手札

Netkiller LDAP 手札

Netkiller Network 手札

Netkiller Cisco IOS 手札

Netkiller H3C 手札

Netkiller Multimedia 手札

Netkiller Perl 手札

Netkiller Amateur Radio 手札

Netkiller DevOps 手札

您可以使用iBook閱讀當前文件


目錄

  • 1. 什麼日誌歸檔
  • 2. 為什麼要做日誌歸檔
  • 3. 何時做日誌歸檔
  • 4. 歸檔日誌放在哪裡
  • 5. 誰去做日誌歸檔
  • 6. 怎樣做日誌歸檔
    • 6.2.1. 軟體安裝
    • 6.2.2. 節點推送端
    • 6.2.3. 日誌收集端
    • 6.2.4. 日誌監控
    • 6.1.1. 將日誌放入資料庫
    • 6.1.2. Apache Pipe
    • 6.1.3. Log format
    • 6.1.4. 日誌匯入到 MongoDB
    • 6.1. 日誌格式轉換
    • 6.2. 日誌中心方案

1. 什麼日誌歸檔

歸檔,是指將日誌整理完畢且有儲存價值的檔案,經系統整理交日誌伺服器儲存的過程。

2. 為什麼要做日誌歸檔

  • 隨時調出歷史日誌查詢。
  • 通過日誌做資料探勘,挖掘有價值的資料。
  • 檢視應用程式的工作狀態

3. 何時做日誌歸檔

日誌歸檔應該是企業規定的一項制度(“歸檔制度”),系統建設之初就應該考慮到日誌歸檔問題。如果你的企業沒有這項工作或制度,在看完本文後建議你立即實施。

4. 歸檔日誌放在哪裡

簡單的可以採用單節點伺服器加備份方案。

隨著日誌規模擴大,未來必須採用分散式檔案系統,甚至涉及到遠端異地容災。

5. 誰去做日誌歸檔

我的答案是日誌歸檔自動化,人工檢查或抽檢。

6. 怎樣做日誌歸檔

將所有伺服器的日誌都彙總到一處,有幾種方法

日誌歸檔常用方法:

  • ftp 定是下載, 這種做法適合小檔案且日誌量不大,定是下載到指定伺服器,缺點是重複傳輸,實時性差。
  • rsyslog 一類的程式,比較通用,但擴充套件不便。
  • rsync 定是同步,適合打檔案同步,好於FTP,實時性差。

6.1. 日誌格式轉換

首先我來介紹一種簡單的方案

我用D語言寫了一個程式將 WEB 日誌正則分解然後通過管道傳遞給資料庫處理程式

6.1.1. 將日誌放入資料庫

將WEB伺服器日誌通過管道處理然後寫入資料庫

處理程式原始碼

				$ vim match.d
import std.regex;
import std.stdio;
import std.string;
import std.array;

void main()
{
    // nginx
	//auto r = regex(`^(S+) (S+) (S+) [(.+)] "([^"]+)" ([0-9]{3}) ([0-9]+) "([^"]+)" "([^"]+)" "([^"]+)"`);

	// apache2
	auto r = regex(`^(S+) (S+) (S+) [(.+)] "([^"]+)" ([0-9]{3}) ([0-9]+) "([^"]+)" "([^"]+)"`);

	foreach(line; stdin.byLine)
	{

		foreach(m; match(line, r)){
			//writeln(m.hit);
			auto c = m.captures;
			c.popFront();
			//writeln(c);
			auto value = join(c, "","");
			auto sql = format("insert into log(remote_addr,unknow,remote_user,time_local,request,status,body_bytes_sent,http_referer,http_user_agent,http_x_forwarded_for) value("%s");", value );
			writeln(sql);
		}
	}
}				

編譯

$ dmd match.d
$ strip match

$ ls
match  match.d  match.o				

簡單用法

$ cat access.log | ./match				

高階用法

				$ cat access.log | match | mysql -hlocalhost -ulog -p123456 logging				

實時處理日誌,首先建立一個管道,尋該日誌檔案寫入管道中。

cat  管道名 | match | mysql -hlocalhost -ulog -p123456 logging				

這樣就可以實現實時日誌插入。

提示

上面程式稍加修改即可實現Hbase, Hypertable 本版

6.1.2. Apache Pipe

Apache 日誌管道過濾 CustomLog "| /srv/match >> /tmp/access.log" combined

				<VirtualHost *:80>
        ServerAdmin webmaster@localhost

        #DocumentRoot /var/www
        DocumentRoot /www
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>
        #<Directory /var/www/>
        <Directory /www/>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride None
                Order allow,deny
                allow from all
        </Directory>

        ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
        <Directory "/usr/lib/cgi-bin">
                AllowOverride None
                Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                Order allow,deny
                Allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log

        # Possible values include: debug, info, notice, warn, error, crit,
        # alert, emerg.
        LogLevel warn

        #CustomLog ${APACHE_LOG_DIR}/access.log combined
        CustomLog "| /srv/match >> /tmp/access.log" combined

    Alias /doc/ "/usr/share/doc/"
    <Directory "/usr/share/doc/">
        Options Indexes MultiViews FollowSymLinks
        AllowOverride None
        Order deny,allow
        Deny from all
        Allow from 127.0.0.0/255.0.0.0 ::1/128
    </Directory>

</VirtualHost>				

經過管道轉換過的日誌效果

				$ tail /tmp/access.log
insert into log(remote_addr,unknow,remote_user,time_local,request,status,body_bytes_sent,http_referer,http_user_agent,http_x_forwarded_for) value("192.168.6.30","-","-","21/Mar/2013:16:11:00 +0800","GET / HTTP/1.1","304","208","-","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22");
insert into log(remote_addr,unknow,remote_user,time_local,request,status,body_bytes_sent,http_referer,http_user_agent,http_x_forwarded_for) value("192.168.6.30","-","-","21/Mar/2013:16:11:00 +0800","GET /favicon.ico HTTP/1.1","404","501","-","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22");
insert into log(remote_addr,unknow,remote_user,time_local,request,status,body_bytes_sent,http_referer,http_user_agent,http_x_forwarded_for) value("192.168.6.30","-","-","21/Mar/2013:16:11:00 +0800","GET / HTTP/1.1","304","208","-","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22");				

6.1.3. Log format

通過定義LogFormat可以直接輸出SQL形式的日誌

Apache

LogFormat "%v:%p %h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" vhost_combined
LogFormat "%h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" combined
LogFormat "%h %l %u %t "%r" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent				

Nginx

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';				

但對於系統管理員使用grep,awk,sed,sort,uniq分析時造成一定的麻煩。所以我建議仍然採用正則分解

產生有規則日誌格式,Apache:

LogFormat 
        ""%h",%{%Y%m%d%H%M%S}t,%>s,"%b","%{Content-Type}o",  
        "%U","%{Referer}i","%{User-Agent}i""				

將access.log檔案匯入到mysql中

LOAD DATA INFILE '/local/access_log' INTO TABLE tbl_name
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ESCAPED BY '\'				

6.1.4. 日誌匯入到 MongoDB

# rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
# yum install mongodb				

D語言日誌處理程式

				import std.regex;
//import std.range;
import std.stdio;
import std.string;
import std.array;

void main()
{
	// nginx
	auto r = regex(`^(S+) (S+) (S+) [(.+)] "([^"]+)" ([0-9]{3}) ([0-9]+) "([^"]+)" "([^"]+)" "([^"]+)"`);
	// apache2
	//auto r = regex(`^(S+) (S+) (S+) [(.+)] "([^"]+)" ([0-9]{3}) ([0-9]+) "([^"]+)" "([^"]+)"`);
	foreach(line; stdin.byLine)
	{
		//writeln(line);
		//auto m = match(line, r);
		foreach(m; match(line, r)){
			//writeln(m.hit);
			auto c = m.captures;
			c.popFront();
			//writeln(c);
			/*
			SQL
			auto value = join(c, "","");
			auto sql = format("insert into log(remote_addr,unknow,remote_user,time_local,request,status,body_bytes_sent,http_referer,http_user_agent,http_x_forwarded_for) value("%s");", value );
			writeln(sql);
			*/
			// MongoDB
			string bson = format("db.logging.access.save({
						'remote_addr': '%s',
						'remote_user': '%s',
						'time_local': '%s',
						'request': '%s',
						'status': '%s',
						'body_bytes_sent':'%s',
						'http_referer': '%s',
						'http_user_agent': '%s',
						'http_x_forwarded_for': '%s'
						})",
						c[0],c[2],c[3],c[4],c[5],c[6],c[7],c[8],c[9]
						);
			writeln(bson);

		}
	}

}				

編譯日誌處理程式

dmd mlog.d				

用法

cat /var/log/nginx/access.log | mlog | mongo 192.169.0.5/logging -uxxx -pxxx				

處理壓錯過的日誌

# zcat /var/log/nginx/*.access.log-*.gz | /srv/mlog | mongo 192.168.6.1/logging -uneo -pchen				

實時採集日誌

tail -f /var/log/nginx/access.log | mlog | mongo 192.169.0.5/logging -uxxx -pxxx				

6.2. 日誌中心方案

上面的方案雖然簡單,但太依賴系統管理員,需要配置很多伺服器,每種應用軟體產生的日誌都不同,所以很複雜。如果中途出現故障,將會丟失一部日誌。

於是我又回到了起點,所有日誌存放在自己的伺服器上,定時將他們同步到日誌伺服器,這樣解決了日誌歸檔。遠端收集日誌,通過UDP協議推送彙總到日誌中心,這樣解決了日誌實時監控、抓取等等對實時性要求較高的需求。

為此我用了兩三天寫了一個軟體,下載地址:https://github.com/netkiller/logging

這種方案並不是最佳的,只是比較適合我的場景,而且我僅用了兩三天就完成了軟體的開發。後面我會進一步擴充套件,增加訊息佇列傳送日誌的功能。

6.2.1. 軟體安裝

$ git clone https://github.com/netkiller/logging.git
$ cd logging
$ python3 setup.py sdist
$ python3 setup.py install				

6.2.2. 節點推送端

安裝啟動指令碼

CentOS

# cp logging/init.d/ulog /etc/init.d			

Ubuntu

$ sudo cp init.d/ulog /etc/init.d/	

$ service ulog 
Usage: /etc/init.d/ulog {start|stop|status|restart}			

配置指令碼,開啟 /etc/init.d/ulog 檔案

配置日誌中心的IP地址

HOST=xxx.xxx.xxx.xxx				

然後配置埠與採集那些日誌

	done << EOF
1213 /var/log/nginx/access.log
1214 /tmp/test.log
1215 /tmp/$(date +"%Y-%m-%d.%H:%M:%S").log
EOF				

格式為

Port | Logfile
------------------------------
1213 /var/log/nginx/access.log
1214 /tmp/test.log
1215 /tmp/$(date +"%Y-%m-%d.%H:%M:%S").log				

1213 目的埠號(日誌中心埠)後面是你需要監控的日誌,如果日誌每日產生一個檔案寫法類似 /tmp/$(date +"%Y-%m-%d.%H:%M:%S").log

提示

每日產生一個新日誌檔案需要定時重啟 ulog 方法是 /etc/init.d/ulog restart

配置完成後啟動推送程式

# service ulog start				

檢視狀態

$ service ulog status
13865 pts/16   S      0:00 /usr/bin/python3 /usr/local/bin/rlog -d -H 127.0.0.1 -p 1213 /var/log/nginx/access.log				

停止推送

# service ulog stop				

6.2.3. 日誌收集端

# cp logging/init.d/ucollection /etc/init.d

# /etc/init.d/ucollection 
Usage: /etc/init.d/ucollection {start|stop|status|restart}				

配置接收埠與儲存檔案,開啟 /etc/init.d/ucollection 檔案,看到下面段落

				done << EOF
1213 /tmp/nginx/access.log
1214 /tmp/test/test.log
1215 /tmp/app/$(date +"%Y-%m-%d.%H:%M:%S").log
1216 /tmp/db/$(date +"%Y-%m-%d")/mysql.log
1217 /tmp/cache/$(date +"%Y")/$(date +"%m")/$(date +"%d")/cache.log
EOF				

格式如下,表示接收來自1213埠的資料,並儲存到/tmp/nginx/access.log檔案中。

Port | Logfile
1213 /tmp/nginx/access.log				

如果需要分割日誌配置如下

1217 /tmp/cache/$(date +"%Y")/$(date +"%m")/$(date +"%d")/cache.log				

上面配置日誌檔案將會產生在下面的目錄中

$ find /tmp/cache/
/tmp/cache/
/tmp/cache/2014
/tmp/cache/2014/12
/tmp/cache/2014/12/16
/tmp/cache/2014/12/16/cache.log				

提示

同樣,如果分割日誌需要重啟收集端程式。

啟動收集端

# service ulog start	

停止程式

# service ulog stop			

檢視狀態

$ init.d/ucollection status
12429 pts/16   S      0:00 /usr/bin/python3 /usr/local/bin/collection -d -p 1213 -l /tmp/nginx/access.log
12432 pts/16   S      0:00 /usr/bin/python3 /usr/local/bin/collection -d -p 1214 -l /tmp/test/test.log
12435 pts/16   S      0:00 /usr/bin/python3 /usr/local/bin/collection -d -p 1215 -l /tmp/app/2014-12-16.09:55:15.log
12438 pts/16   S      0:00 /usr/bin/python3 /usr/local/bin/collection -d -p 1216 -l /tmp/db/2014-12-16/mysql.log
12441 pts/16   S      0:00 /usr/bin/python3 /usr/local/bin/collection -d -p 1217 -l /tmp/cache/2014/12/16/cache.log				

6.2.4. 日誌監控

監控來自1217寬口的資料

$ collection -p 1213

192.168.6.20 - - [16/Dec/2014:15:06:23 +0800] "GET /journal/log.html HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36"
192.168.6.20 - - [16/Dec/2014:15:06:23 +0800] "GET /journal/docbook.css HTTP/1.1" 304 0 "http://192.168.6.2/journal/log.html" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36"
192.168.6.20 - - [16/Dec/2014:15:06:23 +0800] "GET /journal/journal.css HTTP/1.1" 304 0 "http://192.168.6.2/journal/log.html" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36"
192.168.6.20 - - [16/Dec/2014:15:06:23 +0800] "GET /images/by-nc-sa.png HTTP/1.1" 304 0 "http://192.168.6.2/journal/log.html" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36"
192.168.6.20 - - [16/Dec/2014:15:06:23 +0800] "GET /js/q.js HTTP/1.1" 304 0 "http://192.168.6.2/journal/log.html" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36"				

啟動後實時將最新日誌傳送過來