前端监控学习笔记

现成的SDK

  • Sentry
  • Fun Debug

需要监控什么?

  • 错误统计
    记录我们代码发布到线上各种奇奇怪怪的错误

  • 行为日志埋点
    记录用户行为,比如:分析用户浏览时间比较长的页面有哪些,常常点击的有哪些,可以做 相应的推荐

  • PV/UV统计
    记录用户访问页面的次数
    PV:访问的操作的次数,UV:访问页面的用户多少

前端监控的主要流程:
在这里插入图片描述

功能拆分

前端监控学习笔记_第1张图片

错误监控

错误监控,即当代码发生错误时,比如,同步错误,异步错误,promise错误,资源加载错误时,我们需要捕获到错误,然后上报给后端。

上报到后端简单,发送请求即可。那如何捕获到错误,我们下面进行讨论:

错误类型

  1. 语法错误

    语法错误一般在可发阶段就可以发现,比如常见的单词拼写错误,中英文符号错误等。注意:语法错误是无法被try catch捕获的,因为在开发阶段就能发现,所以一般不会发布到线上环境。

    try {
      let name = 'heima; // 少一个单引号
      console.log(name);
    } catch (error) {
      console.log('----捕获到了语法错误-----');
    }
    
  2. 同步错误

    同步错误指的是在js同步执行过程中的错误,比如变量未定义,是可以被try catch给捕获到的

    try {
      const name = 'heima';
      console.log(nam);
    } catch (error) {
      console.log('------同步错误-------')
    }
    
  3. 异步错误

    异步错误指的是在setTimeout等函数中发生的错误,是无法被try catch捕获到的

    try {
      setTimeout(() => {
        undefined.map();
      }, 0);
    } catch (error) {
      console.log('-----异步错误-----')
    }
    

    异步错误的话我们可以用window.onerror来进行处理,这个方法比try catch要强大很多

    /**
     * @param {String}  msg    错误描述
     * @param {String}  url    报错文件
     * @param {Number}  row    行号
     * @param {Number}  col    列号
     * @param {Object}  error  错误Error对象
     */
     window.onerror = function (msg, url, row, col, error) {
       console.log('出错了!!!');
       console.log(msg);
       console.log(url);
       console.log(row);
       console.log(col);
       console.log(error);
    };
    
  4. promise错误

    promise 中使用 catch 可以捕获到异步的错误,但是如果没有写 catch 去捕获错误的话 window.onerror 也捕获不到的,所以写 promise 的时候最好要写上 catch ,或者可以在全局加上 unhandledrejection 的监听,用来监听没有被捕获的promise错误。

    window.addEventListener("unhandledrejection", function(error){
      console.log('捕获到异常:', error);
    }, true);
    
  5. 资源加载错误

    资源加载错误指的是比如一些资源文件获取失败,可能是服务器挂掉了等原因造成的,出现这种情况就比较严重了,所以需要能够及时的处理,网路错误一般用 window.addEventListener 来捕获。

    window.addEventListener('error', (error) => {
      console.log(error);
    }, true);
    

用户埋点统计

埋点就是需要记录用户的某些行为,比如点击按钮,我们需要记录用户点击了哪个按钮,然后进行上报。这样做的作用是,我们得到的数据可以进行一些分析。PS:淘宝首页分类,需要知道哪个分类点击次数最多,即哪个分类最火。

手动埋点

手动埋点就是手动的去出发上报函数

// 方式1
<button
  onClick={() => {
    // 业务代码
  	tracker('click', '用户去支付');
    // tracker('visit', '访问新页面');
    // tracker('submit', '提交表单');
  }}
>手动埋点</button>
// 方式2
<button 
	data-target="支付按钮"
	onClick={() => {
    // 业务代码
  }}
>手动上报</button>
  • 优点:可控性强,可以自定义上报具体的数据。
  • 缺点:对业务代码侵入性强,如果有很多地方需要埋点就得一个一个手动的去添加埋点代码。

无痕埋点

无痕埋点是为了解决手动埋点的缺点,实现一种不用侵入业务代码就能在应用中添加埋点监控的埋点方式。

