详细数据:网站流量日志数据自定义采集实现
优采云 发布时间: 2020-12-19 11:17详细数据:网站流量日志数据自定义采集实现
为什么需要对网站个流量数据进行统计分析?
随着大数据时代的到来,各行各业所生成的数据爆炸了。大数据技术已从以前的``虚无''变为可能,人们逐渐发现由数据产生的各种潜在价值。用于各行各业。例如,对网站流量数据的统计分析可以帮助网站管理员,操作员,发起人等获取实时网站流量信息,并从流量来源,网站内容等各个方面提供信息,以及网站访问者特征网站分析的数据基础。这将有助于增加网站的访问量并改善网站的用户体验,使更多的访客成为会员或客户,并以较少的投资获得最大的收益。
网站交通记录数据采集原理分析
首先,用户的行为将触发浏览器对正在计数的页面的http请求,例如打开某个网页。打开网页后,将执行页面中嵌入的javascript代码。
埋点是指:在网页中预先添加一小段javascript代码。此代码段通常将动态创建脚本标签,并将src属性指向单独的js文件。此时,浏览器将请求并执行此单独的js文件(图中的绿色节点)。该js通常是真正的数据采集脚本。
数据采集完成后,js将请求后端数据采集脚本(图中的后端)。该脚本通常是伪装成图片的动态脚本程序。 js将通过http参数传递采集的数据。对于后端脚本,后端脚本解析参数并以固定格式记录访问日志。同时,它可能会在http响应中为客户端植入一些跟踪cookie。
设计与实现
基于原理分析并结合Google Analytics(分析),如果您要构建自定义日志数据采集系统,则需要执行以下操作:
确认采集信息
确定掩埋点代码
埋点是用于网站分析的常用data 采集方法。核心是在需要数据采集来执行数据采集的关键点植入统计代码。例如,在Google Analytics(分析)原型中,需要将其提供的javascript片段插入页面。该片段通常称为嵌入式代码。 (以Google的嵌入式代码为例)
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( m a, s);
})();
其中_maq是一个全局数组,用于放置各种配置,每种配置的格式为:
_maq.push(['Action','param1','param2',...]);
_maq的机制不是焦点,而是焦点在于后面的匿名函数代码。该代码的主要目的是通过通过document.createElement方法创建脚本并遵循协议(http或https)将src指向相应的ma.js来引入外部js文件(ma.js),最后将其插入元素放入页面的dom树中。
请注意,ma.async = true表示外部js文件是异步调用的,即,它不会阻止浏览器的解析,并且将在外部js下载完成后异步执行。此属性是HTML5中新引入的。
前端数据采集脚本
数据采集脚本(ma.js)将在请求后执行。通常,应执行以下操作:
通过浏览器的内置javascript对象采集信息,例如页面标题(通过document.title),引荐来源网址(最后一个URL,通过document.referrer),用户显示分辨率(通过windows.screen),cookie信息(通过document.cookie)等。解析_maq数组并采集配置信息。这可能包括用户定义的事件跟踪,业务数据(例如电子商务网站产品编号等)。以上两个步骤中采集的数据将以预定义的格式进行解析和拼接(获取请求参数)。请求后端脚本,然后将http请求参数中的信息放入后端脚本。
这里唯一的问题是第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是后端脚本。
后端脚本
log.gif是后端脚本,该脚本伪装成gif图片。后端脚本通常需要完成以下操作:
分析http请求参数以获取信息。获取客户端无法从Web服务器获取的某些信息,例如visitor ip。以日志格式写入信息。生成1×1的空白gif图像作为响应内容,并将响应头的Content-type设置为image / gif。通过响应标头中的Set-cookie设置一些必需的cookie信息。
设置cookie的原因是因为如果您要跟踪唯一的访问者,通常的做法是根据规则生成一个全局唯一的cookie,如果发现客户端没有该cookie,则将其植入用户。在请求时指定跟踪cookie,否则Set-cookie放置获取的跟踪cookie以保持相同的用户cookie不变。尽管这种方法并不完美(例如,清除cookie或更改浏览器的用户将被视为两个用户),但目前已广泛使用。
我们使用nginx的access_log进行日志采集,但是存在一个问题,即nginx配置本身具有有限的逻辑表达能力,因此我们选择OpenResty来实现。
OpenResty是基于Nginx的高性能应用程序开发平台。它集成了许多有用的模块,其核心是通过ngx_lua模块将Lua集成在一起,因此Lua可用于在nginx配置文件中表达业务。
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 / 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, 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 格式为 tick
access_log /path/to/logs/directory/ma.log tick;
#输出空字符串
echo '';
}
此脚本使用许多第三方ngxin模块(全部收录在OpenResty中),关键点带有注释。只要您在完成我们提到的End逻辑就可以了之后,就不需要完全了解每一行的含义。
日志格式
日志格式主要考虑日志分隔符,通常有以下选项:
固定数量的字符,制表符,空格,一个或多个其他字符,特定的开始和结束文本。
日志细分
只要日志采集系统访问日志,文件就会变得很大,并且很难在一个文件中管理日志。通常有必要根据时间段拆分日志,例如每天或每小时一个日志。它是通过定期通过crontab调用shell脚本来实现的,如下所示:
_prefix="/path/to/nginx"
time=`date +%Y%m%d%H`
mv ${_prefix}/logs/ma.log ${_prefix}/logs/ma/ma-${time}.log
kill -USR1 `cat ${_prefix}/logs/nginx.pid `
此脚本将ma.log移至指定的文件夹,并将其重命名为ma- {yyyymmddhh} .log,然后将USR1信号发送给nginx以重新打开日志文件。
USR1通常用于通知应用程序重新加载配置文件。向服务器发送USR1信号将导致执行以下步骤:停止接受新连接,等待当前连接停止,重新加载配置文件,然后重新打开日志文件,重新启动服务器以实现相对平稳的更改而不关闭
cat $ {_ prefix} /logs/nginx.pid接受nginx的进程号
然后在/ etc / crontab中添加一行:
59 * * * *根/path/to/directory/rotatelog.sh
每小时59分钟启动此脚本以执行日志轮换操作。