1 设计实现

1.1 确定收集信息

名称 途径 备注
访问时间 web server Nginx $msec
IP web server Nginx $remote_addr
域名 javascript document.domain
URL javascript document.URL
页面标题 javascript document.title
分辨率 javascript window.screen.height & width
颜色深度 javascript window.screen.colorDepth
Referrer javascript document.referrer
浏览客户端 web server Nginx $http_user_agent
客户端语言 javascript navigator.language
访客标识 cookie Nginx $http_cookie
网站标识 javascript 自定义对象
状态码 web server Nginx $status
发送内容量 web server Nginx $body_bytes_sent

1.2 确定埋点代码

埋点,是网站分析的一种常用的数据采集方法。核心就是在需要进行数据采集的关键点植入统计代码,进行数据的采集。比如以谷歌分析原型来说,需要在页面中插入一段它提供的javascript片段,这个片段往往被称为埋点代码。

<script type="text/javascript">  var maq = _maq || [];  _maq.push(['setAccount', 'UA-XXXXX-X']);  (function() {   var ma = document.createElement('script'); ma.type = 'text/javascript'; ma.async = true;   ma.src = 'xxxxx/ma.js';   var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ma, s);  })(); </script>

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

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

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

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

扩展知识:js自调用匿名函数

格式:

(function(){})();

第一对括号向脚本返回未命名的函数;后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数。

自调用匿名函数的好处是,避免重名,自调用匿名函数只会在运行时执行一次,一般用于初始化。

1.3 前端数据收集脚本

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

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

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

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

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

这里唯一的问题是步骤4,javascript请求后端脚本常用的方法是ajax,但是ajax是不能跨域请求的。一种通用的方法是js脚本创建一个Image对象,将Image对象的src属性指向后端脚本并携带参数,此时即实现了跨域请求后端。这也是后端脚本为什么通常伪装成gif文件的原因。

示例代码:

整个脚本放在匿名函数里,确保不会污染全局环境。

(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://xxx.xxxxx.xxxxx/log.gif?' + args;
})();

1.4 后端脚本

log.gif是一个伪装成gif图片的脚本。一般需要完成以下几件事情:

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

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

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

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

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

设置cookie目的

之所以要设置cookie是因为如果要跟踪唯一访客,通常做法是如果在请求时发现客户端没有指定的跟踪cookie,则根据规则生成一个全局唯一的cookie并种植给用户,否则Set-cookie中放置获取到的跟踪cookie以保持同一用户cookie不变。这种做法虽然不是完美的(例如用户清掉cookie或更换浏览器会被认为是两个用户),但是目前被广泛使用的手段。

我们使用nginx的access_log做日志收集,不过有个问题就是nginx配置本身的逻辑表达能力有限,所以选用OpenResty做这个事情。

OpenResty是一个基于Nginx扩展出的高性能应用开发平台,内部集成了诸多有用的模块,其中的核心是通过ngx_lua模块集成了Lua,从而在nginx配置文件中可以通过Lua来表述业务。

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

首先,需要在nginx的配置文件中定义日志格式:

log_format tick
"$msec||$remote_addr||$status||$body_bytes_sent||$u_domain||$u_url||$u_title||$u_referrer||$u_sh||$u_sw||$u_cd||$u_lang||$http_user_agent||$u_account";

注意这里以u_开头的是我们待会会自己定义的变量,其它的是nginx内置变量。然后是核心的两个location:

location / log.gif {#伪装成gif文件default_type image/gif;    #本身关闭access_log,通过subrequest记录logaccess_log off;access_by_lua "-- 用户跟踪cookie名为__utracelocal uid = ngx.var.cookie___utrace        if not uid then-- 如果没有则生成一个跟踪cookie,算法为md5(时间戳+IP+客户端信息)uid = ngx.md5(ngx.now() .. ngx.var.remote_addr .. ngx.var.http_user_agent)end ngx.header['Set-Cookie'] = {'__utrace=' .. uid .. '; path=/'}if ngx.var.arg_domain then-- 通过subrequest子请求到/i-log记录日志,将参数和用户跟踪cookie带过去ngx.location.capture('/i-log?' .. ngx.var.args .. '&utrace=' .. uid)end ";  #此请求资源本地不缓存add_header Expires "Fri, 01 Jan 1980 00:00:00 GMT";add_header Pragma "no-cache";add_header Cache-Control "no-cache, max-age=0, must-revalidate";#返回一个1×1的空gif图片empty_gif;
}   location /i-log {#内部location,不允许外部直接访问internal;#设置变量,注意需要unescape,来自ngx_set_misc模块set_unescape_uri $u_domain $arg_domain;set_unescape_uri $u_url $arg_url;set_unescape_uri $u_title $arg_title;set_unescape_uri $u_referrer $arg_referrer;set_unescape_uri $u_sh $arg_sh;set_unescape_uri $u_sw $arg_sw;set_unescape_uri $u_cd $arg_cd;set_unescape_uri $u_lang $arg_lang;set_unescape_uri $u_account $arg_account;#打开日志 log_subrequest on;#记录日志到ma.log 格式为tickaccess_log /path/to/logs/directory/ma.log tick;#输出空字符串echo '';
}

