乐趣区

关于javascript:前端异常上报

引子

在前端异样解析: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
退出移动版