通常來講,產品作出的原型多多少少會帶有「我的」傾向,UI設計的交互也會人所不一樣,而當公司生存下來了後,數據沉澱達到必定量了後,這種迭代就決不能拍腦殼了,由於人是具備偏見的,若是帶有「偏見」的產品上線後,其反響是不能預估的,咱們不能將公司的生存放在「可能」這種說法上。javascript
小步快跑,經過迭代來優化產品,但若是每一個迭代都顛覆了以前的設計,那就是原地踏步,每一次迭代都要知道這個迭代哪裏出了問題,而後再針對問題作優化,而不是頻繁的改版,持續優化,這個就必須創建在比較良好的數據監控與數據分析上,人有偏見可是數據不會,。html
因此大公司的核心產品,每個決策,每個迭代都須要分析各類數據,創建完善的AB Testing與小流量機制,待收到了充分的信息證實此次迭代是有效的後再作真正的全量更新。前端
數據中每每會有咱們須要的答案,好比前段時間,咱們發現咱們的訂單轉化率比較低,那麼咱們盯着轉換率自己是沒有意義的,咱們能夠考慮影響幾個數據的其餘指標:java
① 頁面PV,通常來講增大PV能有效增長轉化率node
② 按鈕點擊的前提,好比須要登陸後才能下單,和匿名下單的轉化率對比git
③ 優惠券使用狀況(聽說,中國沒有5元買不到的用戶)github
④ ......web
咱們不一樣的渠道,頗有可能產生這不一樣的場景,不一樣的場景下得到的數據,便能知道哪一種是咱們真實須要的,如此一來研發才能真正幫助公司作出正確的判斷,爲後續迭代提供參考。ajax
系列文章:數據庫
【數據可視化之數據定義】如何設計一個前端監控系統 描述如何獲取各類指標數據,如何歸類,首篇博客補足
【數據可視化之持久化】如何設計一個前端監控系統 描述如何作存儲(涉及大數據部分由其餘同事整理)
【數據可視化之圖表呈現(dashboard)】如何設計一個前端監控系統 描述如何將數據變爲有效的展現
代碼地址:https://github.com/yexiaochai/medlog
若是文中有誤的地方請您指出。
統計屬於海量數據的範疇,產品分析作的越細,所產生的數據量越大,好比我要作一個用戶點擊熱點的話,就須要收集用戶全部的點擊數據,這個多是pv的數十倍;另外一方面,海量統計應該是脫離業務自己的,用戶可定製化打點需求,以知足不一樣業務的變化。
瞭解了基本概念,咱們即可以肯定咱們到底須要什麼數據,這個拍腦殼想不出來,就能夠先進行基礎窮舉:
① pv&uv
② 頁面點擊(pv&uv)
③ 頁面來源(web處理這個有些困難),定義頁面從哪裏來,在海量數據的狀況下也能夠不記錄
④ 頁面停留時間(web不必定準確)
⑤ 前端錯誤日誌(這個比較龐大,後面詳述)
⑥ 首屏載入速度
⑦ 用戶環境收集(通常來講這個是附帶的)
⑧ 跨域資源監測(監測全部非白名單腳本,發現腳本注入行爲,附件特性)
而由於如今一套H5代碼會用於不一樣的平臺入口,因此這些數據又會額外具備「渠道信息」的標誌。
再咱們有了以上數據的狀況下,咱們能很輕易的得出某個渠道的轉化率:
由於不一樣渠道表現也許會有所不一樣,有可能微信渠道的入口在首頁,他的轉化率計算通常會通過這麼一個過程:
首頁pv -> 列表頁pv -> 訂單填寫頁pv -> 下單按鈕點擊pv -> server最終成單數
而搜索引擎入口,可能直接就到了訂單填寫頁,因此轉化率計算公式又變成了:
訂單填寫頁pv -> 下單按鈕點擊pv -> server最終成單數
這裏結合首屏載入速度與頁面停留時間,輔以用戶點擊軌跡,就能從某些程度上,追蹤分析一個用戶的行爲了。
曾今有一次咱們發現咱們訂單轉化率降低了50%,因而老闆讓咱們立刻給出緣由,咱們當時懷疑過BUG,懷疑過運營商接口有問題,可是咱們全部的推論都沒有很好的佐證,因而各類查詢數據庫,最後與整個打點的pv數據,咱們便得出了一個結論:
由於,多數用戶的優惠券過時了,因此轉化率急劇降低!!!
爲了證實這個猜測,咱們由將某一個渠道的優惠券加上,次日轉化率就回歸了,咱們這裏能判斷出轉化率降低的緣由和咱們平時完善的打點是息息相關的。
另外一方面,當代碼迭代到必定量的時候,code review也就只能解決很小一部分問題了,前端預警和前端錯誤日誌產生的蛛絲馬跡纔會將一些隱藏的很深的BUG揪出來,全部的這一切都須要從數據採集開始。
我原來也遇到一個BUG,潛伏期很長,並且只有在特定的場景纔會觸發,這種BUG通常來講測試是無力的,當時我發現2個版本的日誌有些奇怪的錯誤,再一步步抽絲剝繭,終於定位到了那個錯誤,當咱們代碼量大了後,合理的錯誤埋點+前端監控才能讓系統變得更健康。
這裏引用一張錯誤監控圖(http://rapheal.sinaapp.com/2014/11/06/javascript-error-monitor/):
這裏將上一週的錯誤數與本週作對比,若是沒有大的迭代,稍微有異常就會產生報警,通常來講用戶纔是最好的測試,上線後沒有報警就沒有BUG。
PS:原來咱們每次大版本發佈,60%的概率要回滾......
前端錯誤捕捉,通常使用onerror,這個偶爾會被try cache影響:
1 window.onerror = function (msg, url, line, col, error) { 2 //...... 3 }
當時生產上的錯誤日誌由於是壓縮過的,真實抓到的錯誤信息十分難看:
錯誤信息所有是第一行,報錯的地方也是作過混淆的,若是不是頁面劃分的過開,這個錯誤會讓人一頭霧水,要想深刻了解錯誤信息,這裏即可以瞭解下source map了
簡單來講,sourcemap是一個信息文件,裏面存儲着位置信息,也就是說,在js代碼壓縮混淆合併後的每一個代碼位置,對應的源碼行列都是有標誌的,有了這個source map,咱們就能直接將源碼對應的錯誤上報回去,大大下降咱們的錯誤定位成本。
這裏不一樣的業務使用的不一樣的構建工具,這裏以grunt爲例,grunt打包通常來講是使用的require,這裏須要爲其配置加入一段代碼便可:
1 "generateSourceMaps": true, 2 "preserveLicenseComments": false, 3 "optimize": "uglify2",
上面那個有一些問題,他將個人關鍵字過濾了,最後採用的這個:
而後就會生成你要的sourcemap了
能夠看到壓縮文件中,包含了map引用:
因而咱們線上代碼就會變成這個樣子:
這個時候,咱們故意寫個錯誤的話,這裏查看報錯:
雖然看到的是源碼,可是上報的數據彷佛沒有什麼意義,這個時候能夠藉助一些第三方工具對日誌作二次解析:
Sentry(GitHub - getsentry/sentry: Sentry is cross-platform crash reporting built with love)
而且,顯然咱們並不但願咱們的源代碼被人看到,因此咱們將sourcemap文件存到線下,在線下將日誌反應爲咱們看得懂的源碼,這裏簡單看看這個文件定義:
1 - version:Source map的版本,目前爲3。 2 - file:轉換後的文件名。 3 - sourceRoot:轉換前的文件所在的目錄。若是與轉換前的文件在同一目錄,該項爲空。 4 - sources:轉換前的文件。該項是一個數組,表示可能存在多個文件合併。 5 - names:轉換前的全部變量名和屬性名。 6 - mappings:記錄位置信息的字符串。
sourcemap的機制什麼的,就不是我關注的重點,想了解的能夠看阮老師的博客,我如今的需求是:
獲取了列號和行,如何能夠在線下映射成咱們要的真實行號
好比咱們拿到了上述的行號與列號爲{1,13310},那麼咱們這裏真實映射的是,合併文件中的某一行:
要完成這一切,咱們須要一套「錯誤還原」的後臺系統,這個直接坐到js監控其中一環就好,好比咱們能夠簡單這樣作:
這個被一國外網站實現了(通常來講要錢的......),因此是能夠實現的,咱們便去追尋他的實現便可。
後續在github找了一個庫,完成了相似的功能,這裏使用nodejs:
1 var mapData = require('./index.json'); 2 // console.log(sourceMap); 3 var sourceMap = require('source-map'); 4 var consumer = new sourceMap.SourceMapConsumer(mapData); 5 var numInfo = consumer.originalPositionFor({ line: 1, column: 13330 }) 6 console.log(numInfo)
輸出==>
1 { source: 'pages/index/index.js', 2 line: 182, 3 column: 0, 4 name: 'layoutHtml' }
因而,咱們已經找到了本身要的東西了。最初,在快速調研的時候,咱們不要知道https://github.com/mozilla/source-map是幹什麼的,可是若是咱們決定使用的話,就須要去仔細研究一番了。
總而言之,線上錯誤日誌蒐集的行號信息,在線下平臺便能很好的翻譯了,這裏方案有了,我接下來立刻想法落地,落地狀況在存儲篇反饋
錯誤日誌這裏,由於比較重要,也與普通的打點不同,佔的篇幅有點長,咱們這裏先繼續往下說,等日誌簡單落地後再詳述。
原本,咱們數據採集可使用百度或者友盟,可是總有那麼一些東西得不到知足,並且也沒有數據對外輸出的API,而公司若是穩步上升的話,作這塊是早晚的事情,因此宜早不宜遲吧,而我這裏主要仍是先關注的移動體系,因此不太會關注兼容性,這個能夠少考慮一些東西,真的遇到一些狀況如跨域什麼的,咱們後面再說吧。
關於存儲一塊有不少須要考慮,好比如何計算首屏加載時間,webapp和傳統網易的異同,hybrid的差別,uv的計算方法等都須要考慮,可是咱們今天變只將採集代碼實現便可,剩下的下篇再處理。
簡單來說,日誌採集,其實就是一個get請求,你就算想用ajax發出去也是沒有問題的,爲了加入額外信息可能咱們會作一個收口:
1 ajax(url, { 2 s: '' 3 b: '' 4 c: '' 5 });
可是這個不是主流的作法,通常來講,咱們打點信息使用的圖片的方式發出,而由於重複的請求會被瀏覽器忽略,咱們甚至會加入uniqueId作標誌:
1 var log = function () { 2 var img = new Image(); 3 img.src = 'http://domain.com/bi/event?'+ uniqueId; 4 };
基本的採集實現就這麼簡單,可是後續逐步完善的功能,會增長複雜度,因而我創建了一個git倉庫存儲代碼,後續大數據一塊的代碼也將放到這裏:
https://github.com/yexiaochai/medlog
閉門造車的意義不大,翻看前輩的一些採集代碼好比alog,會發現他打點的一塊是這樣作的:
1 /** 2 * 上報數據 3 * 4 * @param {string} url 目標連接 5 * @param {Object} data 上報數據 6 */ 7 function report(url, data) { 8 if (!url || !data) { 9 return; 10 } 11 // @see http://jsperf.com/new-image-vs-createelement-img 12 var image = doc.createElement('img'); 13 var items = []; 14 for (var key in data) { 15 if (data[key]) { 16 items.push(key + '=' + encodeURIComponent(data[key])); 17 } 18 } 19 var name = 'img_' + (+new Date()); 20 entry[name] = image; 21 image.onload = image.onerror = function () { 22 entry[name] = 23 image = 24 image.onload = 25 image.onerror = null; 26 delete entry[name]; 27 }; 28 image.src = url + (url.indexOf('?') < 0 ? '?' : '&') + items.join('&'); 29 }
其中有一塊差別是綁定了onload等事件,應該是想釋放資源吧?
這裏的代碼,想與公司業務管理起來,好比根據業務線或者項目生成某個規則的id,上報代碼比較簡單,可是每次都要帶哪些信息,還沒很好的思路,先在這裏立一個flag吧,接下來時間裏全力補足吧,畢竟這塊東西不少。
前端數據有不少須要處理的地方,而數據的核心分爲數據採集打點,數據持久化,數據使用,數據分析。
打點又會區分H5打點與native打點,native因爲權限自己,能作的事情更多,可是底層數據收集基本能作到統一。
採集的代碼是其中一部分,但採集的各項數據獲取是另外一個更重要的部分,會包含數據設計,各類細節處理,咱們下篇文章接着研究,有興趣的同窗可關注。