Skip to content
利用window.onerror完成脚本错误收集,方便进行上报,以便排查未知的客户端问题。 参考自AlloyLever,在它的基础上有简化和调整使用方式。错误上报服务也可以使用商业方案:Sentry和Bugsnag。
JavaScript
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
dist
src
.babelrc
.gitignore
.npmignore
README.md
package.json
rollup.config.js

README.md

npm npm npm

error-reporter

利用window.onerror完成脚本错误收集,方便进行上报,以便排查未知的客户端问题。 参考自AlloyLever,在它的基础上有新增上报场景和调整使用方式。错误上报服务也可以使用商业方案:Sentry和Bugsnag。

这个库提供的核心服务:

  • 动态激活vConsole
  • 收集客户端脚本运行错误
  • 收集script|img|link标签加载资源时的加载错误
  • 收集使用vue时的运行错误
  • 收集使用vue-router时的运行错误
  • 收集使用axios时的请求和响应错误
  • 上报自定义错误

其它场景的错误收集服务,在后续版本会陆续发布。

  • react相关
  • 未捕获的Promise
  • fetch或xhr过程中的错误

使用方式

先介绍下error-reporter的使用方式。

安装:

npm install breif-error-reporter --save

使用:

import ErrorReporter from 'breif-error-reporter'

/**
 * setConfig(options)
 *  onReport {Function} 收集到错误之后的回调函数,在此进行错误上报
 */
// 错误上报
ErrorReporter.setConfig({
    onReport (message, reportType) {
        // message是可以进行上报的包含错误信息的字符串
        // reportType是一个描述上报场景的字符串
    }
})


// 调用enableVConsole动态加载vConsole并实例化,如果show为true则在实例化完成后立即展示vConsole
let show = false
ErrorReporter.enableVConsole(show)

如果你想在某个url参数下自动唤起vConsole,可以采用以下类似的做法:

if(window.location.href.indexOf('vconsole') > -1) {
    ErrorReporter.enableVConsole()
}

当生产环境有用户反馈问题,不好排查原因时,可将带有vconsole参数的链接发给用户,让用户协助截图或录屏的方式,帮忙反馈vConsole收集的日志。

这个库并没有把url参数唤起的方式,自动加入到库的实现里面去,只提供了动态唤起vConsole的方法,每个项目怎么用,由项目自己决定。

VS. AlloyLever

  • 本库不提供url参数唤起vConsole,以及entry机关唤起vConsole,只提供动态唤起vConsole的方法,每个项目可自行决定如何唤起。
  • 本库不默认提供上报的逻辑,由每个项目自己通过回调函数决定如何上报。 AlloyLever内部是通过new Image().src的方式来进行上报的,我个人更喜欢用ajax来处理,比new Image().src灵活。

详细使用说明

setConfig(options)

