美文网首页
前端error上报

前端error上报

作者: Yinzhishan | 来源:发表于2022-08-17 16:19 被阅读0次

前言

一套健壮的系统,肯定少不了维护和监控,前端因为是toc的,所以很多错误,我们无法直接看到,所以我们就需要一套前端的error收集系统;

一、前端收集上报

首先准备要用到的公共方法;
新建一个JsMonitor.js文件

// urlConcat
const urlConcat = data => {
    let url = '';
    for (let k in data) {
        let value = data[k] !== undefined ? data[k] : '';
        url += '&' + k + '=' + encodeURIComponent(value);
    }
    return url ? url.substring(1) : '';
};

/**
 * debounce 节流函数
 * @param {Function} func 实际要执行的函数
 * @param {Number} delay 延迟时间,单位是 ms
 * @param {Function} callback 在 func 执行后的回调
 *
 * @return {Function}
 */
function debounce(func, delay, callback) {
    let timer;

    return () => {
        let that = this;
        let args = arguments;

        clearTimeout(timer);

        timer = setTimeout(() => {
            func.apply(that, args);
            !callback || callback();
        }, delay);
    };
}
// 定义Monitor类
let Monitor = {};
// 初始化
function __init() {}
function __config(opts) {}
// 入口函数
Monitor.init = opts => {
    __config(opts, config);
    __init();
};

Monitor.handleError = handleError;

export default Monitor;

接下来,要定义我们的error数据需要如何上报

// 配置项
let config = {
    concat: true,
    delay: 0, // ETC 错误处理间隔时间
    maxError: 16, // ETC 异常报错数量限制
    sampling: 1, // ETC 采样率
    errorSite: '', // ETC 平台类型
    devLogURL: 'http://jserror.dev.com/ts.html?',
    logURL: 'https://jserror.com/ts.html?',
    isDev: process.env.NODE_ENV === 'development'
};

// 定义Monitor类
let Monitor = {};
// 错误码
let ERROR_CONSOLE = 'console error',
    ERROR_RUNTIME = 'runtime error',
    ERROR_SCRIPT = 'script error',
    ERROR_STYLE = 'style error',
    ERROR_IMAGE = 'image error',
    ERROR_AUDIO = 'audio error',
    ERROR_VIDEO = 'video error';

// 错误类型
let LOAD_ERROR_TYPE = {
    SCRIPT: ERROR_SCRIPT,
    LINK: ERROR_STYLE,
    IMG: ERROR_IMAGE,
    AUDIO: ERROR_AUDIO,
    VIDEO: ERROR_VIDEO
};

// 忽略错误监听
let ignoreError = false;
// 错误日志列表
let errorList = [];
// 错误处理回调
let report;

····省略

完整代码

// urlConcat
const urlConcat = data => {
    let url = '';
    for (let k in data) {
        let value = data[k] !== undefined ? data[k] : '';
        url += '&' + k + '=' + encodeURIComponent(value);
    }
    return url ? url.substring(1) : '';
};
// 配置项
let config = {
    concat: true,
    delay: 0, // ETC 错误处理间隔时间
    maxError: 16, // ETC 异常报错数量限制
    sampling: 1, // ETC 采样率
    errorSite: '', // ETC 平台类型
    devLogURL: 'http://jserror.dev.innonly.com/ts.html?',
    logURL: 'https://jserror.innonly.com/ts.html?',
    isDev: process.env.NODE_ENV === 'development'
};

// 定义Monitor类
let Monitor = {};
// 错误码
let ERROR_CONSOLE = 'console error',
    ERROR_RUNTIME = 'runtime error',
    ERROR_SCRIPT = 'script error',
    ERROR_STYLE = 'style error',
    ERROR_IMAGE = 'image error',
    ERROR_AUDIO = 'audio error',
    ERROR_VIDEO = 'video error';

// 错误类型
let LOAD_ERROR_TYPE = {
    SCRIPT: ERROR_SCRIPT,
    LINK: ERROR_STYLE,
    IMG: ERROR_IMAGE,
    AUDIO: ERROR_AUDIO,
    VIDEO: ERROR_VIDEO
};

// 忽略错误监听
let ignoreError = false;
// 错误日志列表
let errorList = [];
// 错误处理回调
let report;

/**
 * 设置一个采样率,决定是否上报
 * @param  {Number} sampling 0 - 1
 * @return {Boolean}
 */
function needReport(sampling) {
    return Math.random() < (sampling || 1);
}

/**
 * 往异常信息数组里面添加一条记录
 * @param  {Object} errorLog 错误日志
 */
function pushError(errorLog) {
    if (needReport(config.sampling) && errorList.length < config.maxError) {
        errorList.push(errorLog);
    }
}

/**
 * 生成 runtime 错误日志
 * @param  {String} message 错误信息
 * @param  {String} source  发生错误的脚本URL
 * @param  {Number} lineno  发生错误的行号
 * @param  {Number} colno   发生错误的列号
 * @param  {Object} error   error对象
 * @return {Object}
 */