<button onClick={() => {
  // 业务代码
}}>自动埋点</button>
// 自动埋点实现
function autoTracker () {
  // 添加全局click监听
  document.body.addEventListener('click', function (e) {
    const clickedDom = e.target;
    // 获取data-target属性值
    let target = clickedDom?.getAttribute('data-target');
    if (target) {
      // 如果设置data-target属性就上报对应的值--手动埋点
      tracker('click', target);
    } else {
      // 如果没有设置data-target属性就上报被点击元素的html路径
      const path = getPathTo(clickedDom);
      tracker('click', path);
    }
  }, false);
};
  • 优点:不用侵入务代码就能实现全局的埋点。
  • 缺点:只能上报基本的行为交互信息,无法上报自定义的数据;上报次数多,服务器性能压力大。

PV统计

  • history路由

    history路由是由window.histroy api来实现的:
    - istory.back(); // 返回上一页,和浏览器回退功能一样
    - history.forward(); // 前进一页,和浏览器前进功能一样
    - history.go(); // 跳转到历史记录中的某一页,
    - history.pushState(); // 添加新的历史记录
    - history.replaceState(); // 修改当前的记录项

/**
 * 重写pushState和replaceState方法
 * @param {*} name 
 * @returns 
 */
const createHistoryEvent = function (name) {
  // 拿到原来的处理方法
  const origin = window.history[name];
  return function(event) {
    if (name === 'replaceState') {
      const { current } = event;
      const pathName = location.pathname;
      if (current === pathName) {
        let res = origin.apply(this, arguments);
        return res;
      }
    }

    let res = origin.apply(this, arguments);
    let e = new Event(name);
    e.arguments = arguments;
    window.dispatchEvent(e);
    return res;
  };
};

window.history.pushState = createHistoryEvent('pushState');
window.history.replaceState = createHistoryEvent('replaceState');

function listener() {
  const stayTime = getStayTime(); // 停留时间
  const currentPage = window.location.href; // 页面路径
  lazyReport('visit', {
    stayTime,
    page: beforePage,
  })
  beforePage = currentPage;
}

// history.go()、history.back()、history.forward() 监听
window.addEventListener('popstate', function () {
  listener()
});

// history.pushState
window.addEventListener('pushState', function () {
  listener()
});

// history.replaceState
window.addEventListener('replaceState', function () {
  listener()
});	

history路由无法监听到pushState和replaceState,所以我们冲洗了一个方法,并用windows.dispatch创建了一个自定义监听事件。

  • hash路由
    url上hash的改变会出发 hashchange 的监听,所以我们只需要在全局加上一个监听函数,在监听函数中实现采集并上报就可以了。但是在react和vue中,对于hash路由的跳转并不是通过 hashchange 的监听实现的,而是通过 pushState 实现,所以,还需要加上对 pushState 的监听才可以。
export function hashPageTrackerReport() {
  let beforeTime = Date.now(); // 进入页面的时间
  let beforePage = ''; // 上一个页面
  
  // 上报
  function listener() {
    const stayTime = getStayTime();
    const currentPage = window.location.href;
      lazyReport('visit', {
      stayTime,
      page: beforePage,
    })
    beforePage = currentPage;
  }

  // hash路由监听
  window.addEventListener('hashchange', function () {
    listener()
  });
}

UV统计

在SDK初始化的时候,上报即可

UV统计的是一天内访问该网站的用户数

/**
 * 初始化配置
 * @param {*} options 
 */
function init(options) {
  ... // 加载配置
  report('user', '加载应用'); // uv统计
}

合并上报

对于无痕埋点来说,一次点击就进行一次上报,会对服务器造成很大的压力,所以我们需要合并一下请求

// cache.js
const cache = [];

export function getCache() {
  return cache;
}

export function addCache(data) {
  cache.push(data);
}

// lazyReport.js
export function lazyReport(type, params) {
  // ....
  const data = getCache();

  if (delay === 0) { // delay=0相当于不做延迟上报
    report(data);
    return;
  }

  if (data.length > 10) { // 数据达到10条上报
    report(data);
    clearTimeout(timer);
    return;
  }

  clearTimeout(timer);
  timer = setTimeout(() => { // 合并上报
    report(data);
  }, delay);
}

你可能感兴趣的:(探索未知的旅程,前端,学习,笔记)