10min系列之二日志可視化進階(作者原創,同步發布在github)
本文需要有一定的python和前端基礎,如果沒基礎的,請關注我后續的基礎教程系列博客
本文所有的demo,都是瀏覽器下展示的
原創文章,本文同步發布在github,跪求github右上角star
基於python,前端基於echarts,力求用簡單的代碼說明原理
提綱
- access日志按照什么維度展示數據
- 和web結合,更好的體驗,只需記住URL
- 分頁排序和搜索,表格展示體驗的提升
- 一圖勝萬言,匯總信息更友好的展示
- 逼格滿滿,讓IP信息在地圖上展示
- 后續展望--機房網絡流量可視化,機器、機櫃、機房3D展示
- 希望能給大家帶來一些處理log的思路
背景
老板要看日志數據匯總
- 本文重點:如何做可視化
- 目標:如何用友好的方式去展現沉悶繁冗的數據
- 下個月就發年終獎!!!
- 為了說明可視化的方式,用一個簡單的log舉例子
原材料
- 一個標准的access_log日志 大概2W行
- 老板想要這個日志的分析結果,每個url,ip,status分別訪問多少次,把前幾名統計出來看看
- 分析出統計數據 展現結果
很普通的日志,大概長這樣
為了方便展示,切割了一下,大概2W行
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
66.249.64.5 - - [23/Aug/2014:00:02:16 +0800] "GET /data/uploads/2013/0519/09/small_51982ba18e012.jpg HTTP/1.1" 200 \ "-" "Googlebot-Image/1.0" "-"
66.249.64.10 - - [23/Aug/2014:00:02:54 +0800] "GET /data/uploads/2013/0319/08/middle_5147b116e93b4.jpg HTTP/1.1" 200 \ "-" "Googlebot-Image/1.0" "-"
目標
- 初級展現
- 友好交互
- 餅圖匯總
- 更進一步
- 后續展望
級別1
- 數據處理,命令行展現
- 打開文件,處理完數據后排序
- 打印前10
talk is cheap, show me the money code!
# coding=utf-8
f = open('www_access_20140823.log')
res = {}
for l in f:
arr = l.split(' ')
# 獲取ip url 和status
ip = arr[0]
url = arr[6]
status = arr[8]
# ip url 和status當key,每次統計+1
res[(ip,url,status)] = res.get((ip,url,status),0)+1
# 生成一個臨時的list
res_list = [(k[0],k[1],k[2],v) for k,v in res.items()]
# 按照統計數量排序,打印前10
for k in sorted(res_list,key=lambda x:x[3],reverse=True)[:10]:
print k
處理結果
('222.86.153.12', '/images/cursor_minify.cur', '404', 60)
('222.86.153.12', '/images/cursor_zoom.cur', '404', 32)
('58.253.6.133', '/images/cursor_minify.cur', '404', 32)
('111.85.34.165', '/%3Ca%20href=', '404', 28)
('58.253.6.133', '/images/cursor_zoom.cur', '404', 27)
('218.29.111.117', '/images/cursor_zoom.cur', '404', 27)
('218.29.111.117', '/images/cursor_minify.cur', '404', 26)
('117.63.146.40', '/public/js/common.js?20110824', '200', 19)
('117.63.146.40', '/favicon.ico', '404', 18)
('117.63.146.40', '/public/js/weibo.js?20110824', '200', 16)
任務完成
- 下一步粘到郵件里,或者生成一個csv文件發出去
- 然而這是一個看臉的社會,運維也逃脫不了這個魔咒
級別2
瀏覽器端展現
生成list之后,拼接sql,存入數據庫
talk is cheap, show me the money code!
import MySQLdb as mysql
con = mysql.connect(user='root',\
passwd='',\
db='log',\
host='localhost')
con.autocommit(True)
cur = con.cursor()
# 處理文件省略
for s in res_list:
sql = 'insert log values ("%s","%s",%s,%s)' % s
try:
# 入庫
cur.execute(sql)
except Exception, e:
pass
前端展現
讀庫 展現頁面
talk is cheap, show me the money code!
from flask import Flask,request,render_template
app = Flask(__name__)
import MySQLdb as mysql
con = mysql.connect(user='xx',\
passwd='xx',\
db='xx')
cur = con.cursor()
@app.route('/')
def index():
table = '<table border="1">'
cur.execute('select * from log order by value desc limit 20; ')
for c in cur.fetchall():
table += '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>'%c
table +='</table>'
return table
if __name__ == '__main__':
app.run(host='0.0.0.0',port=9092)
給老板一個url即可,老板想看隨時能看
但是老板表示有點丑
完整的思路

前端展現上做一些優化
- 分頁
- 表格排序
- 搜索
- 控制每頁顯示數量
- 數據多了之后,前端交互和后端數據的接口配合
不動戳大

