线上监控系统的设计

1. 确定监控指标(埋点)

1.1 页面性能

为什么要监控性能?

这是一个最基本的问题,为什么要关注和监控前端性能?对于公司来说,性能在一定程度上与利益直接相关。国外有很多这方面的调研数据:

性能 收益
Google 延迟 400ms 搜索量下降 0.59%
Bing 延迟 2s 收入下降 4.3%
Yahoo 延迟 400ms 流量下降 5-9%
Mozilla 页面打开减少 2.2s 下载量提升 15.4%
Netflix 开启 Gzip 性能提升 13.25% 带宽减少50%

为什么性能会影响公司的收益呢?根本原因还是在于性能影响了用户体验。加载的延迟、操作的卡顿等都会影响用户的使用体验。尤其是移动端,用户对页面响应延迟和连接中断的容忍度很低。想象一下你拿着手机打开一个网页想看到某个信息却加载半天的心情,你很可能选择直接离开换一个网页。谷歌也将页面加载速度作为 SEO 的一个权重,页面加载速度对用户体验和 SEO 的影响的调研有很多。

尽管性能很重要,开发迭代过程中难免会有所忽视,性能会伴随产品的迭代而有所衰减。特别在移动端,网络一直是一个很大的瓶颈,而页面却越来越大,功能越来越复杂。并没有简单的几条黄金规则就可以搞定性能优化工作,我们需要一套性能监控系统持续监控、评估、预警页面性能状况、发现瓶颈,指导优化工作的进行。

有哪些可用工具

Page Speed

Page Speed 是谷歌开发的分析和优化网页的工具,可以作为浏览器插件使用。工具基于一系列优化规则对网站进行检测,对于未通过的规则会给出详细的建议。与此类似的工具还有 Yslow 等,推荐使用gtmetrix网站同时查看多个分析工具的结果,如下图所示:

WebPagetest

WebPageTest 是一款非常优秀的网页前端性能测试工具,已开源。可以使用在线版,也可以自己搭建。国内也有利用 WebPagetest 搭建的性能测试平台,推荐使用阿里测 (以下示例使用阿里测进行测试)。

使用 WebPagetest,你可以详细掌握网站加载过程中的瀑布流、性能得分、元素分布、视图分析等数据。其中比较直观的视图分析功能可以直接看到页面加载各个阶段的截屏:

上图直观地展现了浏览类网站两个重要的时间点:白屏时间首屏时间,即用户多久能在页面中看到内容,以及多久首屏渲染完成(包含图片等元素加载完成)。这两个时间点直接决定了用户需要等待多久才能看到自己想看到的信息。谷歌优化建议中也提到减少非首屏使用的 css 及 js,尽快让首屏呈现。

PhantomJS

PhantomJS轻松地将监控带入了自动化的行列。Phantom JS 是一个服务器端的 JavaScript API 的 WebKit,基于它可以轻松实现 web 自动化测试。PhantomJS 需要一定编程工作,但也更灵活。官方文档中已经有一个完整的获取网页加载 har 文件的示例,具体说明可以查看此文档,国内也有不少关于此工具的介绍。另外新浪@貘吃馍香开发的类似工具berserkJS也挺不错,还贴心的提供了首屏统计的功能,具体文章可以查看此处。

为什么要监控真实访问性能

到此肯定有同学问,既然有这么多优秀的工具,为什么要监控线上用户真实访问性能呢?

我们发现,工具模拟测试会在一定程度上与真实情况偏离,有时无法反映性能的波动情况。另外除了白屏首屏之类的基础指标,产品线同样关注产品相关的指标,例如广告可见、搜索可用、签到可用等,这些功能直接与页面 JS 加载相关,通过工具较难模拟。

为了持续监控不同网络环境下用户访问情况与页面各功能可用状况,我们选择在页面中植入 JS 来监控线上真实用户访问性能,同时利用已有的分析工具作为辅助,形成一套完整多元的数据监控体系,为产品线的评估与优化提供可靠的数据。

关于不同监控方式的简单对比可以查看下表:

