【电商日志项目之二】数据收集一

环境
  js
  javaphp

1、埋点分析,是网站分析的一种经常使用的数据采集方法。数据埋点是一种良好的私有化部署数据采集方式。java

2、页面数据收集事件的分析和设计
一、针对不一样分析模块,须要不一样的数据,来设计页面事件:
(1)用户基本信息就是用户的浏览行为信息分析,只须要pageview事件就能够了;
(2)浏览器信息分析以及地域信息分析其实就是在用户基本信息分析的基础上添加浏览器和地域这个维度信息,其中浏览器信息咱们能够经过浏览器的window.navigator.userAgent来进行分析,地域信息能够经过nginx服务器来收集用户的ip地址来进行分析,也就是说pageview事件也能够知足这两个模块的分析。
(3)外链数据分析以及用户浏览深度分析咱们能够在pageview事件中添加访问页面的当前url和前一个页面的url来进行处理分析,也就是说pageview事件也能够知足这两个模块的分析。
(4)订单信息分析要求客户端发送一个订单产生的事件,那么对应这个模块的分析,咱们须要一个新的事件chargeRequest。
(5)对于事件分析咱们也须要一个客户端发送一个新的事件数据,咱们能够定义为event。
(6)除此以外,咱们还须要设置一个launch事件来记录新用户的访问。
客户端的各类不一样事件发送的数据url格式以下,其中url中后面的参数就是咱们收集到的数据:http://wjy.com/log.gif?requestdata
通过上面的分析咱们须要设计四类页面事件以下:node

最终分析模块nginx

客户端js sdk事件web

用户基本信息分析浏览器

pageview事件服务器

浏览器信息分析cookie

地域信息分析session

外链数据分析app

用户浏览深度分析

订单信息分析(订单成功、订单失败)

chargeRequest事件

事件分析

event事件

 

launch事件

 

二、页面处理流程以下:

 

数据参数:

参数名称

类型

描述

en

string

事件名称, eg: e_pv

ver

string

版本号, eg: 0.0.1

pl

string

平台, eg: website

sdk

string

Sdk类型, eg: js

b_rst

string

浏览器分辨率,eg: 1800*678

b_iev

string

浏览器信息useragent

u_ud

string

用户/访客惟一标识符

l

string

客户端语言

u_mid

string

会员id,和业务系统一致

u_sd

string

会话id

c_time

string

客户端时间

p_url

string

当前页面的url

p_ref

string

上一个页面的url

tt

string

当前页面的标题

ca

string

Event事件的Category名称

ac

string

Event事件的action名称

kv_*

string

Event事件的自定义属性

du

string

Event事件的持续时间

oid

string

订单id

on

string

订单名称

cua

string

支付金额

cut

string

支付货币类型

pt

string

支付方式

三、js sdk:

(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://node2/log.gif", sessionTimeout : 360, // 360s -> 6min
            maxWaitTime : 3600, // 3600s -> 60min -> 1h
            ver : "1" }, cookieExpiresTime : 315360000000, // cookie过时时间,10年
 columns : { // 发送到服务器的列名称
            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 : "bftrack_sid",//会话ID
            uuid : "bftrack_uuid",//用户ID
            mid : "bftrack_mid",//会员ID
            preVisitTime : "bftrack_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); }, /** * 设置mid */ setMemberId : function(mid) { if (mid) { CookieUtil.setExt(this.keys.mid, mid); } }, startSession : function() { // 加载js就触发的方法
            if (this.getSid()) { // 会话id存在,表示uuid也存在
                if (this.isSessionTimeout()) { // 会话过时,产生新的会话
                    this.createNewSession(); } else { // 会话没有过时,更新最近访问时间
                    this.updatePreVisitTime(new Date().getTime()); } } else { // 会话id不存在,表示uuid也不存在
                this.createNewSession(); } this.onPageView(); }, onLaunch : function() { // 触发launch事件
            var launch = {}; launch[this.columns.eventName] = this.keys.launch; // 设置事件名称
            this.setCommonColumns(launch); // 设置公用columns
            this.sendDataToServer(this.parseParam(launch)); // 最终发送编码后的数据
 }, onPageView : function() { // 触发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; // 设置当前url
                pageviewEvent[this.columns.referrerUrl] = document.referrer; // 设置前一个页面的url
                pageviewEvent[this.columns.title] = document.title; // 设置title
                this.setCommonColumns(pageviewEvent); // 设置公用columns
                this.sendDataToServer(this.parseParam(pageviewEvent)); // 最终发送编码后的数据
                this.updatePreVisitTime(time); } }, onChargeRequest : function(orderId, name, currencyAmount, currencyType, paymentType) { // 触发订单产生事件
            if (this.preCallApi()) { if (!orderId || !currencyType || !paymentType) { this.log("订单id、货币类型以及支付方式不能为空"); return; } if (typeof (currencyAmount) == "number") { // 金额必须是数字
                    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); // 设置公用columns
                    this.sendDataToServer(this.parseParam(chargeRequestEvent)); // 最终发送编码后的数据
                    this.updatePreVisitTime(time); } else { this.log("订单金额必须是数字"); return; } } }, onEventDuration : function(category, action, map, duration) { // 触发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); // 设置公用columns
                    this.sendDataToServer(this.parseParam(event)); // 最终发送编码后的数据
                    this.updatePreVisitTime(time); } else { this.log("category和action不能为空"); } } }, /** * 执行对外方法前必须执行的方法 */ preCallApi : function() { if (this.isSessionTimeout()) { // 若是为true,表示须要新建
                this.startSession(); } else { this.updatePreVisitTime(new Date().getTime()); } return true; }, sendDataToServer : function(data) { alert(data); // 发送数据data到服务器,其中data是一个字符串
            var i2 = new Image(1, 1);// <img src="url"></img>
            i2.onerror = function() { // 这里能够进行重试操做
 }; i2.src = this.clientConfig.serverUrl + "?" + data; }, /** * 往data中添加发送到日志收集服务器的公用部分 */ 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(); // 设置用户id
            data[this.columns.memberId] = this.getMemberId(); // 设置会员id
            data[this.columns.sessionId] = this.getSid(); // 设置sid
            data[this.columns.clientTime] = new Date().getTime(); // 设置客户端时间
            data[this.columns.language] = window.navigator.language; // 设置浏览器语言
            data[this.columns.userAgent] = window.navigator.userAgent; // 设置浏览器类型
            data[this.columns.resolution] = screen.width + "*" + screen.height; // 设置浏览器分辨率
 }, /** * 建立新的会员,并判断是不是第一次访问页面,若是是,进行launch事件的发送。 */ createNewSession : function() { var time = new Date().getTime(); // 获取当前操做时间
            // 1. 进行会话更新操做
            var sid = this.generateId(); // 产生一个session id
            this.setSid(sid); this.updatePreVisitTime(time); // 更新最近访问时间
            // 2. 进行uuid查看操做
            if (!this.getUuid()) { // uuid不存在,先建立uuid,而后保存到cookie,最后触发launch事件
                var uuid = this.generateId(); // 产品uuid
                this.setUuid(uuid); this.onLaunch(); } }, /** * 参数编码返回字符串 */ 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; } }, /** * 产生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(''); }, /** * 判断这个会话是否过时,查看当前时间和最近访问时间间隔时间是否小于this.clientConfig.sessionTimeout<br/> * 若是是小于,返回false;不然返回true。 */ isSessionTimeout : function() { var time = new Date().getTime(); var preTime = CookieUtil.get(this.keys.preVisitTime); if (preTime) { // 最近访问时间存在,那么进行区间判断
                return time - preTime > this.clientConfig.sessionTimeout * 1000; } return true; }, /** * 更新最近访问时间 */ updatePreVisitTime : function(time) { CookieUtil.setExt(this.keys.preVisitTime, time); }, /** * 打印日志 */ log : function(msg) { console.log(msg); }, }; // 对外暴露的方法名称 这样在页面中或者其余js域能够直接调用
    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() { // 进行参数设置
        var _aelog_ = _aelog_ || window._aelog_ || []; var memberId = null; for (i = 0; i < _aelog_.length; i++) { _aelog_[i][0] === "memberId" && (memberId = _aelog_[i][1]); } // 根据是给定memberid,设置memberid的值
        memberId && __AE__.setMemberId(memberId); // 启动session
 __AE__.startSession(); }; autoLoad(); })();

 

3、程序后台事件分析

一、程序后台事件设计
好比订单成功和订单退款事件只能在后台程序中触发,页面是没法触发的:

 http://wjy.com/log.gif?requestdata

最终分析模块

PC端js sdk事件

订单信息分析

chargeSuccess事件

chargeRefund事件

数据参数:

参数名称

类型

描述

en

string

事件名称, eg: e_cs

ver

string

版本号, eg: 0.0.1

pl

string

平台, eg: website,javaweb,php

sdk

string

Sdk类型, eg: java

u_mid

string

会员id,和业务系统一致

c_time

string

客户端时间

oid

string

订单id

 

 

二、后台程序处理流程:

 三、程序示例

事件定义代码:

package com.sxt.client; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * 分析引擎sdk java服务器端数据收集 * * @author root * @version 1.0 * */
public class AnalyticsEngineSDK { // 日志打印对象
    private static final Logger log = Logger.getGlobal(); // 请求url的主体部分
    public static final String accessUrl = "http://node2/log.gif"; private static final String platformName = "java_server"; private static final String sdkName = "jdk"; private static final String version = "1"; /** * 触发订单支付成功事件,发送事件数据到服务器 * * @param orderId * 订单支付id * @param memberId * 订单支付会员id * @return 若是发送数据成功(加入到发送队列中),那么返回true;不然返回false(参数异常&添加到发送队列失败). */
    public static boolean onChargeSuccess(String orderId, String memberId) { try { if (isEmpty(orderId) || isEmpty(memberId)) { // 订单id或者memberid为空
                log.log(Level.WARNING, "订单id和会员id不能为空"); return false; } // 代码执行到这儿,表示订单id和会员id都不为空。
            Map<String, String> data = new HashMap<String, String>(); data.put("u_mid", memberId); data.put("oid", orderId); data.put("c_time", String.valueOf(System.currentTimeMillis())); data.put("ver", version); data.put("en", "e_cs"); data.put("pl", platformName); data.put("sdk", sdkName); // 建立url
            String url = buildUrl(data); // 发送url&将url加入到队列
 SendDataMonitor.addSendUrl(url); return true; } catch (Throwable e) { log.log(Level.WARNING, "发送数据异常", e); } return false; } /** * 触发订单退款事件,发送退款数据到服务器 * * @param orderId * 退款订单id * @param memberId * 退款会员id * @return 若是发送数据成功,返回true。不然返回false。 */
    public static boolean onChargeRefund(String orderId, String memberId) { try { if (isEmpty(orderId) || isEmpty(memberId)) { // 订单id或者memberid为空
                log.log(Level.WARNING, "订单id和会员id不能为空"); return false; } // 代码执行到这儿,表示订单id和会员id都不为空。
            Map<String, String> data = new HashMap<String, String>(); data.put("u_mid", memberId); data.put("oid", orderId); data.put("c_time", String.valueOf(System.currentTimeMillis())); data.put("ver", version); data.put("en", "e_cr"); data.put("pl", platformName); data.put("sdk", sdkName); // 构建url
            String url = buildUrl(data); // 发送url&将url添加到队列中
 SendDataMonitor.addSendUrl(url); return true; } catch (Throwable e) { log.log(Level.WARNING, "发送数据异常", e); } return false; } /** * 根据传入的参数构建url * * @param data * @return * @throws UnsupportedEncodingException */
    private static String buildUrl(Map<String, String> data) throws UnsupportedEncodingException { StringBuilder sb = new StringBuilder(); sb.append(accessUrl).append("?"); for (Map.Entry<String, String> entry : data.entrySet()) { if (isNotEmpty(entry.getKey()) && isNotEmpty(entry.getValue())) { sb.append(entry.getKey().trim()) .append("=") .append(URLEncoder.encode(entry.getValue().trim(), "utf-8")) .append("&"); } } return sb.substring(0, sb.length() - 1);// 去掉最后&
 } /** * 判断字符串是否为空,若是为空,返回true。不然返回false。 * * @param value * @return
     */
    private static boolean isEmpty(String value) { return value == null || value.trim().isEmpty(); } /** * 判断字符串是否非空,若是不是空,返回true。若是是空,返回false。 * * @param value * @return
     */
    private static boolean isNotEmpty(String value) { return !isEmpty(value); } }

事件发送代码:

package com.sxt.client; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.logging.Level; import java.util.logging.Logger; /** * 发送url数据的监控者,用于启动一个单独的线程来发送数据 * * @author root * */
public class SendDataMonitor { // 日志记录对象
    private static final Logger log = Logger.getGlobal(); // 队列,用户存储发送url
    private BlockingQueue<String> queue = new LinkedBlockingQueue<String>(); // 用于单列的一个类对象
    private static SendDataMonitor monitor = null; private SendDataMonitor() { // 私有构造方法,进行单列模式的建立
 } /** * 获取单列的monitor对象实例 * * @return
     */
    public static SendDataMonitor getSendDataMonitor() { if (monitor == null) { synchronized (SendDataMonitor.class) { if (monitor == null) { monitor = new SendDataMonitor(); Thread thread = new Thread(new Runnable() { @Override public void run() { // 线程中调用具体的处理方法
 SendDataMonitor.monitor.run(); } }); // 测试的时候,不设置为守护模式 // thread.setDaemon(true);
 thread.start(); } } } return monitor; } /** * 添加一个url到队列中去 * * @param url * @throws InterruptedException */
    public static void addSendUrl(String url) throws InterruptedException { getSendDataMonitor().queue.put(url); } /** * 具体执行发送url的方法 * */
    private void run() { while (true) { try { String url = this.queue.take(); // 正式的发送url
 HttpRequestUtil.sendData(url); } catch (Throwable e) { log.log(Level.WARNING, "发送url异常", e); } } } /** * 内部类,用户发送数据的http工具类 * * @author root * */
    public static class HttpRequestUtil { /** * 具体发送url的方法 * * @param url * @throws IOException */
        public static void sendData(String url) throws IOException { HttpURLConnection con = null; BufferedReader in = null; try { URL obj = new URL(url); // 建立url对象
                con = (HttpURLConnection) obj.openConnection(); // 打开url链接 // 设置链接参数
                con.setConnectTimeout(5000); // 链接过时时间
                con.setReadTimeout(5000); // 读取数据过时时间
                con.setRequestMethod("GET"); // 设置请求类型为get
 System.out.println("发送url:" + url); // 发送链接请求
                in = new BufferedReader(new InputStreamReader(con.getInputStream())); // TODO: 这里考虑是否能够
            } finally { try { if (in != null) { in.close(); } } catch (Throwable e) { // nothing
 } try { con.disconnect(); } catch (Throwable e) { // nothing
 } } } } }
相关文章
相关标签/搜索