fix: fix console plugin's OOM problem (#656)

* fix: fix console plugin's OOM problem

* fix: fix console plugin

* feat: patch

* feat: patch

* feat: patch

Co-authored-by: chenyangbj01 <chenyangbj01@fenbi.com>
This commit is contained in:
Peter Chen
2021-08-07 23:06:50 +08:00
committed by GitHub
parent 838287a16d
commit 8d40e52010
6 changed files with 70 additions and 22 deletions

View File

@@ -1,7 +1,7 @@
import { listenerHandler, RecordPlugin } from '../../../types';
import { stringify } from './stringify';
import { StackFrame, ErrorStackParser } from './error-stack-parser';
import { patch } from '../../../utils';
import { ErrorStackParser, StackFrame } from './error-stack-parser';
import { stringify } from './stringify';
export type StringifyOptions = {
// limit of string length
@@ -11,6 +11,11 @@ export type StringifyOptions = {
* if an object contains more keys than this limit, we would call its toString function directly
*/
numOfKeysLimit: number;
/**
* limit number of depth in an object
* if an object is too deep, toString process may cause browser OOM
*/
depthOfLimit: number;
};
type LogRecordOptions = {

View File

@@ -48,6 +48,31 @@ function pathToSelector(node: HTMLElement): string | '' {
return path;
}
/**
* judge is object
*/
function isObject(obj: any): boolean {
return Object.prototype.toString.call(obj) === '[object Object]';
}
/**
* judge the object's depth
*/
function isObjTooDeep(obj: any, limit: number): boolean {
if (limit === 0) {
return true;
}
const keys = Object.keys(obj);
for (const key of keys) {
if (isObject(obj[key]) && isObjTooDeep(obj[key], limit - 1)) {
return true;
}
}
return false;
}
/**
* stringify any js object
* @param obj the object to stringify
@@ -58,6 +83,7 @@ export function stringify(
): string {
const options: StringifyOptions = {
numOfKeysLimit: 50,
depthOfLimit: 4,
};
Object.assign(options, stringifyOptions);
const stack: any[] = [];
@@ -89,7 +115,7 @@ export function stringify(
if (value === null || value === undefined) {
return value;
}
if (shouldToString(value)) {
if (shouldIgnore(value)) {
return toString(value);
}
if (value instanceof Event) {
@@ -115,18 +141,28 @@ export function stringify(
});
/**
* whether we should call toString function of this object
* whether we should ignore obj's info and call toString() function instead
*/
function shouldToString(_obj: object): boolean {
if (
typeof _obj === 'object' &&
Object.keys(_obj).length > options.numOfKeysLimit
) {
function shouldIgnore(_obj: object): boolean {
// outof keys limit
if (isObject(_obj) && Object.keys(_obj).length > options.numOfKeysLimit) {
return true;
}
// is function
if (typeof _obj === 'function') {
return true;
}
/**
* judge object's depth to avoid browser's OOM
*
* issues: https://github.com/rrweb-io/rrweb/issues/653
*/
if (isObject(_obj) && isObjTooDeep(_obj, options.depthOfLimit)) {
return true;
}
return false;
}

View File

@@ -2,6 +2,7 @@ import { RecordPlugin } from '../../../types';
export declare type StringifyOptions = {
stringLengthLimit?: number;
numOfKeysLimit: number;
depthOfLimit: number;
};
declare type LogRecordOptions = {
level?: LogLevel[] | undefined;
@@ -38,4 +39,5 @@ export declare type Logger = {
};
export declare const PLUGIN_NAME = "rrweb/console@1";
export declare const getRecordConsolePlugin: (options?: LogRecordOptions) => RecordPlugin;
export {};
export { };