日誌歸檔與資料探勘
日誌歸檔與資料探勘
摘要
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"
啟動後實時將最新日誌傳送過來