类型 优点 缺点 示例
非侵入式 指标齐全、客户端主动监测、竞品监控 无法知道性能影响用户数、采样少容易失真、无法监控复杂应用与细分功能 Pagespeed、PhantomJS、UAQ
侵入式 真实海量用户数据、能监控复杂应用与业务功能、用户点击与区域渲染 需插入脚本统计、网络指标不全、无法监控竞品 DP 、Google 统计

前端的数据有哪些

前端的数据其实有很多,从大众普遍关注的 PV、UV、广告点击量,到客户端的网络环境、登陆状态,再到浏览器、操作系统信息,最后到页面性能、JS 异常,这些数据都可以在前端收集到。数据很多、很杂,不进行很好的分类肯定会导致统计混乱,也不利于统计代码的组织,下面就对几种普遍的数据需求进行了分类:

1、访问

访问数据是基于用户每次在浏览器上打开目标页面来统计的,它是以 PV 为粒度的统计,一个 PV 只统计一次访问数据。访问数据可以算作是最基础、覆盖面最广的一种统计,可以统计到很多的指标项,下面列出了一些较为常见的指标项:

  • PV/UV:最基础的 PV(页面访问数量)、UV(独立访问用户数量)

    1、PV(page view)即页面浏览量或点击量,是衡量一个网站或网页用户访问量。具体的说,PV值就是所有访问者在24小时(0点到24点)内看了某个网站多少个页面或某个网页多少次。PV是指页面刷新的次数,每一次页面刷新,就算做一次PV流量。
      度量方法就是从浏览器发出一个对网络服务器的请求(Request),网络服务器接到这个请求后,会将该请求对应的一个网页(Page)发送给浏览器,从而产生了一个PV。那么在这里只要是这个请求发送给了浏览器,无论这个页面是否完全打开(下载完成),那么都是应当计为1个PV。

    实例代码:

    通过创建img src ,来请求服务的,后台接到对应的请求进行相应的处理即可记录数据了。

    //   统计pv 和uv
    api.picLog = function (clickFlag){
        window.setTimeout(function(){
            var playclick = new Image();
            playclick.src="http://*****/logtjsj/commsj/commjstj/www2016/" + clickFlag + ".jpg";
        },300);
    };
    

    2、UV(unique visitor)即独立访客数,指访问某个站点或点击某个网页的不同IP地址的人数。在同一天内,UV只记录第一次进入网站的具有独立IP的访问者,在同一天内再次访问该网站则不计数。

  • 页面来源:页面的 refer,可以定位页面的入口

  • 操作系统:了解用户的 OS 状况,帮助分析用户群体的特征,特别是移动端,iOS 和 Android 的分布就更有意义了
  • 浏览器:可以统计到各种浏览器的占比,对于是否继续兼容 IE6、新技术(HTML5、CSS3 等)的运用等调研提供参考价值
  • 分辨率:对页面设计提供参考,特别是响应式设计
  • 登录率:百度也开始看重登陆,登陆用户具有更高的分析价值,引导用户登陆是非常重要的
  • 地域分布:访问用户在地理位置上的分布,可以针对不同地域做运营、活动等
  • 网络类型:wifi/3G/2G,为产品是否需要适配不同网络环境做决策
  • 访问时段:掌握用户访问时间的分布,引导消峰填谷、节省带宽
  • 停留时长:判断页面内容是否具有吸引力,对于需要长时间阅读的页面比较有意义
  • 到达深度:和停留时长类似,例如百度百科,用户浏览时的页面到达深度直接反映词条的质量
2、性能

页面 DOM 结构越来越复杂,但是又要追求用户体验,这就对页面的性能提出了更高的要求。性能的监控数据主要是用来衡量页面的流畅程度,也有一些主要的指标:

  • 白屏时间:即用户多久能在页面中看到内容,用户从打开页面开始到页面开始有东西呈现为止,这过程中占用的时间就是白屏时间
  • 首屏时间:多久首屏渲染完成(包含图片等元素加载完成),用户浏览器首屏内所有内容都呈现出来所花费的时间
  • 用户可操作时间:用户可以进行正常的点击、输入等操作
  • 总下载时间:页面所有资源都加载完成并呈现出来所花的时间,即页面 onload 的时间
  • 自定义的时间点:对于开发人员来说,完全可以自定义一些时间点,例如:某个组件 init 完成的时间、某个重要模块加载的时间等等
  • 网络指标(DNS、TCP、首字节、html传输时间)
