利用MongoDB分析Nginx日誌

原文地址:html

http://52sox.com/use-mongodb-...python

在項目開發過程當中,老是離不開日誌解析的工做,雖然有些時候以爲確實挺繁瑣的,可是靜下心來會發現有時候也是挺有趣的1件工做。
在這裏,咱們要從日誌文件中找出IP訪問最多的10條記錄,而後判斷其是否合法,從而採起對應的措施。nginx

日誌解析流程

正常狀況下,關於Nginx日誌解析的流程以下所示:正則表達式

流程圖

通常狀況下咱們會對要解析的日誌提早進行切分,經常使用的方式是按照日期,而後保存1個星期的日誌。而後接下來就是日誌的解析了,在這個過程當中會使用到一些工具或編程語言,例如awk、grep、perl、python。
最後的入庫和可視化處理通常視業務而定,沒有強制的要求。sql

日誌查詢的解決方案

而關於Nginx日誌解析的經常使用解決方案主要有以下4種方式:mongodb

  1. 經過awk和grep進行解析數據庫

  2. 經過Postgresql外聯表進行日誌的映射編程

  3. 經過Python與MongoDB的組合來進行日誌查詢api

  4. 經過ELK這個開源套件進行查詢微信

其中Postgresql外聯表的方式在以前公司的時候已經使用過,固然是對公司多個3GB大小的日誌進行處理。而第1種和第4種解決方案沒有太多的實踐的經驗,這裏咱們主要來看第2種解決方案。

日誌格式

關於日誌解析處理,咱們比較經常使用的方式是使用正則表達式來進行匹配,而經常使用的1個庫是nginxparser,咱們能夠直接經過pip進行安裝。固然還有其餘的方式來進行解析,這個要視業務而定。
在日誌解析中,比較重要的是日誌的格式,默認狀況下Nginx的日誌格式以下:

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

下面咱們來看實際業務中的1個應用。以前公司有1個搶微信紅包的活動,固然有用戶反映好幾天都沒法搶到1個紅包。所以,咱們團隊成員認爲可能在這個過程當中存在做弊的現象,所以便決定對Nginx的日誌進行解析。詳細內容能夠點擊優化微信紅包搶購系統
下面是1條真實的日誌的記錄:

101.226.89.14 - - [10/Jul/2016:07:28:32 +0800] "GET /pocketmoney-2016-XiKXCpCK.html HTTP/1.1" 302 231 "-" "Mozilla/5.0 (Linux; Android 5.1; OPPO R9tm Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile MQQBrowser/6.2 TBS/036548 Safari/537.36 MicroMessenger/6.3.22.821 NetType/WIFI Language/zh_CN"

日誌分析

經過awk進行解析

接着,咱們來看下如何使用awk解析出IP訪問最多的記錄,關於awk語法能夠參考進行學習:

dog@dog-pc:~$ awk '{a[$1]++}END{for(i in a)print i,a[i]}' nginx.log |sort -t ' ' -k2 -rn|head -n 10
111.167.50.208 26794
183.28.6.143 16244
118.76.216.77 9560
14.148.114.213 3609
183.50.96.127 3377
220.115.235.21 3246
222.84.160.249 2905
121.42.0.16 2212
14.208.240.200 2000
14.17.37.143 1993

默認狀況下,awk以空格做爲分隔符號,所以$1將獲取到Nginx默認格式中的遠程地址。在這裏,咱們經過定義1個字段,使用IP做爲鍵名,若是對應的鍵名存在則將其數量加1處理。最後咱們遍歷這個字典,以後經過數量進行排序,最後經過head獲取10條記錄。
固然這種操做方式是有較大偏差的,由於咱們沒有指定狀態碼等其餘條件,下面咱們來看根據狀態碼和請求方式這2個條件後過濾的數據:

