最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 前端异常上报

    正文概述 掘金(XXHolic)   2020-12-21   493

    引子

    在前端异常解析:Source Map 中讨论了 Source Map 的使用,接着看看异常上报的方式。

    • Origin
    • My GitHub

    影响因素

    异常上报,可能产生影响的因素有:

    1. 上报的频率。当出现死循环,不断触发异常上报时,这个就跟 DDOS 攻击差不多了。
    2. 上报的数据量。不同的请求方式,能携带的数据量有限制。如果想要录制用户的操作,那么产生的数据量,不同情况下会不一样。
    3. 跨域。日志服务器有些是单独的。
    4. 不同 web 服务器对于请求的 body 大小限制不同,见 Can HTTP POST be limitless? 。
    5. 上报请求的响应方式。响应同样会消耗资源。

    以上是一些比较容易想到的因素,在实际的情况中,根据不同监控的需要,可能会出现其它的影响因素。

    上报方式

    图片 src

    优点:

    • 不受跨域限制。
    • 可以不用响应。

    不足:

    • src 值为 URL 时的长度有限制。在 mdn img 中说 src 值为 URL 时,跟网页地址是一样的,查了下资料,长度的限制可参考 What is the maximum length of a URL in different browsers? 。
    • 可能与其它优先级更高的网络请求竞争资源。

    XHR/Fetch

    优点:

    • POST 的方式可以传送更多的数据,理论上没有限制请求 body 的大小,但不同的服务器,接受的请求的 body 大小可能有限制。

    不足:

    • 受跨域限制。
    • 可能与其它优先级更高的网络请求竞争资源。

    Beacon

    Beacon(信标)接口用于安排异步和非阻塞数据传输,从而能最大限度地减少与其它关键型操作的资源争用,同时还能将请求发送到目的地。特点有:

    • 信标请求使用的 POST 方式而且不需要响应。
    • 信标请求会避免与关键操作和优先级高的网络请求竞争资源。
    • 用户代理可以有效的合并信标请求,以优化移动设备上的能量使用。
    • 信标请求保证在页面卸载之前初始化,并且允许运行完成,而不需要阻塞请求或阻塞其它用户交互事件处理。
    window.navigator.sendBeacon(url,data)
    
    • url : 传送数据的 URL 。
    • data : 传送的数据,这个参数可选,支持 ArrayBufferView、Blob、DOMString、FormData ,更详细类型见 BodyInit unions 。

    兼容性见 Can I use Beacon 。

    不足:

    • 这个方法不会提供任何关于数据传送是否成功的信息。
    • 受跨域限制。

    频率

    针对不同的异常和目的,上报的频率可以人为的进行控制。比较概括的可以分为三类:即时上报、批量上报、用户主动上报。

    即时上报

    即时上报就是触发了异常马上上报,这类异常一般会严重影响到用户的使用。

    当比较多的异常连续触发或出现了无限循环触发异常时,上报的请求也需要一定的管理。下面是 @sentry/utils version 5.8.0 中管理的一种方式:

    import { SentryError } from './error';
    import { SyncPromise } from './syncpromise';
    /** A simple queue that holds promises. */
    export class PromiseBuffer {
        constructor(_limit) {
            this._limit = _limit;
            /** Internal set of queued Promises */
            this._buffer = [];
        }
        /**
         * Says if the buffer is ready to take more requests
         */
        isReady() {
            return this._limit === undefined || this.length() < this._limit;
        }
        /**
         * Add a promise to the queue.
         *
         * @param task Can be any PromiseLike<T>
         * @returns The original promise.
         */
        add(task) {
            if (!this.isReady()) {
                return SyncPromise.reject(new SentryError('Not adding Promise due to buffer limit reached.'));
            }
            if (this._buffer.indexOf(task) === -1) {
                this._buffer.push(task);
            }
            task
                .then(() => this.remove(task))
                .then(null, () => this.remove(task).then(null, () => {
                // We have to add this catch here otherwise we have an unhandledPromiseRejection
                // because it's a new Promise chain.
            }));
            return task;
        }
        /**
         * Remove a promise to the queue.
         *
         * @param task Can be any PromiseLike<T>
         * @returns Removed promise.
         */
        remove(task) {
            const removedTask = this._buffer.splice(this._buffer.indexOf(task), 1)[0];
            return removedTask;
        }
        /**
         * This function returns the number of unresolved promises in the queue.
         */
        length() {
            return this._buffer.length;
        }
        /**
         * This will drain the whole queue, returns true if queue is empty or drained.
         * If timeout is provided and the queue takes longer to drain, the promise still resolves but with false.
         *
         * @param timeout Number in ms to wait until it resolves with false.
         */
        drain(timeout) {
            return new SyncPromise(resolve => {
                const capturedSetTimeout = setTimeout(() => {
                    if (timeout && timeout > 0) {
                        resolve(false);
                    }
                }, timeout);
                SyncPromise.all(this._buffer)
                    .then(() => {
                    clearTimeout(capturedSetTimeout);
                    resolve(true);
                })
                    .then(null, () => {
                    resolve(true);
                });
            });
        }
    }
    

    主要思路:

    • 初始化的时候,会提供一个数组 _buffer 和阈值 _limit
    • 所有的请求会进行类似 Promise 机制包裹,通过方法 add 放到数组中。请求是异步的,根据事件循环机制,可以连续增加新的请求。
    • 当添加的请求数量超过 _limit 时,就不会继续添加了,这样就可以达到一定控制的效果。

    批量上报

    将收集到的信息先存储到本地,当数据量或间隔时间达到一定的阈值,将本地存储的信息一次性打包上传。追踪用户的操作路径这类信息比较适合这种方式。

    用户主动上报

    提供一个异常上报入口,由用户自己反馈所遇到的问题。

    如果考虑隐私方面的政策,在进入系统的时候,需要告知会进行一些信息采集。

    参考资料

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

    《罪恶王冠》画面的确是精美,配乐的倾向也是很燃的那种,但看的时候,并没有那个感觉。

    前端异常上报


    起源地下载网 » 前端异常上报

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元