一、概述
日志埋點分為客戶端和服務器端。參考並轉自:https://www.cnblogs.com/hzhuxin/p/11152805.html,如有侵權,請聯系刪除。)
①客戶端埋點:支持 iOS、安卓、Web/H5、微信小程序,主要用於分析 UV、PV、點擊量等基本指標。例:下圖是Web端的埋點技術圖:
②服務器日志:采集后端業務服務器打印的日志。更強的采集能力,更好的支撐精細化分析場景。(這里可以參考大牛博客:https://www.cnblogs.com/hzhuxin/p/11152805.html)
二、服務器規划
三、日志埋點實現
(1)客戶端埋點
在本項目案例中,采用客戶端網頁埋點實現,在其中需要埋點的頁面中的<head></head>中加入如下代碼:(參考並轉自:https://www.cnblogs.com/hzhuxin/p/11152805.html,如有侵權,請聯系刪除。)
<script src="tj.js"></script> 注:tj.js 就是需埋點的 js 文件
/**函數可對字符串進行編碼,這樣就可以在所有的計算機上讀取該字符串。*/ function ar_encode(str) { //進行URL編碼 return encodeURI(str); } /**屏幕分辨率*/ function ar_get_screen() { var c = ""; if (self.screen) { c = screen.width+"x"+screen.height; } return c; } /**顏色質量*/ function ar_get_color() { var c = ""; if (self.screen) { c = screen.colorDepth+"-bit"; } return c; } /**返回當前的瀏覽器語言*/ function ar_get_language() { var l = ""; var n = navigator; if (n.language) { l = n.language.toLowerCase(); } else if (n.browserLanguage) { l = n.browserLanguage.toLowerCase(); } return l; } /**返回瀏覽器類型IE,Firefox*/ function ar_get_agent() { var a = ""; var n = navigator; if (n.userAgent) { a = n.userAgent; } return a; } /**方法可返回一個布爾值,該值指示瀏覽器是否支持並啟用了Java*/ function ar_get_jvm_enabled() { var j = ""; var n = navigator; j = n.javaEnabled() ? 1 : 0; return j; } /**返回瀏覽器是否支持(啟用)cookie */ function ar_get_cookie_enabled() { var c = ""; var n = navigator; c = n.cookieEnabled ? 1 : 0; return c; } /**檢測瀏覽器是否支持Flash或有Flash插件*/ function ar_get_flash_ver() { var f="",n=navigator; if (n.plugins && n.plugins.length) { for (var ii=0;ii<n.plugins.length;ii++) { if (n.plugins[ii].name.indexOf('Shockwave Flash')!=-1) { f=n.plugins[ii].description.split('Shockwave Flash ')[1]; break; } } } else if (window.ActiveXObject) { for (var ii=10;ii>=2;ii--) { try { var fl=eval("new ActiveXObject('ShockwaveFlash.ShockwaveFlash."+ii+"');"); if (fl) { f=ii + '.0'; break; } } catch(e) {} } } return f; } /**匹配頂級域名*/ function ar_c_ctry_top_domain(str) { var pattern = "/^aero$|^cat$|^coop$|^int$|^museum$|^pro$|^travel$|^xxx$|^com$|^net$|^gov$|^org$|^mil$|^edu$|^biz$|^info$|^name$|^ac$|^mil$|^co$|^ed$|^gv$|^nt$|^bj$|^hz$|^sh$|^tj$|^cq$|^he$|^nm$|^ln$|^jl$|^hl$|^js$|^zj$|^ah$|^hb$|^hn$|^gd$|^gx$|^hi$|^sc$|^gz$|^yn$|^xz$|^sn$|^gs$|^qh$|^nx$|^xj$|^tw$|^hk$|^mo$|^fj$|^ha$|^jx$|^sd$|^sx$/i"; if(str.match(pattern)){ return 1; } return 0; } /**處理域名地址*/ function ar_get_domain(host) { //如果存在則截去域名開頭的 "www." var d=host.replace(/^www\./, ""); //剩余部分按照"."進行split操作,獲取長度 var ss=d.split("."); var l=ss.length; //如果長度為3,則為xxx.yyy.zz格式 if(l == 3){ //如果yyy為頂級域名,zz為次級域名,保留所有 if(ar_c_ctry_top_domain(ss[1]) && ar_c_ctry_domain(ss[2])){ } //否則只保留后兩節 else{ d = ss[1]+"."+ss[2]; } } //如果長度大於3 else if(l >= 3){ //如果host本身是個ip地址,則直接返回該ip地址為完整域名 var ip_pat = "^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$"; if(host.match(ip_pat)){ return d; } //如果host后兩節為頂級域名及次級域名,則保留后三節 if(ar_c_ctry_top_domain(ss[l-2]) && ar_c_ctry_domain(ss[l-1])) { d = ss[l-3]+"."+ss[l-2]+"."+ss[l-1]; } //否則保留后兩節 else{ d = ss[l-2]+"."+ss[l-1]; } } return d; } /**返回cookie信息*/ function ar_get_cookie(name) { //獲取所有cookie信息 var co=document.cookie; //如果名字是個空 返回所有cookie信息 if (name == "") { return co; } //名字不為空 則在所有的cookie中查找這個名字的cookie var mn=name+"="; var b,e; b=co.indexOf(mn); //沒有找到這個名字的cookie 則返回空 if (b < 0) { return ""; } //找到了這個名字的cookie 獲取cookie的值返回 e=co.indexOf(";", b+name.length); if (e < 0) { return co.substring(b+name.length + 1); } else { return co.substring(b+name.length + 1, e); } } /** 設置cookie信息 操作符: 0 表示不設置超時時間 cookie是一個會話級別的cookie cookie信息保存在瀏覽器內存當中 瀏覽器關閉時cookie消失 1 表示設置超時時間為10年以后 cookie會一直保存在瀏覽器的臨時文件夾里 直到超時時間到來 或用戶手動清空cookie為止 2 表示設置超時時間為1個小時以后 cookie會一直保存在瀏覽器的臨時文件夾里 直到超時時間到來 或用戶手動清空cookie為止 * */ function ar_set_cookie(name, val, cotp) { var date=new Date; var year=date.getFullYear(); var hour=date.getHours(); var cookie=""; if (cotp == 0) { cookie=name+"="+val+";"; } else if (cotp == 1) { year=year+10; date.setYear(year); cookie=name+"="+val+";expires="+date.toGMTString()+";"; } else if (cotp == 2) { hour=hour+1; date.setHours(hour); cookie=name+"="+val+";expires="+date.toGMTString()+";"; } var d=ar_get_domain(document.domain); if(d != ""){ cookie +="domain="+d+";"; } cookie +="path="+"/;"; document.cookie=cookie; } /**返回客戶端時間*/ function ar_get_stm() { return new Date().getTime(); } /**返回指定個數的隨機數字串*/ function ar_get_random(n) { var str = ""; for (var i = 0; i < n; i ++) { str += String(parseInt(Math.random() * 10)); } return str; } /* main function */ function ar_main() { //收集完日志 提交到的路徑 var dest_path = "http://127.0.0.1:8081/log?"; var expire_time = 30 * 60 * 1000;//會話超時時長 //處理uv //--獲取cookie ar_stat_uv的值 var uv_str = ar_get_cookie("ar_stat_uv"); var uv_id = ""; //--如果cookie ar_stat_uv的值為空 if (uv_str == ""){ //--為這個新uv配置id,為一個長度20的隨機數字 uv_id = ar_get_random(20); //--設置cookie ar_stat_uv 保存時間為10年 ar_set_cookie("ar_stat_uv", uv_id, 1); } //--如果cookie ar_stat_uv的值不為空 else{ //--獲取uv_id uv_id = uv_str; } //處理ss //--獲取cookie ar_stat_ss var ss_stat = ar_get_cookie("ar_stat_ss"); var ss_id = ""; //sessin id var ss_count = 0; //session有效期內訪問頁面的次數 var ss_time = ""; //--如果cookie中不存在ar_stat_ss 說明是一次新的會話 if (ss_stat == ""){ //--隨機生成長度為10的session id ss_id = ar_get_random(10); //--session有效期內頁面訪問次數為0 ss_count = 0; //--當前事件 ss_time = ar_get_stm() } else { //--如果cookie中存在ar_stat_ss //獲取ss相關信息 var items = ss_stat.split("_"); //--ss_id ss_id = items[0]; //--ss_count ss_count = parseInt(items[1]); //--ss_stm ss_time = items[2]; //如果當前時間-當前會話上一次訪問頁面的時間>30分鍾,雖然cookie還存在,但是其實已經超時了!仍然需要重新生成cookie if (ar_get_stm() - ss_time > expire_time) { //--重新生成會話id ss_id = ar_get_random(10); //--設置會話中的頁面訪問次數為0 ss_count = 0; //--當前事件 ss_time = ar_get_stm(); }else{//--如果會話沒有超時 //--會話id不變 //--設置會話中的頁面方位次數+1 ss_count = ss_count + 1; ss_time = ar_get_stm(); } } //--重新拼接cookie ar_stat_ss的值 value = ss_id+"_"+ss_count+"_"+ss_time; ar_set_cookie("ar_stat_ss", value, 0); //當前地址 var url = document.URL; url = ar_encode(String(url)); //當前資源名 var urlname = document.URL.substring(document.URL.lastIndexOf("/")+1); urlname = ar_encode(String(urlname)); //返回導航到當前網頁的超鏈接所在網頁的URL var ref = document.referrer; ref = ar_encode(String(ref)); //網頁標題 var title = document.title; title = ar_encode(String(title)); //網頁字符集 var charset = document.charset; charset = ar_encode(String(charset)); //屏幕信息 var screen = ar_get_screen(); screen = ar_encode(String(screen)); //顏色信息 var color =ar_get_color(); color =ar_encode(String(color)); //語言信息 var language = ar_get_language(); language = ar_encode(String(language)); //瀏覽器類型 var agent =ar_get_agent(); agent =ar_encode(String(agent)); //瀏覽器是否支持並啟用了java var jvm_enabled =ar_get_jvm_enabled(); jvm_enabled =ar_encode(String(jvm_enabled)); //瀏覽器是否支持並啟用了cookie var cookie_enabled =ar_get_cookie_enabled(); cookie_enabled =ar_encode(String(cookie_enabled)); //瀏覽器flash版本 var flash_ver = ar_get_flash_ver(); flash_ver = ar_encode(String(flash_ver)); //當前ss狀態 格式為"會話id_會話次數_當前時間" var stat_ss = ss_id+"_"+ss_count+"_"+ss_time; //拼接訪問地址 增加如上信息 dest=dest_path+"url="+url+"&urlname="+urlname+"&title="+title+"&chset="+charset+"&scr="+screen+"&col="+color+"&lg="+language+"&je="+jvm_enabled+"&ce="+cookie_enabled+"&fv="+flash_ver+"&cnv="+String(Math.random())+"&ref="+ref+"&uagent="+agent+"&stat_uv="+uv_id+"&stat_ss="+stat_ss; //通過插入圖片訪問該地址 document.getElementsByTagName("body")[0].innerHTML += "<img src=\""+dest+"\" border=\"0\" width=\"1\" height=\"1\" />"; } window.onload = function(){ //觸發main方法 ar_main(); }
說明:
①var dest_path = "http://127.0.0.1:8081/log?"; 此處要改成日志服務器的地址,並且 這個地址是能夠被訪問的,最后一個 "?“ 不要忘加,用來拼后續參數使用的。
②埋點的原理:js代碼會動態在頁面中創建一個寬和高都是1px的圖片,圖片的地址指向了1中定義的日志服務器中的圖片,
document.getElementsByTagName("body")[0].innerHTML += "<img src=\""+dest+"\" border=\"0\" width=\"1\" height=\"1\" />";
(2)服務器端開發(關鍵代碼)
package com.logs.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; /** * @ProjectName: logdemo * @Package: com.logs.controller * @ClassName: LogController * @Author: dong * @Description: ${description} * @Date: 2019/9/4 22:39 * @Version: 1.0 */ @Controller public class LogController { private Logger logger = LoggerFactory.getLogger(LogController.class); @RequestMapping("/log") public void log(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { //1.獲取請求參數 String qs = request.getQueryString(); //2.對URL解碼 String decode = URLDecoder.decode(qs, "utf-8"); //3.轉換成需要處理的格式 StringBuilder sb = new StringBuilder(); String[] attrs = decode.split("&"); for (String attr : attrs) { String[] kv = attr.split("="); String val = kv.length >= 2 ? kv[1] : ""; sb.append(val+"|"); } sb.append(request.getRemoteAddr()); String logStr = sb.toString(); // System.out.println(logStr); logger.info(logStr); } }
四、總結
至此,已經完成了日志的埋點,以供后續日志收集:網站日志流量分析系統之(日志收集),本項目地址:https://github.com/Simple-Coder/log-demo