dog@dog-pc:~$ awk '{if($9>0 && $9==200 && substr($6,2)== "GET") a[$1]++}END{for(i in a)print i,a[i]}' nginx.log|sort -t ' ' -k2 -rn|head -n 10
222.84.160.249 2856
183.28.6.143 2534
116.1.127.110 1625
14.208.240.200 1521
14.17.37.143 1335
219.133.40.13 1014
219.133.40.15 994
14.17.37.144 988
14.17.37.161 960
183.61.51.195 944

這樣咱們就能夠將這10個IP進行分析,考慮下一步的操做,好比經過iptables組合禁止該IP的訪問或限制其訪問的次數等。

經過Postgresql

經過Postgresql入庫後使用SQL進行查詢的方式能夠經過以下2種圖片來查看:

status

在上圖中主要是查看日誌中請求狀態碼的總數量。而下圖是對狀態碼爲200的前10條IP的篩選:

ip

能夠看到基本上與上面awk解析的方式一致。

經過MongoDB進行查詢

咱們知道,MongoDB是1個文檔型數據庫,經過這個數據庫咱們輔助解決關係型數據庫一些不太擅長的工做。
在Python中,主要的MongoDB客戶端驅動是PyMongo,咱們能夠經過以下的方式創建1個鏈接:

In [1]: from pymongo import MongoClient
In [2]: client = MongoClient()

因爲這裏咱們使用的是默認的端口和地址,所以在MongoClient類中不傳入任何的參數。
在這裏,咱們先說下咱們插入到MongoDB中日誌的格式:

{
    "status": 302, //HTTP狀態碼
    "addr": "101.226.89.14", //遠程IP地址
    "url": "-",
    "req": "/pocketmoney-2016-XiCXCpCK.html", //請求的地址
    "agent": "Mozilla/5.0 (Linux; Android 5.1; OPPO R9tm Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile MQQBrowser/6.2 TBS/036548 Safari/537.36 MicroMessenger/6.3.22.821 NetType/WIFI Language/zh_CN", //請求的user-agent
    "referer": "NetType/WIFI",
    "t": "2016/07/10 06:28:32", //請求的時間
    "size": 231, //響應的大小
    "method": "GET", //請求的方法
    "user": "-" //用戶名稱
}

在這裏咱們經過Python進行解析後,組裝成如上的格式後插入到MongoDB中,在這裏主要用到的是MongoDB文檔對象的insert_one方法插入1條記錄。

db = client['log']
col = db['nginx']
data = {}
...
col.insert_one(data)

接着咱們開始對上述的記錄進行查詢操做,主要是經過MongoDB提供的map-reduce來實現聚合操做,其對應的Python代碼爲:

In [3]: db = client['log']
In [4]: col = db['nginx']
In [5]: pipeline = [
    ...: {"$match":{"status":200}},
    ...: {"$group":{"_id":"$addr","count":{"$sum":1}}},
    ...: {"$sort":{"count":-1}},
    ...: {"$limit":10}
    ...: ]
In [6]: list(col.aggregate(pipeline))
Out[6]: 
[{u'_id': u'222.84.160.249', u'count': 2856},
 {u'_id': u'183.28.6.143', u'count': 2534},
 {u'_id': u'116.1.127.110', u'count': 1625},
 {u'_id': u'14.208.240.200', u'count': 1521},
 {u'_id': u'14.17.37.143', u'count': 1335},
 {u'_id': u'219.133.40.13', u'count': 1014},
 {u'_id': u'219.133.40.15', u'count': 994},
 {u'_id': u'14.17.37.144', u'count': 988},
 {u'_id': u'14.17.37.161', u'count': 960},
 {u'_id': u'183.61.51.195', u'count': 944}]

能夠看到這個過程與以前的2種方式獲得的結果是一致的。

關於可視化處理

關於可視化處理,咱們能夠選擇一些Javascript的庫,例如:

  • 百度的Echarts

  • d3.js及其衍生的庫

對於Python,可視化處理可使用以下的一些庫:

  • matplotlib

  • pandas

固然還有一些其餘的庫這裏就不一一敘述了。
下面是1個使用百度Echart繪製的界面:

visual

看起來仍是挺漂亮的。

參考文章:

http://api.mongodb.com/python...

相關文章
相關標籤/搜索