agent.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. (function(global, doc) {
  2. if (typeof global.openDatabase !== 'function')
  3. return;
  4. // 生成uuid
  5. function uuid() {
  6. var s = [], hexDigits = "0123456789abcdef";
  7. for (var i = 0; i < 36; i++) {
  8. s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
  9. }
  10. s[14] = "4";
  11. s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
  12. s[8] = s[13] = s[18] = s[23] = "-";
  13. return s.join("");
  14. }
  15. // ajax
  16. var Ajax = function(config) {
  17. var request = new XMLHttpRequest();
  18. request.onreadystatechange = function() {
  19. if (request.readyState == 4) {
  20. if (request.status == 200) {
  21. config.success && config.success.call(null, request.responseText);
  22. } else {
  23. config.error && config.error.call(null, request.responseText);
  24. }
  25. }
  26. };
  27. request.open("POST", config.url, true);
  28. request.send(config.params ? JSON.stringify(config.params) : null);
  29. };
  30. if (typeof Object.prototype.extend === 'undefined') {
  31. Object.prototype.extend = function(src){
  32. if (src) {
  33. var obj = this;
  34. Object.keys(src).forEach(function(key) {
  35. obj[key] = src[key];
  36. });
  37. }
  38. };
  39. }
  40. // interval设置较长时,存在数据还未post而用户先关闭了浏览器的行为,采用html5 database先保存起来较合理
  41. var ss = global.sessionStorage, Storage = function(config) {
  42. var self = this;
  43. self.config = {db: "track_agent_db", table: "track_agent_log", flag: "track_agent_poster", debug: false}.extend(config);
  44. self.init();
  45. };
  46. Storage.prototype = {
  47. debug : function(l) {
  48. if (this.config.debug) {
  49. console.log(new Date(), l);
  50. }
  51. },
  52. init : function() {
  53. var self = this;
  54. self.getDB().transaction(function (tx) {
  55. // uid: 用户, event: 事件, desc: 行为描述, occur: 触发时间, referer: 页面,
  56. // args: 参数
  57. // 必须指定用户,一台设备可能多人在使用
  58. tx.executeSql("create table if not exists " + self.config.table + "(id text,uid text,event text,desc text,occur integer,referer text,args text)", []);
  59. });
  60. },
  61. getDB : function() {
  62. var self = this, db = self._db;
  63. !db && (self._db = global.openDatabase(self.config.db, "1.0", "track agent data", 1024 * 1024));
  64. return db;
  65. },
  66. push : function(log) {
  67. var self = this;
  68. self.getDB().transaction(function(tx){
  69. // 加uuid作为唯一标识,万一重复post了日志数据,服务器端也可避免重复保存
  70. tx.executeSql("insert into " + self.config.table + " values(?,?,?,?,?,?,?)", [uuid(), log.uid, log.event, log.desc, new Date().getTime(), global.location.href, log.args]);
  71. self.debug('push log {event: ' + log.event + ', desc: ' + log.desc + '}');
  72. });
  73. },
  74. pull : function(timestamp, callback) {
  75. var self = this;
  76. self.getDB().transaction(function (tx) {
  77. tx.executeSql("select * from " + self.config.table + " where occur<=?", [timestamp], function (ts, data) {
  78. if (data) {
  79. var logs = [];
  80. for (var i = 0; i < data.rows.length; i++) {
  81. logs.push(data.rows.item(i));
  82. }
  83. self.debug('pull logs before ' + timestamp + ', size ' + logs.length);
  84. callback.call(null, logs);
  85. }
  86. });
  87. });
  88. },
  89. del : function(timestamp) {
  90. var self = this;
  91. self.getDB().transaction(function (tx) {
  92. tx.executeSql("delete from " + self.config.table + " where<=?", [timestamp]);
  93. self.debug('delete logs before ' + timestamp);
  94. });
  95. },
  96. bind : function(agentId) {
  97. var self = this, flag = self.config.flag;
  98. // 控制整个应用只有一个页面启用post功能
  99. if (!ss.getItem(flag)) {
  100. ss.setItem(flag, agentId);
  101. return true;
  102. }
  103. },
  104. unbind : function(agentId) {
  105. var self = this, flag = self.config.flag;
  106. (agentId == ss.getItem(flag)) && (ss.removeItem(flag));
  107. }
  108. };
  109. var Agent = function(config){
  110. var self = this;
  111. // enableCollect: 允许搜集数据, enablePost: 允许发送数据, interval: 发送数据间隔(分钟),
  112. // transferUrl: 数据中转中心, storageConfig: storage参数
  113. self.config = {enabled: true, debug: false, interval: 15, transferUrl: '/track/transfer'}.extend(config);
  114. if (self.config.enabled) {
  115. self.uuid = uuid();
  116. self.storage = new Storage({debug: self.config.debug}.extend(self.config.storageConfig));
  117. self.enableCollecter();
  118. self.enablePoster();
  119. self.log({event: 'load'});
  120. // 页面关闭前
  121. global.onbeforeunload = function(){
  122. self.log({event: 'unload'});
  123. self.disablePoster();
  124. };
  125. }
  126. };
  127. Agent.prototype = {
  128. log : function(log) {
  129. this.storage.push(log);
  130. },
  131. enableCollecter : function() {
  132. // 指标+策略
  133. var self = this;
  134. doc.onclick = function() {
  135. };
  136. },
  137. enablePoster : function() {
  138. // 如果使用visibilitychange事件来切换poster,在某些操作下,存在数据始终不上传的可能
  139. var self = this, storage = self.storage;
  140. global.setInterval(function(){
  141. if (storage.bind(self.uuid)) {
  142. // 方便后面删除,防止误删
  143. var now = new Date().getTime() - 1;
  144. storage.pull(now, function(logs){
  145. Ajax({
  146. url : self.config.transferUrl,
  147. params : logs,
  148. success : function(){
  149. storage.del(now);
  150. }
  151. });
  152. });
  153. }
  154. }, 60000 * self.config.interval);
  155. },
  156. disablePoster : function() {
  157. var self = this;
  158. self.storage.unbind(self.uuid);
  159. }
  160. };
  161. global.TrackAgent = Agent;
  162. })(window, document);