標籤:
在大型網站做很多使用者行為分析、產品的策劃方案基本上都是通過分析使用者的訪問等資訊而做出的,LOG資訊的統計準確性會直接影響到產品的設計開發(比如搜尋結果的先後排名rank值的產生等)。目前最常用的一個寫LOG的方法就是用JavaScript指令碼在網頁裡 new Image().src = "http://xxx.com/log?msg="+ msg; 這種統計方法基本上不會干擾使用者的正常操作,雖然有LOG丟失的可能,但只要用得好,還是一種非常好的LOG統計回收的方案。
但是這種LOG回收手段有一個非常隱秘的隱患,事情是這樣被發現的。今年3月份百度搜尋結果頁面上線了Suggestion功能更新版,在LOG統計中突然發現上線後的LOG總量比上線前的少了很多(10%以上),表現出來的現象就是LOG丟失了,沒有回收到伺服器中來。
工程師立即做了詳細排除,從各種因素上確定LOG的大量丟失跟網頁上線Suggestion有關,我們馬上對Suggestion指令碼進行地毯式排查,逐行分析代碼,結論是這個指令碼沒有阻攔 new Image().src 的發包請求(至少表面上是這樣的),這段指令碼放在一個指令碼閉包中,沒有影響到全域變數/方法,也沒有屏蔽幹擾HTTP請求的因素。
我們又做了一個線下實驗,同樣的代碼環境,線上下的測試環境中用這種 new Image().src 的手段,總共向Server端發送了10000個LOG資料,也沒有發生LOG丟失,實驗的結果也沒有發現LOG丟失的原因。
但是線上上的環境中發生的就是有LOG資料丟失,原因不明,所以只能緊急下線這個Suggestion升級版。在這個指令碼下線之後,LOG統計資料馬上迴歸到“原正常”狀態。從這個現象來看,也從另一個角度說明上線的新指令碼確實對LOG統計有影響,頭疼呀......
愚者千慮,必有一得!最後方知這個問題的原因是瀏覽器的記憶體回收機制!
function c(q) { var p=window.document.location.href,sQ=‘‘,sV=‘‘; for(v in q){ switch (v){ case "title":sV=encodeURIComponent(q[v].replace(/<[^<>]+>/g,""));break; case "url":sV=escape(q[v]);break; default:sV=q[v] } sQ+=v+"="+sV+"&"; } new Image().src = "http://s.baidu.com/w.gif?q=meizz&"+sQ+"path="+p+"&cid=9&t="+ new Date().getTime(); return true;}
這個 new Image() 對象沒有賦給任何變數,在這個函數執行結束時,瀏覽器的記憶體回收機制對這種“無主”的對象是毫不客氣的回收的,而正是這種回收行為導致了這個HTTP請求(非同步)沒有發出,從而造成了LOG資料的丟失。那為什麼上線一個指令碼就會造成大量的LOG丟失呢?因為一個大指令碼的運行回產生大量的“垃圾”,瀏覽器記憶體回收也會相應地更頻繁的啟動,從而造成LOG資料丟失。找到原因之後對症下藥,把這個 new Image() 對象賦給一個全域有變數常期持有即可,相應的代碼如下:
var n = "log_"+ (newDate()).getTime();var c = window[n] =newImage(); //把new Image()賦給一個全域變數長期持有c.onload = (c.onerror=function(){window[n] = null;});c.src = "http://s.baidu.com/w.gif?q=meizz"+ xxxx;c = null; //釋放局部變數c
在這個統計代碼上線之後,百度的搜尋結果頁面的LOG立即多出近10%,之後再上線其它的指令碼也沒有再出現LOG統計量的波動。
局部變數下,測試連續發1000個log,IE6-7發現log丟失,某些IE核心瀏覽器也發現log丟失,chrome下未發現丟失。
因此 保險的做法是用一個非局部變數持有:
var unique = (function () { var time= (new Date()).getTime()+‘-‘, i=0; return function () { return time + (i++); } })(); var imgLog = function (url) { var data = window[‘imgLogData‘] || (window[‘imgLogData‘] = {}); var img = new Image(); var uid = unique(); img.onload = img.onerror = function () { //銷毀一些對象 img.onload = img.onerror = null; img = null; delete data[uid]; } img.src = url + ‘&_uid=‘ + uid; };
本文源自FuDesign2008
【轉】用new Image().src作LOG統計的一個注意事項