3、点击

在用户的所有操作中,点击应该是最为主要的一个行为,包含了:pc 端鼠标的 click,移动端手指的 touch。用户的每次点击都是一次诉求,从点击数据中可以挖掘的信息其实有很多,下面只列出了我们目前所关注的指标:

  • 页面总点击量
  • 人均点击量:对于导航类的网页,这项指标是非常重要的
  • 流出 url:同样,导航类的网页,直接了解网页导流的去向
  • 点击时间:用户的所有点击行为,在时间上的分布,反映了用户点击操作的习惯
  • 首次点击时间:同上,但是只统计用户的第一次点击,如果该时间偏大,是否就表明页面很卡导致用户长时间不能点击呢?
  • 点击热力图:根据用户点击的位置,我们可以画出整个页面的点击热力图,可以很直观的了解到页面的热点区域
4、异常

这里的异常是指 JS 的异常,用户的浏览器上报 JS 的 bug,这会极大地降低用户体验,对于浏览器型号、版本满天飞的今天,再 NB 的程序员也难免会有擦枪走火的时候,当然 QA 能够覆盖到大部分的 bug,但肯定也会有一些 bug 在线上出现。JS 的异常捕获只有两种方式:window.onerrortry/catch,关于我们是如何做的将在后续的文章中有详细的描述,这里只列出捕获到异常时,一般需要采集哪些信息(主要用来 debug 异常):

  • 异常的提示信息:这是识别一个异常的最重要依据,如:’e.src’ 为空或不是对象
  • JS 文件名
  • 异常所在行
  • 发生异常的浏览器
  • 堆栈信息:必要的时候需要函数调用的堆栈信息,但是注意堆栈信息可能会比较大,需要截取
5、其他

除了上面提到的 4 类基本的数据统计需求,我们当然还可以根据实际情况来定义一些其他的统计需求,如用户浏览器对 canvas 的支持程度,再比如比较特殊的 – 用户进行轮播图翻页的次数,这些数据统计需求都是前端能够满足的,每一项统计的结果都体现了前端数据的价值。

1.2 用户规模

  • App下载量(只针对App)
    • 每日下载App总数
  • 注册激活用户总数
    • 每日新增注册用户数
    • 注册转化率
  • 日均活跃用户数

1.3 市场运营

  • 活跃用户比例
  • 用户主要来源
  • 留存率
    • 使用留存(日、周、月)
    • 购买留存(日、周、月)
  • 浏览情况
    • 人均浏览页面量
    • 人均浏览时长
    • 访问次数
    • 访问频率
  • 互动情况
    • 每日评论用户数
    • 交互发反馈次数(收藏、分享、喜欢等功能)

1.4 商业效果

  • 日均流水
  • 订单转化率
  • 客单价

2. 确定采集数据的方式(数据统计)

如何采集

在前端,通过注入 JS 脚本,使用一些 JS API(如:!!window.localStorage 就可以检验浏览器是否支持 localStorage)或者监听一些事件(如:click、window.onerror、onload 等)就可以得到数据。捕获到这些数据之后,需要将数据发送回服务器端,一般都是采用访问一个固定的 url,把数据作为该 url 的 query string,如:http://www.baidu.com/u.gif?data1=hello&data2=hi。

在实践的过程中我们抽离了一套用于前端统计的框架alog,方便开发者书写自己的统计脚本,具体的使用方法和 API 见github。下面就使用 alog来简单说明如何进行前端数据的采集:

例如:你需要统计页面的 PV,顺便加上页面来源(refer)

// 加载 alog,alog 是支持异步的
void function(e,t,n,a,o,i,m){
e.alogObjectName=o,e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},e[o].l=e[o].l||+new Date,i=t.createElement(n),i.asyn=1,i.src=a,m=t.getElementsByTagName(n)[0],m.parentNode.insertBefore(i,m)
}(window,document,"script","http://uxrp.github.io/alog/dist/alog.min.js","alog");

// 定义一个统计模块 pv
alog('define', 'pv', function(){ 
   var pvTracker = alog.tracker('pv');
   pvTracker.set('ref', document.referrer); // 设定 ref 参数
   return pvTracker;
});