要完全掌握这段脚本的每一个细节还是比较吃力的,用到了诸多第三方ngxin模块(全都包含在OpenResty中了),重点都用注释标出来,可以不用完全理解每一行的意义,只要大约知道这个配置完成了我们提到的后端逻辑就可以了。

1.5 日志格式

日志格式主要考虑日志分隔符,一般会有以下几种选择:

固定数量的字符、制表符分隔符、空格分隔符、其他一个或多个字符、特定的开始和结束文本。

1.6 日志切分

日志收集系统访问日志时间一长文件变得很大,而且日志放在一个文件不便于管理。通常要按时间段将日志切分,例如每天或每小时切分一个日志。通过crontab定时调用一个shell脚本实现,如下:

nginx_path_prefix=/usr/local/nginx
time=`date +%Y%m%d%H`
mv ${nginx_path_prefix}/logs/user_defined.log ${nginx_path_prefix}/logs/user_defined-${time}.log
kill -USR1 `cat ${nginx_path_prefix}/logs/nginx.pid`

这个脚本将user_defined.log移动重命名为user_defined-${time}.log,然后向nginx发送USR1信号令其重新打开日志文件。

USR1通常被用来告知应用程序重载配置文件, 向服务器发送一个USR1信号将导致以下步骤的发生:停止接受新的连接,等待当前连接停止,重新载入配置文件,重新打开日志文件,重启服务器,从而实现相对平滑的不关机的更改。

cat ${nginx_path_prefix}/logs/nginx.pid 取 nginx 的进程号

然后再/etc/crontab里加入一行:

59 * * * * root /path/to/directory/rotatelog.sh

在每个小时的59分启动这个脚本进行日志轮转操作。

2 系统环境部署

服务器中安装依赖

yum -y install gcc perl pcre-devel openssl openssl-devel

上传LuaJIT-2.0.4.tar.gz并安装LuaJIT

tar -zxvf LuaJIT-2.0.4.tar.gz -C /usr/local/src/cd /usr/local/src/LuaJIT-2.0.4/make && make install PREFIX=/usr/local/luajit

设置LuaJIT环境变量

vi /etc/profileexport LUAJIT_LIB=/usr/local/luajit/libexport LUAJIT_INC=/usr/local/luajit/include/luajit-2.0source  /etc/profile

创建modules文件夹,保存nginx依赖的模块

mkdir -p /usr/local/nginx/modules

上传nginx依赖的模块

set-misc-nginx-module-0.29.tar.gz lua-nginx-module-0.10.0.tar.gzngx_devel_kit-0.2.19.tar.gzecho-nginx-module-0.58.tar.gz

将依赖的模块直接解压到modules目录

tar -zxvf lua-nginx-module-0.10.0.tar.gz -C /usr/local/nginx/modules/tar -zxvf set-misc-nginx-module-0.29.tar.gz -C /usr/local/nginx/modules/tar -zxvf ngx_devel_kit-0.2.19.tar.gz -C /usr/local/nginx/modules/tar -zxvf echo-nginx-module-0.58.tar.gz -C /usr/local/nginx/modules/

安装openresty

tar -zxvf openresty-1.9.7.3.tar.gz -C /usr/local/src/cd /usr/local/src/openresty-1.9.7.3/./configure --prefix=/usr/local/openresty --with-luajit && make && make install

安装nginx

tar -zxvf nginx-1.8.1.tar.gz -C /usr/local/src/

编译nginx并支持其他模块

cd /usr/local/src/nginx-1.8.1/./configure --prefix=/usr/local/nginx \--with-ld-opt="-Wl,-rpath,/usr/local/luajit/lib" \--add-module=/usr/local/nginx/modules/ngx_devel_kit-0.2.19 \--add-module=/usr/local/nginx/modules/lua-nginx-module-0.10.0 \--add-module=/usr/local/nginx/modules/set-misc-nginx-module-0.29 \--add-module=/usr/local/nginx/modules/echo-nginx-module-0.58 make -j2make install

备注:如果对linux相关操作不熟,请严格按照上述步骤搭建环境,切记心细,心细,再心细。

3 自定义采集数据实现

3.1 方案一:基本功能实现

a) 创建页面index.html,添加埋点代码,放入nginx默认目录nginx/html下。

b) 在默认目录nginx/html下添加一个数据采集脚本ma.js。

c) 修改nginx的配置文件,添加自定义相关业务逻辑。

d) 启动nginx

sbin/nginx -c conf/nginx.conf

e) 通过游览器访问nginx

f) 观察自定义日志采集文件是否有对应的内容输出

tail -f logs/user_defined.log

