2017-03-28 22:21:04 Nginx 数据收集 1883
导读:
用户数据主要分客户端主动上报和业务服务数据。
客户端主动上报的需要在前段做数据埋点,服务端部署服务收集上报的数据,我们这里用Nginx做日志收集服务,因为对日志收集服务来说,最重要的稳定、高并发和吞吐量,不涉及业务逻辑,保证数据不丢失。而Nginx是不错的选择。
业务服务数据分很多种,一种是Nginx的访问日志,一种是业务数据库中存储的数据,还有业务服务输出的日志等。
这里介绍的用户数据主要基于浏览器和移动APP。
默认能够收集的信息如下:
名字 | 途径 | 备注 |
---|---|---|
访问时间 | web service | Nginx $msec |
IP | web service | Nginx $remote_addr |
域名 | javascript | document.domain |
URL | javascript / web service | document.URL / $request |
页面标签 | javascript | document.title |
分辨率 | javascript | windows.screen.height & width |
颜色深度 | javascript | windows.screen.colorDepth |
Referrer | javascript / web service | document.referrer / $http_referer |
客户端信息 | javascript / web service | navigator.userAgent / $http_user_agent |
客户端语言 | javascript | navigator.language |
其他数据,需要根据业务来添加,另外,用户唯一标识必须要有,用来统计用户数。
用户唯一标识有两种实现方式,一种是客户端生成,一种是服务端生成。Google和Baidu貌似是服务端生成,实现方式无法知道。
以下为客户端生成用户以为标识的流程图:
以下为JavaScript代码:
(function () {
var params = {};
var uuid = "";
function getCookie(c_name) {
if (document.cookie.length > 0) {
c_start = document.cookie.indexOf(c_name + "=");
if (c_start != -1) {
c_start = c_start + c_name.length + 1;
c_end = document.cookie.indexOf(";", c_start);
if (c_end == -1)
c_end = document.cookie.length;
return unescape(document.cookie.substring(c_start, c_end));
}
}
return "";
};
function generateUUID(){
var d = new Date().getTime();
uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x7|0x8)).toString(16);
});
return uuid.replace(new RegExp("-","gm"), "");
};
function checkUUID() {
uuid = getCookie('uuid');
if (uuid.length <= 0) {
var date = new Date();
date.setFullYear(date.getFullYear() + 10);
uuid = generateUUID();
document.cookie = "uuid=" + uuid + "; expires=" + date.toGMTString() + "; path=/; domain=.ssports";
}
return uuid;
};
params.uuid = checkUUID();
function getQueryParameter(q_name) {
var reg = new RegExp("(^|&)" + q_name + "=([^&]*)(&|$)", "i");
var result = window.location.search.substr(1).match(reg);
if (result != null) {
return result[2];
} else {
return null;
}
;
}
if (document) {
//params.domain = document.domain || '';
params.page = document.URL || '';
params.title = document.title || '';
//params.referrer = document.referrer || '';
var s = getQueryParameter('source');
if (s != undefined && s.length > 0) {
document.cookie = "source=" + s + "; path=/; domain=.ssports";
}
}
if (window) {
if (window.screen) {
params.sh = window.screen.height || 0;
params.sw = window.screen.width || 0;
params.cd = window.screen.colorDepth || 0;
}
}
if (navigator) {
params.lang = navigator.language || '';
//params.acn = navigator.appCodeName || '';
// params.an = navigator.appName || '';
//params.av = navigator.appVersion || '';
//params.plat = navigator.platform || '';
//params.agent = navigator.userAgent || '';
}
var args = '';
for (var i in params) {
if (args != '') {
args += '&';
}
args += i + '=' + encodeURIComponent(params[i]);
}
var img = new Image(1, 1);
img.src = 'https://kiswo.com/logs/1.gif?' + args;
})();
由于客户端在上报数据,很容易涉及到跨域问题,而用gif作为接口,对浏览器就不存在跨越问题。
https://log.mmstat.com/2.gif?logtype=1&title=%u5929%u732Btmall.com--%u4E0A%u5929%u732B%uFF0C%u5C31%u591F%u4E86&pre=&cache=c90e9ea&scr=1366x768&category=&uidaplus=&aplus&isbeta=7&p=1&o=win7&b=firefox47&s=1366x768&w=gecko&ism=pc&lver=6.1.16&jsver=aplus_v2
http://msg.71.am/cp2.gif?s=1468927518661&av=ares-2.18.6&y=qc_100001_100025&a=e2babb37798994618a7f1186c7fad602&p=t&rc=-1&t=s&rd=794&ai=0%7C%7C%7C%7C5000000839545%7C%7C5000000929733%7C%7C&d=54000&g=0&l=MTI0LjE5Mi4yMDYuNTg%3D
并不是所有参数都在URI,有些公共参数是放在Cookie或Header中的。
APP数据收集应当尽量从业务服务Nginx日志来获取,减少客户端的上报,以减轻客户端的工作和减少性能影响。
APP数据上报接口和业务服务接口差不多。
http://alog.umeng.com/app_logs?...
无论是PC的上报方式,还是APP的上报方式,都需要注意缓存问题,不能让接口请求出现缓存。
PC的上报方式接口一般都是返回一个空图片(小图片)。
APP的上报方式接口可以没有返回值,也可以返回其它内容。
日志记录格式:
log_format main_gif '$remote_addr | $http_x_forwarded_for | $msec | [$time_local] | "$request" | "$cookie_source" | $status | "$http_referer" | "$http_user_agent"';
请求接口配置:
location /logs/1.gif {
access_log logs/access_gif_1.log main_gif;
default_type image/gif;
#可以获取Cookie中的值
set $cookie_source "";
if ($http_cookie ~* "source=(.+)(?:;|$)" ) {
set $cookie_source $1;
}
#etag on;
expires off;
add_header Last-Modified '';
add_header Cache-Control 'no-cache';
add_header Pragma "no-cache";
empty_gif;
}
location /logs/app {
default_type image/gif;
expires off;
add_header Last-Modified '';
add_header Cache-Control 'no-cache';
add_header Pragma "no-cache";
empty_gif;
}
数据上报的请求服务,一般都是GET方式,如果有POST方式,就需要获取request_body
的内容。
由于Nginx是为了解决负载均衡场景诞生的,所以它默认是不读取body
的行为,会对API Server和Web Application场景造成一些影响。根据需要正确读取、丢弃body
对OpenResty
开发是至关重要的。
以下为POST的请求方式,并获取request_body
。
日志记录格式:
log_format main_gif '$remote_addr | $http_x_forwarded_for | $msec | [$time_local] | "$request" | "$request_body" | $status | "$http_referer" | "$http_user_agent"';
请求接口配置:
location /collect {
access_log logs/access_old.log main_old;
lua_code_cache off;
set $body_data '';
content_by_lua_block {
ngx.req.read_body()
local data = ngx.req.get_body_data()
ngx.var.body_data = data
ngx.say("body: ", data)
}
}
Nginx运行时间久了,日志会变的很大,如果不切割,处理起来会很不方便。
我们一般按小时来切割日志。对应其他日志,例如Nginx的错误日志或不是很重要的日志,可以采取按天或月切割。
#!/bin/bash
#nginx_log_division.sh
##############################
# Nginx 日志切割 #
# Author: wangr #
# Date: 2016-09-23 #
##############################
CUT_TIME=$1
if [ ! -n "$CUT_TIME" ]; then
CUT_TIME=`date -d "1 hours ago" +%Y%m%d%H`
fi
#DATE_DAY=${NOW_DATE//'-'/''}
CUT_HOUR=${CUT_TIME:8:10}
echo "`date +"%Y-%m-%d %H:%M:%S"` start execute script '$0', time is: $CUT_TIME"
echo "$CUT_HOUR"
LOGS_PATH=/usr/local/nginx/logs
mv ${LOGS_PATH}/access.log ${LOGS_PATH}/access_${CUT_TIME}.log
if [ "$CUT_HOUR" -eq "23" ]; then
echo "division error.log"
#mv ${LOGS_PATH}/error.log ${LOGS_PATH}/error_${CUT_TIME}.log
fi
kill -USR1 $(cat /usr/local/nginx/logs/nginx.pid)
echo "`date +"%Y-%m-%d %H:%M:%S"` over execute script '$0', time is: $CUT_TIME"
下面为阿里的老鱼写的一篇文章,有好几年了,网站已经打不开了,只在他的微博上找到了几张图。膜拜一下。
用户行为分析解决方案
网站监控
相关文章