这是进行初始化的一个方法,各个option的用法如下:

  • vConsoleSrc

    @{String|default: '//cdn.bootcss.com/vConsole/3.3.4/vconsole.min.js'}

    这个option指定vConsole的文件地址,当启用vConsole时,会基于这个地址动态加载vConsole源文件。

  • maximumStackLines

    @{Number|default: 20} 这个option用在收集运行时错误的场景中。通常js运行错误,都有比较长的堆栈信息,这个option限制上报时最多收集多少行堆栈信息。

  • resource

    @{Boolean|default: true}

    是否开启收集资源加载失败的错误。默认为true,开启后,script|img|link资源加载失败,会被收集到。

  • vue

    @{Class|default: null} 这个option默认为null,传入Vue之后,就会通过Vue的异常处理机制收集Vue的运行错误。

  • vueRouter

    @{VueRouter|default: null}

    这个option默认为null,可传入vue-router的实例。传入后,就会通过vue-router的异常处理机制,收集vue-router的运行错误。

  • axios

    @{axios|default: null}

    这个option默认为null,可传入axios引用。传入后,通过配置axiosrequest responce的拦截器,收集相关错误。

  • axiosReportConfig

    @{Array|default: ['url', 'method', 'params', 'data', 'headers']}

    配置此数组,可以在捕获到axios的错误时,从它的config对象内提取指定信息。 默认提取的信息数组为:'url', 'method', 'params', 'data', 'headers',这些数据被提取出来后,会作为onReport的第三个参数传入,方便一同上报至后台。

  • axiosIgnore

    @{Array,Function| default: null}

    这个option可以是一个数组,也可以是一个函数。当它是一个数组时,可通过数组指定正则表达式,只要这个数组内存在某个正则,与config.url匹配,则在此config请求失败时,不会进行上报。

    或者是配置为一个函数,会被传入捕获到的axioserror对象,当这个函数返回trusy值时,则不会进行此错误的上报。

  • notReportErrors

    @{Array|default: []}

    这个option可以指定为一个数组,它的元素应该是Error类的子类,当error-reporter捕获的任何错误,只要是这个数组中某一个元素的实例(基于instanceof运算),则不进行该错误的上报。

  • processStack

    @{Function|default:[inner function]

    这个option用来对Error实例的stack进行解析,在此库的内部有使用以下这个函数进行解析,如果不想要或者想更换,则可利用此option处理。

    默认的处理函数如下:

    function processStackMsg (error) {
        // 1. clean line separator
        // 2. replace 'at' with '@' in error source like 'at window.makeError (http://localhost:8080/test.js?a=1&b=2:3:5)'
        // 3. limit maximum stack lines
        // 4. clean query string in error source like 'http://localhost:8080/test.js?a=1&b=2'
    
        let stack = error.stack
            .replace(/\n/gi, '') // 1
            .split(/\bat\b/) // 2
            .slice(0, config.maximumStackLines) // 3
            .join('@')
            .replace(/\?[^:]+/gi, '') // 4
        let msg = error.toString()
        if (stack.indexOf(msg) < 0) {
            stack = msg + '@' + stack
        }
        return stack
    }    
  • onReport

    @{Function|default: noop}

    这是收集到错误以后的回调函数,不管是哪个场景的错误,都会进入这个回调。

    它有三个参数:

    • message 错误信息串
    • reportType 错误场景描述的字符串
    • extraData 某些上报场景可能会包含额外的数据,如axios
  • onResourceLoadError

    @{Function|default: noop}

    这是单独给资源加载失败时提供的额外的回调函数。考虑到资源加载失败是比较严重的错误,而且通常刷新一下页面可能就能恢复正常使用,所以需要一个类似这样的回调函数,来进行相关的提示和刷新操作。

特殊地: 在此库内部,会用到一个自定义的Error实例的属性:notToReport,只要这个属性是一个trusy值,则在捕获到这个错误时,不会进行上报。

所以除了notReportErrorsoption,另外一个屏蔽某些错误不进行上报的方式,可以参考下面的做法:

setTimeout(()=>{
    let e = new Error('sth happened')
    e.notToReport = true
    throw e // 这个错误不会进行上报
})

reportType

内部有定义常量来描述上报场景:

const REPORT_TYPE = {
    RUNTIME: 'runtime',
    RESOURCE: 'resource',
    VUE: 'vue',
    VUE_ROUTER: 'vue-router',
    MANUAL: 'manual',
    AXIOS_REQUEST: 'axios-request',
    AXIOS_RESPONSE: 'axios-response',
    UNHANDLED_REJECTION: 'unhandledrejection'
}

将来支持的场景越多,这个地方还会增加。 另外在后面介绍的api方法中,有一个makeReport方法,它可以传入自定义的reportType

function makeReport (err, reportType, extraData = {}) {
    let error = err
    if (isObjectType(err, 'String')) {
        error = new Error(err)
    }
    if (notToReport(error)) return
    return config.onReport.call(ErrorReporter, config.processStack(error), reportType || REPORT_TYPE.MANUAL, extraData)
}

后端收集到错误,可根据reportType做相关统计和分类查询。

enableVConsole

这个方法用来激活vConsole,它有一个参数:

  • show @{Boolean|Default: undefined} 指定为true时,将在激活后立即显示vConsole面板

getSystemInfo

这个方法可用来收集客户端信息,目前可收集的信息如下:

{
    system,
    systemVersion,
    netType,
    ua,
    wechat,
    wechatVersion,
    wechatMini
}

getCookie

这是一个工具方法,用来从document.cookie中查询指定的cookie值。 有一个参数:

  • name @{String} 要查询的cookie的名字

makeReport

这个方法,可用来自定义错误上报,接收两个参数

  • err @{String|Error} 可以是字符串或Error实例
  • reportType @{String}

wrapNotReport

这个方法,用来包裹一个Error对象,添加notToReport属性,阻止它被上报。

更加完整的示例

import Vue from 'vue'
import ErrorReporter from 'breif-error-reporter'
import axios from 'axios'
import router from './router'

// 错误上报
ErrorReporter.setConfig({
    vue: Vue,
    vueRouter: router,
    axios: axios,
    axiosIgnore: [
        /web-error-report/
    ],
    onResourceLoadError (event) {
        if (event.target && event.target instanceof HTMLScriptElement) {
            if (event.target.src.indexOf('static/js') > -1) {
                location.reload()
            }
        }
    },
    onReport (message, reportType, extraData) {
        let accessToken = localStorage.accessToken || ''
        let clientId = localStorage.client_id || ''

        let systemInfo = this.getSystemInfo()
        let href = location.href
        let path = location.pathname
        let search = location.search ? location.search.substring(1) : ''
        let cookieClientId = this.getCookie('client_id')
        let cookieAccessToken = this.getCookie('accessToken')
        let cookie = []
        cookieClientId && cookie.push(cookieClientId)
        cookieAccessToken && cookie.push(cookieAccessToken)
        cookie = cookie.join('; ')

        let reportData = {
            ...systemInfo,
            accessToken,
            clientId,
            href,
            path,
            search,
            cookie,
            message,
            reportType,
            extraData: JSON.stringify(extraData)
        }

        // post reportData ...
    }
})

运行时错误的跨域问题

ErrorReporter利用window.onerror收集运行时错误,有一个跨域问题需要注意: 如果错误是由跨域的脚本产生的,比如某网站a.com下有个页面有如下js引用:

<script src="http://b.com/some.js"></script>

some.js运行时发生错误,通过ErrorReporter收集到出错信息时,默认只能收到Script error.这样一个字符串,并且window.onerror回调的最后一个很重要的参数error也是null,这是浏览器因为安全策略故意做的,防止信息泄露。这样的错误信息是没有意义的。

如果想收集不同域的脚本报出的完整错误信息,必须做到两点:

  • script标签添加crossorigin属性,如<script crossorigin>
  • 脚本域名必须做CORS的处理,将Access-Control-Allow-Origin这个http header配置好

实现思路

请阅读这篇文章

You can’t perform that action at this time.