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:
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 { };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user