Z05 - 009、设计实现

本文介绍了如何设计实现网站流量日志的自定义采集,包括确定收集信息、埋点代码、前端和后端数据脚本、日志格式和切分。通过在关键点植入统计代码进行数据采集,前端利用ma.js收集信息,后端通过lua脚本处理日志,并使用OpenResty进行日志管理。
摘要由CSDN通过智能技术生成
初学耗时:0.5h

注:CSDN手机端暂不支持章节内链跳转,但外链可用,更好体验还请上电脑端。

一、设计实现
  1.1  确定收集信息。
  1.2  确定埋点代码。
  1.3  前端数据收集脚本。
  1.4  后端脚本。
  1.5  日志格式。
  1.6  日志切分。



记忆词:

  埋点、植入统计代码、进行数据采集

Z05 - 999、网站流量日志分析


ギ 舒适区ゾ || ♂ 累觉无爱 ♀





一、设计实现

  1.1 ~ 确定收集信息。

  alt

  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 = ('https:' == document.location.protocol ?'https://ssl' : 'http://www') + '.google-analytics.com/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 ~ 前端数据收集脚本。
    1.3.1 .  数据收集脚本(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;
	})();
  • 整个脚本放在匿名函数里,确保不会污染全局环境。其中 log.gif 是后端脚本。
  1.4 ~ 后端脚本。
  • log.gif 是后端脚本,是一个伪装成 gif 图片的脚本。
    1.4.1 .  后端脚本一般需要完成以下几件事情:
  1. 解析 http 请求参数得到信息。
  2. 从 Web 服务器中获取一些客户端无法获取的信息,如访客 ip 等。
  3. 将信息按格式写入 log。
  4. 生成一副 1×1 的空 gif 图片作为响应内容并将响应头的 Content-type设为 image/gif。
  5. 在响应头中通过 Set-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_ag
ent||$u_account";
  • 注意这里以 u_开头的是我们待会会自己定义的变量,其它的是 nginx 内置变量。然后是核心的两个 location:
location / log.gif {
	#伪装成 gif 文件
	default_type image/gif;
	#本身关闭 access_log,通过 subrequest 记录 log
	access_log off;
	access_by_lua "
	-- 用户跟踪 cookie 名为__utrace
	local 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, mustrevalidate";
	#返回一个 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 格式为 tick
	access_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 分启动这个脚本进行日志轮转操作。


秦时明月汉时关,万里长征人未还。

- - - - - - - - - - - - - - - - - - - - - - - - - - - -



^ 至此,设计实现完成。


- - - - - - - - - - - - - - - - - - - - - - - - - - - -


※ 世间诱惑何其多,坚定始终不动摇。

Eclipse工具栏的解锁操作,是对Lock the Toolbars做如下操作:


A、单击此项
B、在其前面加√
C、双击此项
D、将其前面的√去掉

D
alt



秦时明月汉时关,万里长征人未还。

- - - - - - - - - - - - - - - - - - - - - - - - - - - -


注:CSDN手机端暂不支持章节内链跳转,但外链可用,更好体验还请上电脑端。

我知道我的不足,我也知道你的挑剔,但我就是我,不一样的烟火,谢谢你的指指点点,造就了我的点点滴滴:)!



秦时明月汉时关,万里长征人未还。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值