// 创建一个 pv 统计模块的实例
alog('pv.create', {
    postUrl: 'http://localhost/u.gif' // 指定上传数据的 url 地址
});

// 上传数据
alog('pv.send', "pageview"); // 指明是 pageview

在页面上部署上面的代码,浏览器将会发送下面的 http 请求:

http://localhost/u.gif?t=pageview&ref=yourRefer

再例如:JS 异常的采集,需要进行事件监听

// 加载 alog
void function(e,t,n,a,o,i,m){
e.alogObjectName=o,e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},e[o].l=e[o].l||+new Date,i=t.createElement(n),i.asyn=1,i.src=a,m=t.getElementsByTagName(n)[0],m.parentNode.insertBefore(i,m)
}(window,document,"script","http://uxrp.github.io/alog/dist/alog.min.js","alog");

// 定义一个统计模块 err
alog('define', 'err', function(){ 
   var errTracker = alog.tracker('err');
   window.onerror = function(message, file, line) { //监听 window.onerror
        errTracker.send('err', {msg:message, js:file, ln:line});
    };
   return errTracker;
});

// 创建一个 err 统计模块的实例
alog('err.create', {
    postUrl: 'http://localhost/u.gif'
});

这时,只要页面中 JS 发生异常,就会发送如下面的 HTTP 请求

http://localhost/u.gif?t=err&msg=errMessage&js=jsFileName&ln=errLine

2.1 确定统计起点

技术实现:

在用户输入 URL 或者点击链接的时候就开始统计,可以借用Navigation Timing API或者cookie时间戳。

2.2 统计白屏时间

技术实现:

通过获取头部资源加载完的时刻来近似统计白屏时间。

尽管并不精确,但却考虑了影响白屏的主要因素:首字节时间和头部资源加载时间。

头部资源加载时间

技术实现:

在浏览器 head 内底部加一句 JS, 统计头部资源加载结束点。

<!DOCTYPE HTML>
<html>
   <head>
       <meta charset="UTF-8"/>
   <script>
     var start_time = +new Date; //测试时间起点,实际统计起点为 DNS 查询
   </script>
   <!-- 3s 后这个 js 才会返回 -->
   <script src="script.PHP"></script>  
   <script>
     var end_time = +new Date; //时间终点
     var headtime = end_time - start_time; //头部资源加载时间    
     console.log(headtime);
   </script>
   </head>
   <body>    
   <p>在头部资源加载完之前页面将是白屏</p> 
   </body>
</html>

2.3 统计首屏时间

通过统计首屏内图片的加载时间便可以获取首屏渲染完成的时间。

技术实现

统计流程如下:

首屏位置调用 API 开始统计 -> 绑定首屏内所有图片的 load 事件 -> 页面加载完后判断图片是否在首屏内,找出加载最慢的一张 -> 首屏时间

这是同步加载情况下的简单统计逻辑,另外需要注意的几点:

  • 页面存在 iframe 的情况下也需要判断加载时间
  • gif 图片在 IE 上可能重复触发 load 事件需排除
  • 异步渲染的情况下应在异步获取数据插入之后再计算首屏
  • css 重要背景图片可以通过 JS 请求图片 url 来统计(浏览器不会重复加载)
  • 没有图片则以统计 JS 执行时间为首屏,即认为文字出现时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    function getOffsetTop(ele) {
    var offsetTop = ele.offsetTop;
    if (ele.offsetParent !== null) {
    offsetTop += getOffsetTop(ele.offsetParent);
    }
    return offsetTop;
    }

    var firstScreenHeight = win.screen.height;
    var firstScreenImgs = [];
    var isFindLastImg = false;
    var allImgLoaded = false;
    var t = setInterval(function() {
    var i, img;
    if (isFindLastImg) {
    if (firstScreenImgs.length) {
    for (i = 0; i < firstScreenImgs.length; i++) {
    img = firstScreenImgs[i];
    if (!img.complete) {
    allImgLoaded = false;
    break;
    } else {
    allImgLoaded = true;
    }
    }
    } else {
    allImgLoaded = true;
    }
    if (allImgLoaded) {
    collect.add({
    firstScreenLoaded: startTime - Date.now()
    });
    clearInterval(t);
    }
    } else {
    var imgs = body.querySelector('img');
    for (i = 0; i < imgs.length; i++) {
    img = imgs[i];
    var imgOffsetTop = getOffsetTop(img);
    if (imgOffsetTop > firstScreenHeight) {
    isFindLastImg = true;
    break;
    } else if (imgOffsetTop <= firstScreenHeight && !img.hasPushed) {
    img.hasPushed = 1;
    firstScreenImgs.push(img);
    }
    }
    }
    }, 0);



    doc.addEventListener('DOMContentLoaded', function() {
    var imgs = body.querySelector('img');
    if (!imgs.length) {
    isFindLastImg = true;
    }
    });

    win.addEventListener('load', function() {
    allImgLoaded = true; isFindLastImg = true; if (t) { clearInterval(t); } collect.log(collect.global); });

