Skip to content

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

Notifications You must be signed in to change notification settings

liuyunzhuge/error-reporter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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配置好

实现思路

请阅读这篇文章

About

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

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published