離線數(shù)據(jù)分析平臺實戰(zhàn)——250JSSDK數(shù)據(jù)收集引擎編寫
JsSDK設(shè)計規(guī)則
在js sdk中我們需要收集launch、pageview话浇、chargeRequest和eventDuration四種數(shù)據(jù),
所以我們需要在js中寫入四個方法來分別收集這些數(shù)據(jù)这揣,
另外我們還需要提供一些操作cookie和發(fā)送數(shù)據(jù)的的公用方法踊谋。
SDK測試
啟動集群上的hdfs+nginx+flume進(jìn)程立由,
通過模擬數(shù)據(jù)的發(fā)送然后將數(shù)據(jù)發(fā)送到nginx服務(wù)器中,
查看最終是否在hdfs中有數(shù)據(jù)的寫入洼裤。
命令:
啟動hdfs
start-dfs.sh
: 命令
su root
:切換用戶
啟動nginx
service nginx restart
: 進(jìn)程
啟動flume進(jìn)程:
進(jìn)入flume安裝根目錄邻辉,執(zhí)行命令:
flume-ng agent --conf ./conf/ --conf-file ./conf/test2.conf --name agent &
參考資料
一、將analytics.js集成到你想收集收集的頁面即可逸邦。
集成方式主要分為以下兩種:
1) 第一種方式
`將analytics.js集成到所有頁面的的頭部恩沛,然后通過提供的方法調(diào)用進(jìn)行數(shù)據(jù)收集。
<script src='//www.bjsxt.com/js/common/analytics.js'></script>
<script>__AE__.setMemberId("123456");</script>
2) 第二種方式
`使用javascript代碼缕减,異步引入analytics.js文件雷客,在引入之前可以通過_aelog_設(shè)置會員id
<script type="text/javascript">
var _aelog_ = _aelog_ || window._aelog_ || [];
_aelog_.push(['member_id', '123456']); // 如果此時用戶已經(jīng)登錄,那么通過才參數(shù)指定用戶id
window._aelog_ = _aelog_;
(function() {
var amwae = document.createElement('script');
amwae.type = 'text/javascript';
amwae.async = true;
amwae.src = '//www.beifeng.com/js/common/analytics.js'; // 指定鏈接
var script = document.getElementsByTagName('script')[0];
script.parentNode.insertBefore(amwae, script);
})();
</script>
注意:當(dāng)用戶登錄后桥狡,請調(diào)用__AE__.setMemberId('123456')方法進(jìn)行會員id的設(shè)置搅裙,方便用戶數(shù)據(jù)的收集。
(function(){
var CookieUtil = {
// get the cookie of the key is name
get: function(name) {
var cookieName = encodeURIComponent(name) + "=",
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null;
if (cookieStart > -1) {
var cookieEnd = document.cookie.indexOf(";", cookieStart);
if (cookieEnd == -1) {
cookieEnd = document.cookie.length;
}
cookieValue = decodeURIComponent(document.cookie.substring(cookieStart+cookieName.length, cookieEnd));
}
return cookieValue;
},
// set the name/value pair to browser cookie
set: function(name, value, expires, path, domain, secure) {
var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
if (expires) {
// set the expires time
var expiresTime = new Date();
expiresTime.setTime(expires);
cookieText += ";expires=" + expiresTime.toGMTString();
}
if (path) {
cookieText += ";path=" + path;
}
if (domain) {
cookieText += ";domain=" + domain;
}
if (secure) {
cookieText += ";secure";
}
document.cookie = cookieText;
},
setExt: function(name, value) {
this.set(name, value, new Date().getTime() + 315360000000, "/");
}
};
// 主體裹芝,其實就是tracker js
var tracker = {
// config
clientConfig: {
serverUrl: "http://hh/log.gif",
sessionTimeout: 360, // 360s -> 6min
maxWaitTime: 3600, // 3600s -> 60min -> 1h
ver: "1"
},
cookieExpiresTime: 315360000000, // cookie過期時間部逮,10年
columns: {
// 發(fā)送到服務(wù)器的列名稱
eventName: "en",
version: "ver",
platform: "pl",
sdk: "sdk",
uuid: "u_ud",
memberId: "u_mid",
sessionId: "u_sd",
clientTime: "c_time",
language: "l",
userAgent: "b_iev",
resolution: "b_rst",
currentUrl: "p_url",
referrerUrl: "p_ref",
title: "tt",
orderId: "oid",
orderName: "on",
currencyAmount: "cua",
currencyType: "cut",
paymentType: "pt",
category: "ca",
action: "ac",
kv: "kv_",
duration: "du"
},
keys: {
pageView: "e_pv",
chargeRequestEvent: "e_crt",
launch: "e_l",
eventDurationEvent: "e_e",
sid: "cktrack_sid",
uuid: "cktrack_uuid",
mid: "cktrack_mid",
preVisitTime: "cktrack_previsit",
},
/**
* 獲取會話id
*/
getSid: function() {
return CookieUtil.get(this.keys.sid);
},
/**
* 保存會話id到cookie
*/
setSid: function(sid) {
if (sid) {
CookieUtil.setExt(this.keys.sid, sid);
}
},
/**
* 獲取uuid,從cookie中
*/
getUuid: function() {
return CookieUtil.get(this.keys.uuid);
},
/**
* 保存uuid到cookie
*/
setUuid: function(uuid) {
if (uuid) {
CookieUtil.setExt(this.keys.uuid, uuid);
}
},
/**
* 獲取memberID
*/
getMemberId: function() {
return CookieUtil.get(this.keys.mid);
},
/**
* 設(shè)置mid
*/
setMemberId: function(mid) {
if (mid) {
CookieUtil.setExt(this.keys.mid, mid);
}
},
startSession: function() {
// 加載js就觸發(fā)的方法
if (this.getSid()) {
// 會話id存在嫂易,表示uuid也存在
if (this.isSessionTimeout()) {
// 會話過期,產(chǎn)生新的會話
this.createNewSession();
} else {
// 會話沒有過期兄朋,更新最近訪問時間
this.updatePreVisitTime(new Date().getTime());
}
} else {
// 會話id不存在,表示uuid也不存在
this.createNewSession();
}
this.onPageView();
},
onLaunch: function() {
// 觸發(fā)launch事件
var launch = {};
launch[this.columns.eventName] = this.keys.launch; // 設(shè)置事件名稱
this.setCommonColumns(launch); // 設(shè)置公用columns
this.sendDataToServer(this.parseParam(launch)); // 最終發(fā)送編碼后的數(shù)據(jù)
},
onPageView: function() {
// 觸發(fā)page view事件
if (this.preCallApi()) {
var time = new Date().getTime();
var pageviewEvent = {};
pageviewEvent[this.columns.eventName] = this.keys.pageView;
pageviewEvent[this.columns.currentUrl] = window.location.href; // 設(shè)置當(dāng)前url
pageviewEvent[this.columns.referrerUrl] = document.referrer; // 設(shè)置前一個頁面的url
pageviewEvent[this.columns.title] = document.title; // 設(shè)置title
this.setCommonColumns(pageviewEvent); // 設(shè)置公用columns
this.sendDataToServer(this.parseParam(pageviewEvent)); // 最終發(fā)送編碼后的數(shù)據(jù)ss
this.updatePreVisitTime(time);
}
},
onChargeRequest: function(orderId, name, currencyAmount, currencyType, paymentType) {
// 觸發(fā)訂單產(chǎn)生事件
if (this.preCallApi()) {
if (!orderId || !currencyType || !paymentType) {
this.log("訂單id怜械、貨幣類型以及支付方式不能為空");
return ;
}
if (typeof(currencyAmount) == "number") {
// 金額必須是數(shù)字
var time = new Date().getTime();
var chargeRequestEvent = {};
chargeRequestEvent[this.columns.eventName] = this.keys.chargeRequestEvent;
chargeRequestEvent[this.columns.orderId] = orderId;
chargeRequestEvent[this.columns.orderName] = name;
chargeRequestEvent[this.columns.currencyAmount] = currencyAmount;
chargeRequestEvent[this.columns.currencyType] = currencyType;
chargeRequestEvent[this.columns.paymentType] = paymentType;
this.setCommonColumns(chargeRequestEvent); // 設(shè)置公用columns
this.sendDataToServer(this.parseParam(chargeRequestEvent)); // 最終發(fā)送編碼后的數(shù)據(jù)ss
this.updatePreVisitTime(time);
} else {
this.log("訂單金額必須是數(shù)字");
return ;
}
}
},
onEventDuration: function(category, action, map, duration) {
// 觸發(fā)event事件
if (this.preCallApi()) {
if (category && action) {
var time = new Date().getTime();
var event = {};
event[this.columns.eventName] = this.keys.eventDurationEvent;
event[this.columns.category] = category;
event[this.columns.action] = action;
if (map) {
for (var k in map){
if (k && map[k]) {
event[this.columns.kv + k] = map[k];
}
}
}
if (duration) {
event[this.columns.duration] = duration;
}
this.setCommonColumns(event); // 設(shè)置公用columns
this.sendDataToServer(this.parseParam(event)); // 最終發(fā)送編碼后的數(shù)據(jù)ss
this.updatePreVisitTime(time);
} else {
this.log("category和action不能為空");
}
}
},
/**
* 執(zhí)行對外方法前必須執(zhí)行的方法
*/
preCallApi: function() {
if (this.isSessionTimeout()) {
// 如果為true颅和,表示需要新建
this.startSession();
} else {
this.updatePreVisitTime(new Date().getTime());
}
return true;
},
sendDataToServer: function(data) {
// 發(fā)送數(shù)據(jù)data到服務(wù)器,其中data是一個字符串
var that = this;
var i2 = new Image(1,1);
i2.onerror = function(){
// 這里可以進(jìn)行重試操作
};
i2.src = this.clientConfig.serverUrl + "?" + data;
},
/**
* 往data中添加發(fā)送到日志收集服務(wù)器的公用部分
*/
setCommonColumns: function(data) {
data[this.columns.version] = this.clientConfig.ver;
data[this.columns.platform] = "website";
data[this.columns.sdk] = "js";
data[this.columns.uuid] = this.getUuid(); // 設(shè)置用戶id
data[this.columns.memberId] = this.getMemberId(); // 設(shè)置會員id
data[this.columns.sessionId] = this.getSid(); // 設(shè)置sid
data[this.columns.clientTime] = new Date().getTime(); // 設(shè)置客戶端時間
data[this.columns.language] = window.navigator.language; // 設(shè)置瀏覽器語言
data[this.columns.userAgent] = window.navigator.userAgent; // 設(shè)置瀏覽器類型
data[this.columns.resolution] = screen.width + "*" + screen.height; // 設(shè)置瀏覽器分辨率
},
/**
* 創(chuàng)建新的會員缕允,并判斷是否是第一次訪問頁面峡扩,如果是,進(jìn)行l(wèi)aunch事件的發(fā)送障本。
*/
createNewSession: function() {
var time = new Date().getTime(); // 獲取當(dāng)前操作時間
// 1. 進(jìn)行會話更新操作
var sid = this.generateId(); // 產(chǎn)生一個session id
this.setSid(sid);
this.updatePreVisitTime(time); // 更新最近訪問時間
// 2. 進(jìn)行uuid查看操作
if (!this.getUuid()) {
// uuid不存在教届,先創(chuàng)建uuid响鹃,然后保存到cookie,最后觸發(fā)launch事件
var uuid = this.generateId(); // 產(chǎn)品uuid
this.setUuid(uuid);
this.onLaunch();
}
},
/**
* 參數(shù)編碼返回字符串
*/
parseParam: function(data) {
var params = "";
for (var e in data) {
if (e && data[e]) {
params += encodeURIComponent(e) + "=" + encodeURIComponent(data[e]) + "&";
}
}
if (params) {
return params.substring(0, params.length - 1);
} else {
return params;
}
},
/**
* 產(chǎn)生uuid
*/
generateId: function() {
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
var tmpid = [];
var r;
tmpid[8] = tmpid[13] = tmpid[18] = tmpid[23] = '-';
tmpid[14] = '4';
for (i=0; i<36; i++) {
if (!tmpid[i]) {
r = 0| Math.random()*16;
tmpid[i] = chars[(i==19) ? (r & 0x3) | 0x8 : r];
}
}
return tmpid.join('');
},
/**
* 判斷這個會話是否過期案训,查看當(dāng)前時間和最近訪問時間間隔時間是否小于this.clientConfig.sessionTimeout<br/>
* 如果是小于买置,返回false;否則返回true。
*/
isSessionTimeout: function() {
var time = new Date().getTime();
var preTime = CookieUtil.get(this.keys.preVisitTime);
if (preTime) {
// 最近訪問時間存在,那么進(jìn)行區(qū)間判斷
return time - preTime > this.clientConfig.sessionTimeout * 1000;
}
return true;
},
/**
* 更新最近訪問時間
*/
updatePreVisitTime: function(time) {
CookieUtil.setExt(this.keys.preVisitTime, time);
},
/**
* 打印日志
*/
log: function(msg) {
console.log(msg);
},
};
// 對外暴露的方法名稱
window.__AE__ = {
startSession: function() {
tracker.startSession();
},
onPageView: function() {
tracker.onPageView();
},
onChargeRequest: function(orderId, name, currencyAmount, currencyType, paymentType) {
tracker.onChargeRequest(orderId, name, currencyAmount, currencyType, paymentType);
},
onEventDuration: function(category, action, map, duration) {
tracker.onEventDuration(category, action, map, duration);
},
setMemberId: function(mid) {
tracker.setMemberId(mid);
}
};
// 自動加載方法
var autoLoad = function() {
// 進(jìn)行參數(shù)設(shè)置
var _aelog_ = _aelog_ || window._aelog_ || [];
var memberId = null;
for (i=0;i<_aelog_.length;i++) {
_aelog_[i][0] === "memberId" && (memberId = _aelog_[i][1]);
}
// 根據(jù)是給定memberid强霎,設(shè)置memberid的值
memberId && __AE__.setMemberId(memberId);
// 啟動session
__AE__.startSession();
};
autoLoad();
})();
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>測試頁面1</title>
<script type="text/javascript" src="./js/analytics.js"></script>
</head>
<body>
測試頁面1<br/>
跳轉(zhuǎn)到:
<a href="demo.jsp">demo</a>
<a href="demo2.jsp">demo2</a>
<a href="demo3.jsp">demo3</a>
<a href="demo4.jsp">demo4</a>
</body>
</html>
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>測試頁面2</title>
<script type="text/javascript" src="./js/analytics.js"></script>
</head>
<body>
測試頁面2
<br/>
<label>orderid: 123456</label><br>
<label>orderName: 測試訂單123456</label><br/>
<label>currencyAmount: 524.01</label><br/>
<label>currencyType: RMB</label><br/>
<label>paymentType: alipay</label><br/>
<button onclick="__AE__.onChargeRequest('123456','測試訂單123456',524.01,'RMB','alipay')">觸發(fā)chargeRequest事件</button><br/>
跳轉(zhuǎn)到:
<a href="demo.jsp">demo</a>
<a href="demo2.jsp">demo2</a>
<a href="demo3.jsp">demo3</a>
<a href="demo4.jsp">demo4</a>
</body>
</html>
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>測試頁面3</title>
<script type="text/javascript" src="./js/analytics.js"></script>
</head>
<body>
測試頁面3<br/>
<label>category: event的category名稱</label><br/>
<label>action: event的action名稱</label><br/>
<label>map: {"key1":"value1", "key2":"value2"}</label><br/>
<label>duration: 1245</label><br/>
<button onclick="__AE__.onEventDuration('event的category名稱','event的action名稱', {'key1':'value1','key2':'value2'}, 1245)">觸發(fā)帶map和duration的事件</button><br/>
<button onclick="__AE__.onEventDuration('event的category名稱','event的action名稱')">觸發(fā)不帶map和duration的事件</button><br/>
跳轉(zhuǎn)到:
<a href="demo.jsp">demo</a>
<a href="demo2.jsp">demo2</a>
<a href="demo3.jsp">demo3</a>
<a href="demo4.jsp">demo4</a>
</body>
</html>
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>測試頁面4</title>
<script type="text/javascript">
(function(){
var _aelog_ = _aelog_ || window._aelog_ || [];
// 設(shè)置_aelog_相關(guān)屬性
_aelog_.push(["memberId","ck"]);
window._aelog_ = _aelog_;
(function(){
var aejs = document.createElement('script');
aejs.type = 'text/javascript';
aejs.async = true;
aejs.src = './js/analytics.js';
var script = document.getElementsByTagName('script')[0];
script.parentNode.insertBefore(aejs, script);
})();
})();
</script>
</head>
<body>
測試頁面4<br/>
在本頁面設(shè)置memberid為gerryliu<br/>
跳轉(zhuǎn)到:
<a href="demo.jsp">demo</a>
<a href="demo2.jsp">demo2</a>
<a href="demo3.jsp">demo3</a>
<a href="demo4.jsp">demo4</a>
</body>
</html>