2.4 统计用户可操作时间

技术实现:

用户可操作默认可以统计domready时间。

2.5 统计总下载时间

技术实现:

总下载时间默认可以统计onload时间,这样可以统计同步加载的资源全部加载完的耗时。如果页面中存在很多异步渲染,可以将异步渲染全部完成的时间作为总下载时间。

2.6 网络指标

2.6.1 网络类型判断

技术实现:

可以通过测速的方式来判断不同 IP 段对应的网络。测速例如比较经典的有 facebook 的方案。

2.6.2 网络耗时统计

技术实现:

网络耗时数据可以借助一些工具(如Navigation Timing 接口)获取,可以获取页面所有静态资源的加载耗时。通过此接口可以轻松获取 DNS、TCP、首字节、html 传输等耗时。

3. 数据分析

可以从多个维度去分析数据。大数据处理需要借助 Hadoop、Hive 等方式,而对于普通站点则任意一种后端语言处理即可。

3.1 均值与分布

均值与分布是数据处理中最常见的两种方式。因为它能直观的表示指标的趋势与分布状况,方便进行评估、瓶颈发现与告警。处理过程中应去除异常值,例如明显超过阈值的脏数据等。

3.2 多维分析

为了方便挖掘性能可能的瓶颈,需要从多维的角度对数据进行分析。

例如移动端最重要的维度就是网络,数据处理上除了总体数据,还需要根据网络类型对数据进行分析。

常见的维度还有系统、浏览器、地域运营商等。我们还可以根据自身产品的特点来确定一些维度,例如页面长度分布、简版炫版等。

需要注意的是维度并不是越多越好,需要根据产品的特点及终端来确定。维度是为了方便查找性能瓶颈。

4. 数据可视化

技术实现:

采用Highcharts,Echarts制作可视化图表。

Echart可以统计的有:

浏览器的占比情况、用户登陆情况、用户地理位置分布、浏览器的多天占比波动情况、搜索词排行等等。

5. 智能告警

5.1 安全监测

  • 文字监测
  • 链接监测:监测网站的链接,对暗链进行扫描
  • 图片监测
  • 脚本监测
  • 挂马监测

5.2 性能监测

监控服务器性能(进程是否存活、内存、cpu、网卡)变化,超过阈值即报警

5.3 诊断功能

监测网站内部的错误页面,内部链接错误等监测。

5.4 报表功能

可以导出报表。

5.5 报警方式

邮件、短信、微信等方式。

网站统计中的数据收集原理及实现_埋点统计

网站数据统计分析工具是网站站长和运营人员经常使用的一种工具,比较常用的有谷歌分析、百度统计和腾讯分析等等。所有这些统计分析工具的第一步都是网站访问数据的收集。目前主流的数据收集方式基本都是基于javascript的。

数据收集原理分析

简单来说,网站统计分析工具需要收集到用户浏览目标网站的行为(如打开某网页、点击某按钮、将商品加入购物车等)及行为附加数据(如某下单行为产生的订单金额等)。早期的网站统计往往只收集一种用户行为:页面的打开。而后用户在页面中的行为均无法收集。这种收集策略能满足基本的流量分析、来源分析、内容分析及访客属性等常用分析视角,但是,随着ajax技术的广泛使用及电子商务网站对于电子商务目标的统计分析的需求越来越强烈,这种传统的收集策略已经显得力不能及。

