前端性能和错误监控

本地缓存在localStorage中的监控对象

export default function monitorInit(timeout = 7 * 24 * 60 * 60) {
  let whiteScreen = new Date() - performance.timing.navigationStart;
  const monitor = {
    // 数据上传地址
    // url: '',
    // 性能信息
    performance: {},
    // 资源信息
    resources: {},
    // 错误信息
    errors: {},

    // 用户信息
    client: {
      // 屏幕宽度
      screen: screen.width,
      // 屏幕高度
      height: screen.height,
      // 浏览器平台
      platform: navigator.platform,
      // 浏览器的用户代理信息
      userAgent: navigator.userAgent,
      // 浏览器用户界面的语言
      language: navigator.language
    },
    cacheTime: new Date().getTime(),
    timeout: timeout
    // 手动添加错误
    // addError(error) {
    //   const obj = {};
    //   const { type, msg, url, row, col } = error;
    //   if (type) obj.type = type;
    //   if (msg) obj.msg = msg;
    //   if (url) obj.url = url;
    //   if (row) obj.row = row;
    //   if (col) obj.col = col;
    //   obj.time = new Date().getTime();
    //   monitor.errors.push(obj);
    // },
    // 重置 monitor 对象
    // reset() {
    //   window.performance && window.performance.clearResourceTimings();
    //   monitor.performance = getPerformance();
    //   monitor.resources = getResources();
    //   monitor.errors = {};
    // },
    // 清空 error 信息
    // clearError() {
    //   monitor.errors = {};
    // },
    // 上传监控数据
    // upload() {
    // 自定义上传
    // axios.post({
    //     url: monitor.url,
    //     data: {
    //         performance,
    //         resources,
    //         errors,
    //         user,
    //     }
    // })
    // },
    // 设置数据上传地址
    // setURL(url) {
    //   monitor.url = url;
    // }
  };

  const getMonitor = () => {
    isOverTime();
    return JSON.parse(localStorage.getItem('monitor'));
  };

  const isOverTime = () => {
    const data = JSON.parse(localStorage.getItem('monitor'));

    // 没有数据 一定超时
    if (!data) {
      return true;
    }
    // 获取系统当前时间戳
    const currentTime = new Date().getTime();
    // 获取当前时间与存储时间的过去的秒数
    const overTime = (currentTime - data.cacheTime) / 1000;
    // console.log(overTime);
    // 如果过去的秒数大于当前的超时时间,也返回null让其去服务端取数据
    if (overTime > data.timeout) {
      localStorage.removeItem('monitor');
      return true;
    }
    return false;
  };
  // 获取性能信息
  const getPerformance = () => {
    if (!window.performance) return;
    const timing = window.performance.timing;
    const performance = {
      // 重定向耗时
      redirect: timing.redirectEnd - timing.redirectStart,
      // 白屏时间
      whiteScreen: whiteScreen,
      // DOM 渲染耗时
      dom: timing.domComplete - timing.domLoading,
      // 页面加载耗时
      load: timing.loadEventEnd - timing.navigationStart,
      // 页面卸载耗时
      unload: timing.unloadEventEnd - timing.unloadEventStart,
      // 请求耗时
      request: timing.responseEnd - timing.requestStart,
      // 获取性能信息时当前时间
      time: new Date().getTime()
    };

    return performance;
  };

  // 获取资源信息
  const getResources = () => {
    if (!window.performance) return;
    const data = window.performance.getEntriesByType('resource');
    const resource = {
      xmlhttprequest: [],
      css: [],
      other: [],
      script: [],
      img: [],
      link: [],
      fetch: [],
      // 获取资源信息时当前时间
      time: new Date().getTime()
    };

    data.forEach(item => {
      const arry = resource[item.initiatorType];
      arry &&
        arry.push({
          // 资源的名称
          name: item.name,
          // 资源加载耗时
          duration: item.duration.toFixed(2),
          // 资源大小
          size: item.transferSize,
          // 资源所用协议
          protocol: item.nextHopProtocol
        });
    });

    return resource;
  };

  window.onload = () => {
    // 在浏览器空闲时间获取性能及资源信息 https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback
    let monitorObj = getMonitor() ? getMonitor() : monitor;
    if (window.requestIdleCallback) {
      window.requestIdleCallback(() => {
        monitorObj.performance = getPerformance();
        // console.log('页面性能信息');
        // console.log(monitor.performance);
        monitorObj.resources = getResources();
        // console.log('页面资源信息');
        // console.log(monitor.resources);
        localStorage.setItem('monitor', JSON.stringify(monitorObj));
      });
    } else {
      setTimeout(() => {
        monitor.performance = getPerformance();
        // console.log('页面性能信息');
        // console.log(monitor.performance);
        monitor.resources = getResources();
        // console.log('页面资源信息');
        // console.log(monitor.resources);
        localStorage.setItem('monitor', JSON.stringify(monitor));
      }, 0);
    }
  };

  // 捕获资源加载失败错误 js css img...
  addEventListener(
    'error',
    e => {
      const target = e.target;

      if (target !== window) {
        let monitorObj = getMonitor() ? getMonitor() : monitor;
        let url = target.src || target.href;
        if (!monitorObj.errors[url]) {
          monitorObj.errors[url] = {
            type: target.localName,
            url: url,
            msg: (target.src || target.href) + ' is load error',
            // 错误发生的时间
            time: new Date().getTime()
          };
          localStorage.setItem('monitor', JSON.stringify(monitorObj));
          // console.log('所有的错误信息');
          // console.log(monitor.errors);
        }
      }
    },
    true
  );

  // 监听 js 错误
  window.onerror = function(msg, url, row, col, error) {
    let monitorObj = getMonitor() ? getMonitor() : monitor;
    let key = `${url}:${row}`;
    if (!monitorObj.errors[key]) {
      monitorObj.errors[key] = {
        type: 'javascript', // 错误类型
        row: row, // 发生错误时的代码行数
        col: col, // 发生错误时的代码列数
        msg: error && error.stack ? error.stack : msg, // 错误信息
        url: url, // 错误文件
        time: new Date().getTime() // 错误发生的时间
        // console.log('所有的错误信息');
        // console.log(monitor.errors);
      };
      localStorage.setItem('monitor', JSON.stringify(monitorObj));
    }
  };

  // 监听 promise 错误 缺点是获取不到行数数据
  addEventListener('unhandledrejection', e => {
    let monitorObj = getMonitor() ? getMonitor() : monitor;
    let msg = (e.reason && e.reason.msg) || e.reason || '';
    if (!monitorObj.errors[msg]) {
      monitorObj.errors[msg] = {
        type: 'promise',
        msg: msg,
        // 错误发生的时间
        time: new Date().getTime()
      };
      // console.log('所有的错误信息');
      // console.log(monitor.errors);
      localStorage.setItem('monitor', JSON.stringify(monitorObj));
    }
  });

  return monitor;
}

 

前端性能和错误监控

前端异常监控解决方案研究

原文地址:https://www.cnblogs.com/ziyoublog/p/14764259.html