此时还可以观察nginx默认的输出日志文件

tail -f logs/access.log

停止nginx:

sbin/nginx –s stop

3.2 方案二:页面点击事件

详细步骤请参考附件资料。

前端页面JS埋点自定义采集实现相关推荐

  1. thinkphp 前端页面js接收后端传过来的数据

    后端接口: 前端页面:

  2. 前端页面数据埋点、分析和参考

    最近从松江图书馆中借了一本叫<指尖上行 移动前端开发进阶之路>的书,该书分为5大章,此处只记录了其中的第4章. 书中写到在项目上线后,通过数据监控发现: 1. 一些之前觉得很好的创意,由于 ...

  3. webrtc streamer前端页面js播放摄像头rtsp流

    webrtc streamer 大致的了解了一下,就是使用js来播放rtsp视频流的一个技术.目前实现的厂家有很多,但是要收费.我这里是找了一个开源免费的项目,使用起来有一定的局限性,需要根据自己的业 ...

  4. Struts项目中前端页面向后台页面传参中文出现乱码(Get请求)

    问题描述:Struts项目中前端页面向后台页面传递中文参数值,中文值传递到后台后出现乱码并且以???形式出现 解决方法: 1.前端页面js文件中使用encodeURI()方法将所传递的中文值加密起来( ...

  5. 前端页面业务中的埋点统计数据

    1.背景 在H5的页面中有很多业务需要统计按钮的点击数与页面的打开数量统计以及客户是谁(多数是获取用户手机号),而统计数量的逻辑一般来说是在接口里进行以保证后台系统的查询,所以前端主要的方法便是用户点 ...

  6. vuejs项目前端纯js在线下载网页内容保存为自定义格式的word文件、另存为word文件

    所有前端导入导出方法集合: 前端必备技能知识:JS导出Blob流文件为Excel表格.Vue.js使用Blob的方式实现excel表格的下载(流文件下载)_勤动手多动脑少说多做厚积薄发-CSDN博客_ ...

  7. vue 新建的页面如何访问_Vue.js—实现前后端分离架构中前端页面搭建(四)(完)...

    [Vue.js实现前后端分离架构中前端页面搭建] 二十.实现服务端登录业务 前提:已经有单机版Eureka,端口8761.启动开Eureka 1. 新建父项目 新建backend_parent. 为了 ...

  8. Web项目中前端页面引用外部Js和Css的路径问题

    公众号:南宫一梦 Web项目中前端页面引用外部Js和Css的路径问题 一般我们在做Web项目时,通常会将多个页面引入的公共js和css文件抽取出来,单独写成一个公共文件,以期方便各个页面单独引入,达到 ...

  9. 【HTML响应式项目】成人教育官网前端页面(HTML+CSS+JS实现三端适应)

    项目源码已上传至码云仓库:云南农业职业技术学院/HTML响应式成人教育官网前端页面(HTML+CSS+JS实现) 项目演示地址:成人教育网 AAP端下载地址:成人教育网APP端.apk-互联网文档类资 ...

最新文章

  1. mysql插入数据显示中文乱码
  2. 操作系统习题2—进程调度
  3. 一文读懂云计算和PAYG“现付现用”模型
  4. Jeecg平台扩展性不好的地方收集启动。
  5. 搜狐“狐友”正式版上线 扩张我的社交圈
  6. layui checkbox加th;全选 反选
  7. 电脑开机提示计算机无法启动不了,电脑无法启动并出现“System Halted”如何解决?...
  8. Robot Framework 自动化框架大纲
  9. 你们身边有没有程序媛?
  10. xbox手柄接收器驱动_Xbox精英手柄,对这款游戏手柄使用感受
  11. myeclipse使用(技术和快捷键)
  12. unity引擎发展史
  13. 如何用python把pdf转为word_如何使用python将双栏pdf转换成word?
  14. 东财《金融法X》综合作业
  15. 实例讲解新GRE填空的变化
  16. 无线通信关键技术---扰码
  17. [Java]JDK1.7中HashMap的并发死链
  18. 小高考三门计算机能报大专吗,美术生小高考的要求吗
  19. 设计模式---代理(Proxy)模式
  20. 让php返回204,python请求返回HTTP 204

热门文章

  1. KT6368A的HID蓝牙双模版本_蓝牙键盘鼠标扫码枪方案介绍
  2. 冒名顶替上大学罗彩霞_冒名顶替综合症对男人的打击与对女人的打击一样……以及成千上万的其他发现……...
  3. 优化 20% 资源成本,新东方的 Serverless 实践之路
  4. 数据库知识点总结——转载
  5. 一款工具集小程序,去水印、提取音频等工具应有尽有
  6. python_docx制作word文档
  7. 在控制台打印佛祖图片
  8. python字节和字符串互转
  9. 漫画:用户的嘴,骗人的鬼!
  10. 【计算机科学速成课】[40集全/文字版] - 20.文件系统