后来,Google在其产品谷歌分析中创新性的引入了可定制的数据收集脚本,用户通过谷歌分析定义好的可扩展接口,只需编写少量的javascript代码就可以实现自定义事件和自定义指标的跟踪和分析。目前百度统计、搜狗分析等产品均照搬了谷歌分析的模式。

其实说起来两种数据收集模式的基本原理和流程是一致的,只是后一种通过javascript收集到了更多的信息。下面看一下现在各种网站统计工具的数据收集基本原理。

流程概览

首先通过一幅图总体看一下数据收集的基本流程。

图1. 网站统计数据收集基本流程

首先,用户的行为会触发浏览器对被统计页面的一个http请求,这里姑且先认为行为就是打开网页。当网页被打开,页面中的埋点javascript片段会被执行,用过相关工具的朋友应该知道,一般网站统计工具都会要求用户在网页中加入一小段javascript代码,这个代码片段一般会动态创建一个script标签,并将src指向一个单独的js文件,此时这个单独的js文件(图1中绿色节点)会被浏览器请求到并执行,这个js往往就是真正的数据收集脚本。数据收集完成后,js会请求一个后端的数据收集脚本(图1中的backend),这个脚本一般是一个伪装成图片的动态脚本程序,可能由php、python或其它服务端语言编写,js会将收集到的数据通过http参数的方式传递给后端脚本,后端脚本解析参数并按固定格式记录到访问日志,同时可能会在http响应中给客户端种植一些用于追踪的cookie。

上面是一个数据收集的大概流程,下面以谷歌分析为例,对每一个阶段进行一个相对详细的分析。

埋点脚本执行阶段

若要使用谷歌分析(以下简称GA),需要在页面中插入一段它提供的javascript片段,这个片段往往被称为埋点代码。下面是我的博客中所放置的谷歌分析埋点代码截图:

图2. 谷歌分析埋点代码

其中_gaq是GA的的全局数组,用于放置各种配置,其中每一条配置的格式为:

_gaq.push(['Action', 'param1', 'param2', ...]);

Action指定配置动作,后面是相关的参数列表。GA给的默认埋点代码会给出两条预置配置,_setAccount用于设置网站标识ID,这个标识ID是在注册GA时分配的。_trackPageview告诉GA跟踪一次页面访问。更多配置请参考:https://developers.google.com/analytics/devguides/collection/gajs/。实际上,这个_gaq是被当做一个FIFO队列来用的,配置代码不必出现在埋点代码之前,具体请参考上述链接的说明。

就本文来说,_gaq的机制不是重点,重点是后面匿名函数的代码,这才是埋点代码真正要做的。这段代码的主要目的就是引入一个外部的js文件(ga.js),方式是通过document.createElement方法创建一个script并根据协议(http或https)将src指向对应的ga.js,最后将这个element插入页面的dom树上。

注意ga.async = true的意思是异步调用外部js文件,即不阻塞浏览器的解析,待外部js下载完成后异步执行。这个属性是HTML5新引入的。

数据收集脚本执行阶段

数据收集脚本(ga.js)被请求后会被执行,这个脚本一般要做如下几件事:

1、通过浏览器内置javascript对象收集信息,如页面title(通过document.title)、referrer(上一跳url,通过document.referrer)、用户显示器分辨率(通过windows.screen)、cookie信息(通过document.cookie)等等一些信息。

2、解析_gaq收集配置信息。这里面可能会包括用户自定义的事件跟踪、业务数据(如电子商务网站的商品编号等)等。

3、将上面两步收集的数据按预定义格式解析并拼接。

4、请求一个后端脚本,将信息放在http request参数中携带给后端脚本。