function formatRuntimerError(message, source, lineno, colno, error) {
    return {
        type: ERROR_RUNTIME,
        desc: message + ' at ' + source + ':' + lineno + ':' + colno,
        stack: error && error.stack ? error.stack.substring(0, 300) : 'no stack' // ETC IE <9, has no error stack
    };
}

/**
 * 生成 laod 错误日志
 * @param  {Object} errorTarget
 * @return {Object}
 */
function formatLoadError(errorTarget) {
    return {
        type: LOAD_ERROR_TYPE[errorTarget.nodeName.toUpperCase()],
        desc: errorTarget.baseURI + '@' + (errorTarget.src || errorTarget.href),
        stack: 'no stack'
    };
}

/**
 * 发送错误日志
 * @param {Object} params
 */
function logReport(params) {
    let baseInfo = {
        tp: 'jserror',
        site: config.errorSite,
        url: global.location.href,
        version: '',
        os: window.navigator.userAgent
    };
    let [url, msg] = ['', ''];

    if (typeof params === 'string') {
        msg = {
            desc: params
        };
    }
    if (typeof params === 'object') {
        msg = params;
    }
    let errorinfo = urlConcat(Object.assign(baseInfo, msg));

    // 创建script发送
    let domScript = document.createElement('script');
    if (config.isDev) {
        url = config.devLogURL + errorinfo;
    } else {
        url = config.logURL + errorinfo;
    }

    domScript.src = url;
    domScript.onload = () => {
        domScript && domScript.remove && domScript.remove();
        domScript && domScript.removeNode && domScript.removeNode();
    };
    let head = document.head || document.documentElement;
    head.appendChild(domScript);
    return true;
}

/**
 * 错误数据预处理
 * @param {Object} errorLog 错误日志
 */
function handleError(errorLog) {
    // 是否延时处理
    if (!config.delay) {
        !needReport(config.sampling) || logReport(errorLog);
    } else {
        pushError(errorLog);
        report(errorList);
    }
}

/**
 * debounce 节流函数
 * @param {Function} func 实际要执行的函数
 * @param {Number} delay 延迟时间,单位是 ms
 * @param {Function} callback 在 func 执行后的回调
 *
 * @return {Function}
 */
function debounce(func, delay, callback) {
    let timer;

    return () => {
        let that = this;
        let args = arguments;

        clearTimeout(timer);

        timer = setTimeout(() => {
            func.apply(that, args);
            !callback || callback();
        }, delay);
    };
}

function __config(opts) {
    // merge配置
    Object.assign(config, opts);

    report = debounce(logReport, config.delay, () => {
        errorList = [];
    });
}

// 初始化
function __init() {
    // 监听 Javascript 报错
    window.onerror = (...arg) => {
        if (ignoreError) {
            ignoreError = false;
            return;
        }
        handleError(formatRuntimerError.apply(null, arg));
    };

    // 针对vue的console.error 劫持
    console.error = (origin => {
        return info => {
            let errorLog = {
                type: ERROR_CONSOLE,
                desc: info,
                stack: 'no stack'
            };
            handleError(errorLog);
            origin.call(console, info);
        };
    })(console.error);
}

// 入口函数
Monitor.init = opts => {
    __config(opts, config);
    __init();
};

Monitor.handleError = handleError;

export default Monitor;

相同的,如果我们有自定义埋点的需求,也可以通过这种方式实现。

二、直接使用sentry框架

https://sentry.io/
什么都有

image.png
image.png

相关文章

  • 前端error上报

    前言 一套健壮的系统,肯定少不了维护和监控,前端因为是toc的,所以很多错误,我们无法直接看到,所以我们就需要一套...

  • 如何确定数据埋点的准确性

    一、常见的数据上报类型 从事件上报的触发逻辑层面上看,数据上报类型可分为:前端触发上报、前端获取后端结果...

  • syntax error, unexpected '[' --

    缘起 本地开发时代码运行正常, 放到测试服务器上报错: Parse error: syntax error, un...

  • InstallStaging:Error staging apk

    在Android 8.0 + 操作系统上报异常: InstallStaging:Error staging apk...

  • 数据埋点之四:埋点事件触发类型

    前端触发上报用户在前端进行相应的操作时,即触发采集数据事件。 前端获取后端结果上报这种方式,一般同由于除了记录用户...

  • appium环境配置的坑--总结

    一、appium安卓7.0以上报错:Original error: Command failed: ps: uia...

  • 上报错误/异常到xmail邮箱

    1. 使用 Log:error($message, $context) 打印的日志,会上报到 xmail 邮箱 如...

  • 前端数据上报 sendBeacon

    发请求上报 问题:在页面卸载或者刷新的时候上报,请求会在浏览器关闭或者重新加载的时候被cancel掉,导致上报失败...

  • 前端埋点上报

    本文所说的埋点上报,只包含两种:点击上报(click)、曝光上报(show)。 整体思路: 点击上报: 使用 wi...

  • nuxt.js遇见的问题

    在开发环境没问题,在正式环境上报错 [nuxt] Error while mounting app: Hierar...

网友评论

      本文标题:前端error上报

      本文链接:https://www.haomeiwen.com/subject/iiwjgrtx.html