日志收集与上传

出于运营的需要,我们需要将页面的流转信息,用户点击分布,错误,页面渲染情况发送到后端

小程序编译阶段,会将所有事件转换为一个全局的dispatchEvent方法,因此我们可以这里做统一的日志的收集

  1. <div onclick="dispatchEvent" data-beacon-uid="clickForVibration" class="item" data-click-uid="e1765" data-class-uid="c1321">
  2. <text>震动</text>
  3. </div>

如果我们发现这事件类型是click/tap/change/blur, 我们就会为这些元素添加一个data-beacon-uid, 值为default,(如果你已经写了,它就不会添加),然后在dispatchEvent执行app.js的全局对象的onCollectLogs方法,让用户整理成一个对象,放到一个数组中, 并尝试使用onReportLogs自动发送;

  1. //dispatchEvent的源码
  2. export function dispatchEvent(e) {
  3. const instance = this.reactInstance;
  4. if (!instance || !instance.$$eventCached) {
  5. return;
  6. }
  7. const eventType = toLowerCase(e.type);
  8. const app = _getApp();
  9. const target = e.currentTarget;
  10. const dataset = target.dataset || {};
  11. const eventUid = dataset[eventType + 'Uid'];
  12. const fiber = instance.$$eventCached[eventUid + 'Fiber'] || {
  13. props: {},
  14. type: 'unknown'
  15. };
  16. if (app && app.onCollectLogs && /click|tap/.test(eventType) ) {
  17. app.onCollectLogss(dataset, eventType, fiber && fiber.stateNode);
  18. }
  19. //....略
  20. }

当用户退出APP时,会进入onHide事件,这时我们就会上传剩余的所有日志

因此用户只需要在app.js定义好这两个事件,框架帮你搞定日志上传。下面是示例:

  1. import React from '@react';
  2. import './pages/index/index';
  3. import './pages/demo/base/index';
  4. import './pages/demo/native/index/index';
  5. import './app.scss';
  6. function computeXpath(node){ //通过xpath实现自动埋点
  7. var xpath = [];
  8. while (node.parentNode){
  9. var index = node.parentNode.children.indexOf(node);
  10. var tag = node.type == 'div' ? 'view': node.type;
  11. xpath.unshift(tag+'['+index+']');
  12. node = node.parentNode;
  13. }
  14. return '/page/'+ xpath.join('/');
  15. }
  16. function computeCompressedXpath(node){ //压缩后的xpath
  17. var xpath = [];
  18. while (node.parentNode){
  19. var index = node.parentNode.children.indexOf(node);
  20. xpath.unshift(index);
  21. node = node.parentNode;
  22. }
  23. return xpath.join('/');
  24. }
  25. var openChange = false;
  26. class Global extends React.Component {
  27. static config = {
  28. window: {
  29. backgroundTextStyle: 'light',
  30. navigationBarBackgroundColor: '#0088a4',
  31. navigationBarTitleText: 'mpreact',
  32. navigationBarTextStyle: '#fff'
  33. }
  34. };
  35. // 全局数据
  36. globalData = {
  37. ufo: 'ufo'
  38. };
  39. onCollectLogs(dataset, eventType, node){ //这里会在框架的dispatchEvent自动调起,实现自动理点
  40. var beaconId = dataset.beaconUid;
  41. if( beaconId == 'default' && node ){
  42. beaconId = computeCompressedXpath(node);
  43. }
  44. if (eventType === 'input') {//input事件触发太频繁了,我们只想收集一次
  45. if (openChange) return;
  46. openChange = true;
  47. setTimeout(() => {
  48. openChange = false;
  49. }, 1000);
  50. }
  51. var otherData = dataset.xxx//data-xxxx
  52. var otherData2 = dataset.xxx2;
  53. var timeStamp = new Date - 0;
  54. var path = React.getCurrentPage().props.path;//页面路径
  55. var logs = this.globalData.logs || (this.globalData.logs = [])
  56. logs.push({
  57. pid: beaconId,
  58. path: path,
  59. timeStamp: timeStamp
  60. action: eventType
  61. });
  62. if(logs.length > 20){
  63. var uploadLogs = logs.splice(0, 10);//截取前十条;
  64. if(this.onReportLogs){
  65. this.onReportLogs(uploadLogs)
  66. }
  67. }
  68. };
  69. onHide(){
  70. this.onReportLogs(); //微信,支付宝,百度
  71. };
  72. onDistory(){
  73. this.onReportLogs(); //快应用
  74. };
  75. onReportLogs(logs){ //自己实现
  76. if(!logs){
  77. var existLogs = this.globalData.logs
  78. if(!Array.isArray(existLogs) || existLogs.length == 0){
  79. return
  80. }
  81. logs = existLogs;
  82. this.globalData.logs = [];
  83. }
  84. if(!logs.length){
  85. return
  86. }
  87. var buildType = this.globalData.buildType;// wx, bu, ali
  88. var info = this.globalData.systemInfo | React.api.getSystemInfornSync();
  89. var { brand, model, version, platform} = info ;//获取手机品牌,手机型号, 微信版本号, 客户端平台;
  90. React.api.request({
  91. url: "/fdsfdsf/sdfds",
  92. type: 'GET',
  93. data {
  94. logs, //logData
  95. buildType,//wx, bu, ali, quick, tt, qq
  96. brand, //commonData
  97. model, //commonData
  98. version,//commonData
  99. platform,//commonData
  100. //other
  101. }
  102. })
  103. },
  104. onLaunch() {
  105. console.log('App launched');
  106. }
  107. }
  108. export default App(new Global());

在common目录下

  1. import React from '@react'
  2. //此方法用于手动埋点
  3. function createLog(dataset, eventType){
  4. var app = React.getApp();
  5. if(typeof app.onCollectLogs === 'function' ){
  6. app.onCollectLogs(dataset, eventType, null)
  7. }
  8. }

各种日志的处理

  • 订单 等这样重要的行为, 要业务中进行手动埋点,使用上面的createLog方法
  • 点击,输入这样的事件,使用自动埋点,框架会在内部的dispatchEvent方法中自行调用全局的onCollectLogs方法
  • 页面流转情况, 建议对React.api.redirectTo等四个方法进行再包装,里面封入onCollectLogs方法,
  • 页面渲染时间,通过全局的onGlobalLoad, onGlobalReady等到某一页的渲染时间
  • 页面停留时间,通过全局的onGlobalShow onGlobalHide等到某一页的停留时间