我們的console頁面,提供幾個匯總信息,那就更好啦
比如根據http的status來個匯總
難不倒我
一句sql搞定
select status,sum(value) from log group by status
+--------+------------+
| status | sum(value) |
+--------+------------+
| 200 | 15529 |
| 206 | 6 |
| 301 | 2 |
| 304 | 3549 |
| 403 | 1 |
| 404 | 847 |
+--------+------------+
6 rows in set (0.02 sec)
匯總信息可視化
根據https狀態匯總
其他功能
- 圖例開關
- 圖表轉換
- 數據視圖
- 直接導出圖片
可視化並不僅限於此
上面只是舉得小栗子
如果你對時間更感興趣,我們的log里也是有時間信息的,可以像下面這樣
統計量,時間軸拖動,保存圖片,etc

更進一步
如何讓日志數據更加一目了然,讓老板覺得你很有逼格呢
IP都是有地址位置的,定位每個ip的位置,畫個地圖出來匯總
經緯度坐標系統
- 地球坐標(WGS84)
- 國際標准,從 GPS 設備中取出的數據的坐標系
- 國際地圖提供商使用的坐標系
- 火星坐標(GCJ-02)
- 中國標准,從國行移動設備中定位獲取的坐標數據使用這個坐標系
- 百度坐標(BD-09)
- 百度標准,百度 SDK,百度地圖,Geocoding 使用
應用場景
- WGS84坐標系:
- 國際標准,谷歌國外地圖、osm地圖等國外的地圖一般都是這個
- 火星坐標系:
- iOS 地圖
- Gogole地圖
- 搜搜、阿里雲、高德地圖
- 百度坐標系:
- 當然只有百度地圖
地圖是需要經緯度的,用第三方的ip庫轉換一下
http://developer.baidu.com/map/index.php?title=webapi/ip-api

talk is cheap, show me the money code!
import urllib2
import json
key = 'q5mTrTGzCSVq5QmGpI9y18Bo'
ipurl = 'http://api.map.baidu.com/location/ip?ak='+key+'&coor=bd09ll&ip='
sqlarr = []
def getGeo(ip):
try:
u = urllib2.urlopen(ipurl+ip)
page = json.load(u)
if 'content' in page:
point = page['content'].get('point')
print 'ip %s has geoX %s and geoY %s' % (ip,point['x'],point['y'])
except:
print 'error'
getGeo('202.198.16.3')
# ip 202.198.16.3 has geoX 125.31364243 and geoY 43.89833761
就想玩網游時候,坐標可以定位一個人,經緯度可以再地圖上定位一個點,畫圖展現
剛才那個圖僅關注區域,進階一下,還要關注訪問量
可以根據value篩選
進階:多台機器的日志
- 獲取每個機器的hostname和ip,和日志數據一起存在數據庫里
- 一個表存日志,帶上一個機器的id
- 機器的id=>ip和經緯度
- 最終統計訪問量
后續擴展
- 日志數據
- 前端展現場景
怎么實踐
- 這次分享的主題關注與可視化
- 我們用了一個很小的靜態日志,目的是說明可視化的思路
- 實際工作中日志數據應該怎么處理
elk
Logstash+ElasticSearch+Kibana4
- logstash
- 日志進行收集、分析,並將其存儲供使用
- ElasticSearch
- 開源分布式搜索引擎,
- Kibana4
- 日志分析友好的 Web 界面
- 其他
- Kafka scribe等
常見日志處理架構
- ELK
- logstash+Hadoop
- scribe+hadoop
- 線上數據->Flume->Kafka->Hdfs->Map/Reduce
- 線上數據->flume->kafka->storm
- 在上面的基礎上定制化二次開發,比如MR平台上寫代碼,我們的代碼就可以直接拿來用
- 日志的數據處理架構詳情,請見下回分解
前端展現場景
- 展現逼格更高一些,數據一樣,效果更好
- 運維人員權限樹
- 流量圖
- 年終數據統計
- 區域點擊統計圖
- 3D機房
大家具體需要哪個,可以繼續擴展,給大家展現幾個假數據的demo
高逼格餅圖展示狀態匯總(假數據,可以替換為http_status)
人員權限樹(假數據,可以作為運維人員權限展示)
流量圖(假數據,可以作為機房之間,或者網卡的流量)
日志統計匯總(假數據,可以用來展示年日志數據匯總)
區域數據匯總餅圖展示(假數據,可選擇省份,生成餅圖)
3D展示機房(網上盜圖,后續會做一個類似的開源)
json生成,實時查看機器狀態,點擊時間
3D展示不止於此 有圖有XX
謝謝!
原創文章,本文同步發布在github 繼續求star