分析 :
在采集数据的网页上进行
埋点
(在网页中预先加入小段 javascript 代码) , 编写采集数据的js(该js一般再用一个服务器去存储 , 为的就是解耦合 , 便于修改)通过img标签的src属性解决跨域问题
将数据传递给后端服务器
后端服务器执行步骤
3.1接受请求 , 响应图片(log.gif)
3.2 解析参数 , 保存数据
3.3 设置cookie
埋点的js代码通常使用
匿名JS函数
(JS匿名函数自调用 , 执行且只执行一次 , 通常用于在页面加载的初始化相关操作)
扩展知识:js 自调用匿名函数
格式: (function(){})()
;
第一对括号向脚本返回未命名的函数;后一对空括号立即执行返回的未命名函数,括号内为匿名函数的参数。
自调用匿名函数的好处是,避免重名,自调用匿名函数只会在运行时执行一次,一般用于初始化。
名称 | 途径 | 备注 |
---|---|---|
访问时间 | web server | Nginx $msec |
IP | web server | Nginx $remote_add |
域名 | 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 |
2.2 确定埋点代码
2.3 前端数据收集脚本
2.4 后端脚本
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";
日志格式主要考虑日志分隔符,一般会有以下几种选择:
固定数量的字符、制表符分隔符、空格分隔符、其他一个或多个字符、特的开始和结束文本
。
日志收集系统访问日志时间一长文件变得很大,而且日志放在一个文件不便于管理。通常要按时间段将日志切分,例如每天或每小时切分一个日志。通过crontab 定时调用一个 shell 脚本实现,如下:
vi rotatelog.sh
#!/bin/bash
_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 `
USR1 通常被用来告知应用程序重载配置文件
cat ${_prefix}/logs/nginx.pid
取 nginx 的进程号
然后再/etc/crontab 里加入一行:
59 * * * * root /path/to/directory/rotatelog.sh
补充 :
cookie session
cookie是浏览器端用于标识访客信息
cookie值不能够重复
nginx日志默认一直写在同一个文件中 越来越大 不利于后续数据分析
按时间 每小时切割一次
按大小 每50M切割
细心 , 细心 ,再细心
所有软件都上传到/export/software
下 , 选择node-3作为nginx服务器
服务器中安装依赖
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/profile
export LUAJIT_LIB=/usr/local/luajit/lib
export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0
source /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.gz
ngx_devel_kit-0.2.19.tar.gz
echo-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 -j2
make install
备注:如果对 linux 相关操作不熟,请严格按照上述步骤搭建环境,切记心细,心细,再心细。
我们将node-1作为网站服务器 , node-3作为nginx服务器
a) 创建页面 index.html,添加埋点代码,放入 nginx 默认目录 nginx/html 下。
埋点代码
ma.async = true
的意思是异步调用外部 js 文件即不阻塞浏览器的解析,待外部 js 下载完成后异步执行。埋点的JS
yum -y install httpd
service httpd start
/var/www/html
目录下 , 上传前一定要注意linux地址的修改
<html>
<head>
<meta charset="UTF-8">
<title>welcom to itheimatitle>
<script type="text/javascript">
var _maq = _maq || [];
_maq.push(['_setAccount', 'AllenWoon']);
//JS自调用匿名函数
(function() {
var ma = document.createElement('script');
ma.type = 'text/javascript';
ma.async = true;
//引入的数据搜集的脚本
ma.src = 'http://192.168.244.153/ma.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ma, s);
})();
script>
head>
<body>
<h1 align="center">黑马程序员--云计算大数据h1>
body>
html>
b) 在默认目录 nginx/html 下添加一个数据采集脚本 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://192.168.244.153/log.gif?' + args;
})();
c) 修改 nginx 的配置文件,添加自定义相关业务逻辑。
后端脚本Nginx+Lua
cd /usr/local/nginx
>cd conf/
> 逻辑删除之前的nginx.conf文件mv nginx.conf nginx.conf.bak
>将下面的nginx.conf拷贝到当前目录即可>启动nginx , 首先回到cd /usr/local/nginx
处 , 执行启动命令sbin/nginx -c conf/nginx.conf
==>实时监测日志文件cd logs/
tail -f user_defined.log
worker_processes 2;
#每一个worker进程能并发处理(发起)的最大连接数
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format user_log_format "$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";
sendfile on; #允许sendfile方式传输文件,默认为off
keepalive_timeout 65; #连接超时时间,默认为75s
server {
listen 80;
server_name localhost;
location /log.gif {
#伪装成gif文件
default_type image/gif;
#nginx本身记录的access_log,日志格式为main
access_log logs/access.log main;
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
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;
#打开subrequest(子请求)日志
log_subrequest on;
#自定义采集的日志,记录数据到user_defined.log
access_log logs/user_defined.log user_log_format;
#输出空字符串
echo '';
}
}
}
d) 启动 nginx
sbin/nginx -c conf/nginx.conf
重启nginx==> sbin/nginx -s reload
e) 通过游览器访问 nginx
http://192.168.244.151/index.html
f) 观察自定义日志采集文件是否有对应的内容输出
tail -f logs/user_defined.log
此时还可以观察 nginx 默认的输出日志文件
tail -f logs/access.log
停止 nginx:
sbin/nginx –s stop
测试结果
1542023107.669||192.168.244.130||200||0||192.168.244.151||http://192.168.244.151/index.html||welcom to itheima||||768||1366||24||zh-CN||Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0||AllenWoon
clstag
, 用于触发点击事件index2.html
<html>
<head>
<meta charset="UTF-8">
<title>welcom to itheimatitle>
<script src="/jquery-3.2.1.min.js">
script>
<script type="text/javascript">
var _maq = _maq || [];
_maq.push(['_setAccount', 'AllenWoon']);
$(function(){
$("a").click(function(){
var clstag = $(this).attr("clstag");
var _a_value = $(this).text();
_maq.push(['_a_value',_a_value]);
clstag = clstag.split('|');
for (i in clstag){
_maq.push(['type'+i, clstag[i]]);
}
sendRequest();
})
});
function sendRequest(){
var ma = document.createElement('script');
ma.type = 'text/javascript';
ma.async = true;
ma.src = 'http://192.168.244.153/ma.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ma, s);
}
script>
head>
<body>
<h1 align="center">黑马程序员--云计算大数据h1>
<a href="http://192.168.244.151/page1.html" target="_blank" clstag="click|index|page1">这是点击1a><br/>
<a href="http://192.168.244.151/page2.html" target="_blank" clstag="click|index|page2">这是点击2a>
body>
html>
page1.html
<html>
<head>
<meta charset="UTF-8">
<title>page1title>
<script type="text/javascript">
var _maq = _maq || [];
_maq.push(['_setAccount', 'AllenWoon']);
(function() {
var ma = document.createElement('script');
ma.type = 'text/javascript';
ma.async = true;
ma.src = 'http://192.168.244.153/ma.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ma, s);
})();
script>
head>
<body>
<h1 align="center">黑马程序员--云计算大数据h1>
<h1 align="center">Page 1h1>
body>
html>
page2.html
<html>
<head>
<meta charset="UTF-8">
<title>page2title>
<script type="text/javascript">
var _maq = _maq || [];
_maq.push(['_setAccount', 'AllenWoon']);
(function() {
var ma = document.createElement('script');
ma.type = 'text/javascript';
ma.async = true;
ma.src = 'http://192.168.244.153/ma.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ma, s);
})();
script>
head>
<body>
<h1 align="center">黑马程序员--云计算大数据h1>
<h1 align="center">page 2h1>
body>
html>
/usr/local/nginx/html
中之前的ma.js删除 , 将新的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;
case '_a_value':
params.avalue = _maq[i][1];
break;
case 'type0':
params.type0 = _maq[i][1];
break;
case 'type1':
params.type1 = _maq[i][1];
break;
case 'type2':
params.type2 = _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://192.168.244.153/log.gif?' + args;
})();
/usr/local/nginx/conf
中之前的nginx.conf删除 , 将新的nginx.conf传输进去sbin/nginx -s reload
worker_processes 2;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format user_log_format "$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_avalue||$u_type0||$u_type1||$u_type2";
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location /log.gif {
#伪装成gif文件
default_type image/gif;
#nginx本身记录的access_log,日志格式为main
access_log logs/access.log main;
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
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;
set_unescape_uri $u_avalue $arg_avalue;
set_unescape_uri $u_type0 $arg_type0;
set_unescape_uri $u_type1 $arg_type1;
set_unescape_uri $u_type2 $arg_type2;
#打开subrequest(子请求)日志
log_subrequest on;
#自定义采集的日志,记录数据到user_defined.log
access_log logs/user_defined.log user_log_format;
#输出空字符串
echo '';
}
}
}
测试如下 : 每次点击之后生成两个日志 , 一个是点击的 , 一个是点击后网页的
1542023777.191||192.168.244.130||200||0||192.168.244.151||http://192.168.244.151/index2.html||welcom to itheima||||768||1366||24||zh-CN||Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0||AllenWoon||\xE8\xBF\x99\xE6\x98\xAF\xE7\x82\xB9\xE5\x87\xBB1||click||index||page1
1542023777.260||192.168.244.130||200||0||192.168.244.151||http://192.168.244.151/page1.html||page1||http://192.168.244.151/index2.html||768||1366||24||zh-CN||Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0||AllenWoon||||||||
1542023784.207||192.168.244.130||200||0||192.168.244.151||http://192.168.244.151/index2.html||welcom to itheima||||768||1366||24||zh-CN||Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0||AllenWoon||\xE8\xBF\x99\xE6\x98\xAF\xE7\x82\xB9\xE5\x87\xBB2||click||index||page2
1542023784.267||192.168.244.130||200||0||192.168.244.151||http://192.168.244.151/page2.html||page2||http://192.168.244.151/index2.html||768||1366||24||zh-CN||Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0||AllenWoon||||||||
扩展
- linux上C语言软件安装步骤
- ./configure 检查编译环境
- make 编译
- make install 编译后安装 --prefix
tips: 一键启动nginx脚本编写
vi startNg.sh
==>
#!/bin/bash
/usr/local/nginx/sbin/nginx -c conf/nginx.conf
授予权限chmod u+x startNg.sh
tips : 一键关闭nginx脚本编写
#!/bin/bash
/usr/local/nginx/sbin/nginx -s stop
授予权限chmod u+x stopNg.sh
查看是否开启ps -ef | grep nginx