ngnix+lua+js埋点 实现自定义日志采集

ngnix+lua+js埋点 实现自定义日志采集

1 收集数据的页面

js埋点: 在想要手机数据的页面预先放一段js代码, 当用户发生行为时, 触发js方法, 收集数据,发到后端

http://192.168.4.101/index.html 这里的index.html page1.html page2.html 放在101的tomcat中

  • 在页面js中添加点击事件 点击事件触发后, 引入外部js 外部js中有发送请求的代码
    • js放到外部(可以放在静态资源服务器)的原因 : 解耦合 因为发送请求的js文件可能更改频繁
  • ma.async = true 的意思是异步调用外部 js 文件
    • 异步调用的好处: 不阻塞浏览器对页面的解析,待外部 js 下载完成后异步执行
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>日志采集</title>
		<script src="/jquery-3.2.1.min.js">
		</script>
				 
		<script type="text/javascript">
         <!-- 收集数据的数组 -->
		 var _maq = _maq || [];
		 _maq.push(['_setAccount', 'QuietHR']);
		
         <!-- jquery -->
		 $(function(){
         <!-- 埋点 给页面的a标签添加点击事件 当点击事件触发后, 
              收集数据到_maq数组中, 引入外部js ,发送给后端   -->
		 $("a").click(function(){
                <!-- 获取当前a标签的clstag属性 进行解析-->
				var clstag = $(this).attr("clstag");
                <!-- 获取当前a标签的文本 -->
				var _a_value = $(this).text();
                <!-- 封装数据 -->
				_maq.push(['_a_value',_a_value]);
				clstag = clstag.split('|');
				for (i in clstag){ 
					_maq.push(['type'+i, clstag[i]]);
					}
                    <!-- 引入外部js 发送请求 -->
					sendRequest();
				})
		});
        
        <!-- 创建script标签 设置为异步处理 src js的路径 将js添加到页面中  -->
		function sendRequest(){
				var ma = document.createElement('script'); 
				ma.type = 'text/javascript';
				ma.async = true;
				ma.src = 'http://192.168.4.103/ma.js';
                <!-- 获得js标签数组的第一个 将引入的js放在他之前  -->
				var s = document.getElementsByTagName('script')[0]; 
				s.parentNode.insertBefore(ma, s);
		}
		</script>
		
	</head>
	<body>
		<h1 align="center">bei cai ji de ye mian</h1>
		<a href="http://192.168.4.101/page1.html" target="_blank" clstag="click|index|page1">点击后触发click方法,并跳转到page1.html</a><br/>
		
		<a href="http://192.168.4.101/page2.html" target="_blank" clstag="click|index|page2">点击后触发click方法,并跳转到page2.html</a>
	</body>
</html>


2 要跳转的页面(page1.html)

page1.html 在加载时, 自调用匿名函数触发, 引用外部js

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>page1</title>	
	
		<script type="text/javascript">
		var _maq = _maq || [];
		_maq.push(['_setAccount', 'QuietHR']);
	 
		(function() {
			var ma = document.createElement('script'); 
			ma.type = 'text/javascript';
			ma.async = true;
			ma.src = 'http://192.168.4.103/ma.js';
			var s = document.getElementsByTagName('script')[0]; 
			s.parentNode.insertBefore(ma, s);
		})();
		</script>
		
				
	</head>
	<body>
		<h1 align="center">日志采集页面</h1>
		<h1 align="center">Page 1</h1>
	</body>
</html>

3 发送请求的js

http://192.168.4.103/ma.js 这里的js放在103服务器的nginx中

将_maq数组中的数据及其他信息(如document window) 封装到param对象中, 发送到日志采集服务器

  • js自调用匿名函数 (function(){})();

    • 避免重名
    • 自调用匿名函数 只在运行时调用一次, 一般用于初始化
    • 在这里使用是因为在用户进入 page1.html 时, 就要采集用户信息, 发送给日志采集服务器
  • 发送请求时,伪装成图片img发送

    • 使用图片的原因 : 如果使用js的http请求, 会发生js跨域请求问题
    • 请求一个图片,在请求URL中携带要采集的数据 所以重点在数据,而不是图片
    • 图片设置为1*1像素大小
(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;
            }   
        }   
    }   
    //将param对象中的属性 解析 拼接成字符串
    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.4.103/log.gif?' + args;
})();

4 nginx后端脚本(lua)

nginx的配置文件 nginx.conf

  • 定义一个自定义的日志文件 设置格式 log_format user_log_format
  • location /log.gif
    • 当有 192.168.4.103/log.gif 请求到达nginx时, 会触发这个脚本
    • 通过subrequest到/i-log记录日志 把URL参数转发过去
    • /i-log 中的internal表示这是一个内部location只能通过其他location转发子请求(subrequest)到自己
    • i-log 将数据进行解析, 写入到user_defined.log 日志文件中
    • i-log 响应空字符串给log.gif
    • log.gif响应空图片给js
# nginx启动时 开启两个线程
worker_processes  2;
# 连接数
events {
    worker_connections  1024;
}

# 重点
http {
    include       mime.types;
    default_type  application/octet-stream;
	
	# 日志文件格式  access.log  nginx默认的日志文件
	log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
  	
  	# 日志文件格式 user_defined.log  数据会写到这个文件中
    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;
        # 接收 localhost/log.gif 请求
		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 '';
		}	
	
    }
}

测试

写入到日志文件的数据中, 中文变成了乱码

  • \xE8\xBF\x99\xE6\x98\xAF\xE7\x82\xB9\xE5\x87\xBB
  • 修改ngnix源码…
# 点击事件触发的日志采集
1542028871.526||192.168.4.130||200||0||192.168.4.101||http://192.168.4.101/index.html||bei cai ji de ye mian||||1080||1920||24||zh-CN||Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0||QuietHR||\xE8\xBF\x99\xE6\x98\xAF\xE7\x82\xB9\xE5\x87\xBB1||click||index||page1
# 进入page1页面 自动触发的日志
1542028871.601||192.168.4.130||200||0||192.168.4.101||http://192.168.4.101/page1.html||page1||http://192.168.4.101/index.html||1080||1920||24||zh-CN||Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0||QuietHR||||||||
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值