这里唯一的问题是步骤4,javascript请求后端脚本常用的方法是ajax,但是ajax是不能跨域请求的。这里ga.js在被统计网站的域内执行,而后端脚本在另外的域(GA的后端统计脚本是http://www.google-analytics.com/__utm.gif),ajax行不通。一种通用的方法是js脚本创建一个Image对象,将Image对象的src属性指向后端脚本并携带参数,此时即实现了跨域请求后端。这也是后端脚本为什么通常伪装成gif文件的原因。通过http抓包可以看到ga.js对__utm.gif的请求:

图3. 后端脚本请求的http包

可以看到ga.js在请求__utm.gif时带了很多信息,例如utmsr=1280×1024是屏幕分辨率,utmac=UA-35712773-1是_gaq中解析出的我的GA标识ID等等。

值得注意的是,__utm.gif未必只会在埋点代码执行时被请求,如果用_trackEvent配置了事件跟踪,则在事件发生时也会请求这个脚本。

由于ga.js经过了压缩和混淆,可读性很差,我们就不分析了,具体后面实现阶段我会实现一个功能类似的脚本。

后端脚本执行阶段

GA的__utm.gif是一个伪装成gif的脚本。这种后端脚本一般要完成以下几件事情:

1、解析http请求参数的到信息。

2、从服务器(WebServer)中获取一些客户端无法获取的信息,如访客ip等。

3、将信息按格式写入log。

5、生成一副1×1的空gif图片作为响应内容并将响应头的Content-type设为image/gif。

5、在响应头中通过Set-cookie设置一些需要的cookie信息。

之所以要设置cookie是因为如果要跟踪唯一访客,通常做法是如果在请求时发现客户端没有指定的跟踪cookie,则根据规则生成一个全局唯一的cookie并种植给用户,否则Set-cookie中放置获取到的跟踪cookie以保持同一用户cookie不变(见图4)。

图4. 通过cookie跟踪唯一用户的原理

这种做法虽然不是完美的(例如用户清掉cookie或更换浏览器会被认为是两个用户),但是是目前被广泛使用的手段。注意,如果没有跨站跟踪同一用户的需求,可以通过js将cookie种植在被统计站点的域下(GA是这么做的),如果要全网统一定位,则通过后端脚本种植在服务端域下(我们待会的实现会这么做)。

系统的设计实现

根据上述原理,我自己搭建了一个访问日志收集系统。总体来说,搭建这个系统要做如下的事:

图5. 访问数据收集系统工作分解

下面详述每一步的实现。我将这个系统叫做MyAnalytics。

确定收集的信息

为了简单起见,我不打算实现GA的完整数据收集模型,而是收集以下信息。

埋点代码

埋点代码我将借鉴GA的模式,但是目前不会将配置对象作为一个FIFO队列用。一个埋点代码的模板如下:

<script type="text/javascript"> 
    var _maq = _maq || []; 
    _maq.push(['_setAccount', '网站标识']);   
    (function() {     
        var ma = document.createElement('script'); 
        ma.type = 'text/javascript'; 
        ma.async = true;     
        ma.src = ('https:' == document.location.protocol ? 'https://analytics' : 'http://analytics') + '.codinglabs.org/ma.js';                 var s = document.getElementsByTagName('script')[0];             s.parentNode.insertBefore(ma, s); 
    })(); 
</script>

这里我启用了二级域名analytics.codinglabs.org,统计脚本的名称为ma.js。当然这里有一点小问题,因为我并没有https的服务器,所以如果一个https站点部署了代码会有问题,不过这里我们先忽略吧。

前端统计脚本

我写了一个不是很完善但能完成基本工作的统计脚本ma.js:

(function () {     var params = {};     //Document对象数据     if(document) {         params.domain = document.domain || '';          params.url = document.URL || '';          params.title = document.title || '';          params.referrer = document.referrer || '';      }        //Window对象数据     if(window && window.screen) {         params.sh = window.screen.height || 0;         params.sw = window.screen.width || 0;         params.cd = window.screen.colorDepth || 0;     }        //navigator对象数据     if(navigator) {         params.lang = navigator.language || '';      }        //解析_maq配置     if(_maq) {         for(var i in _maq) {             switch(_maq[i][0]) {                 case '_setAccount':                     params.account = _maq[i][1];                     break;                 default:                     break;             }            }        }        //拼接参数串     var args = '';      for(var i in params) {         if(args != '') {             args += '&';         }            args += i + '=' + encodeURIComponent(params[i]);     }          //通过Image对象请求后端脚本     var img = new Image(1, 1);      img.src = 'http://analytics.codinglabs.org/1.gif?' + args; })();

整个脚

参考自:

http://blog.csdn.net/ddcowboy/article/details/55511304

http://fex.baidu.com/blog/2014/05/front_end-data/

http://blog.csdn.net/u010427666/article/details/52173219

原理:

http://www.ithao123.cn/content-1820695.html