豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit caea23a

Browse files
authored
fix: limit stack traces to 50 lines (#923)
This PR refactors the stack trace formatting logic a bit: We split the `formatStackTrace` function into 2: One for the top-level stack trace and the rest for the recursive ones. We also return lines separately so we can slice the result later.
1 parent e320ebf commit caea23a

File tree

3 files changed

+124
-22
lines changed

3 files changed

+124
-22
lines changed

src/formatters/ConsoleFormatter.ts

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export interface ConsoleFormatterOptions {
2323
}
2424

2525
export class ConsoleFormatter {
26+
static readonly #STACK_TRACE_MAX_LINES = 50;
27+
2628
readonly #id: number;
2729
readonly #type: string;
2830
readonly #text: string;
@@ -134,7 +136,6 @@ export class ConsoleFormatter {
134136
this.#formatArgs(),
135137
this.#formatStackTrace(this.#stack, this.#cause, {
136138
includeHeading: true,
137-
includeNote: true,
138139
}),
139140
].filter(line => !!line);
140141
return result.join('\n');
@@ -158,7 +159,6 @@ export class ConsoleFormatter {
158159
arg.message,
159160
this.#formatStackTrace(arg.stackTrace, arg.cause, {
160161
includeHeading: false,
161-
includeNote: true,
162162
}),
163163
]
164164
.filter(line => !!line)
@@ -186,38 +186,59 @@ export class ConsoleFormatter {
186186
#formatStackTrace(
187187
stackTrace: DevTools.DevTools.StackTrace.StackTrace.StackTrace | undefined,
188188
cause: SymbolizedError | undefined,
189-
opts: {includeHeading: boolean; includeNote: boolean},
189+
opts: {includeHeading: boolean},
190190
): string {
191191
if (!stackTrace) {
192192
return '';
193193
}
194194

195+
const lines = this.#formatStackTraceInner(stackTrace, cause);
196+
const includedLines = lines.slice(
197+
0,
198+
ConsoleFormatter.#STACK_TRACE_MAX_LINES,
199+
);
200+
const reminderCount = lines.length - includedLines.length;
201+
195202
return [
196203
opts.includeHeading ? '### Stack trace' : '',
197-
this.#formatFragment(stackTrace.syncFragment),
198-
...stackTrace.asyncFragments.map(this.#formatAsyncFragment.bind(this)),
199-
this.#formatCause(cause),
200-
opts.includeNote
201-
? 'Note: line and column numbers use 1-based indexing'
202-
: '',
204+
...includedLines,
205+
reminderCount > 0 ? `... and ${reminderCount} more frames` : '',
206+
'Note: line and column numbers use 1-based indexing',
203207
]
204208
.filter(line => !!line)
205209
.join('\n');
206210
}
207211

212+
#formatStackTraceInner(
213+
stackTrace: DevTools.DevTools.StackTrace.StackTrace.StackTrace | undefined,
214+
cause: SymbolizedError | undefined,
215+
): string[] {
216+
if (!stackTrace) {
217+
return [];
218+
}
219+
220+
return [
221+
...this.#formatFragment(stackTrace.syncFragment),
222+
...stackTrace.asyncFragments
223+
.map(this.#formatAsyncFragment.bind(this))
224+
.flat(),
225+
...this.#formatCause(cause),
226+
];
227+
}
228+
208229
#formatFragment(
209230
fragment: DevTools.DevTools.StackTrace.StackTrace.Fragment,
210-
): string {
211-
return fragment.frames.map(this.#formatFrame.bind(this)).join('\n');
231+
): string[] {
232+
return fragment.frames.map(this.#formatFrame.bind(this));
212233
}
213234

214235
#formatAsyncFragment(
215236
fragment: DevTools.DevTools.StackTrace.StackTrace.AsyncFragment,
216-
): string {
237+
): string[] {
217238
const separatorLineLength = 40;
218239
const prefix = `--- ${fragment.description || 'async'} `;
219240
const separator = prefix + '-'.repeat(separatorLineLength - prefix.length);
220-
return separator + '\n' + this.#formatFragment(fragment);
241+
return [separator, ...this.#formatFragment(fragment)];
221242
}
222243

223244
#formatFrame(frame: DevTools.DevTools.StackTrace.StackTrace.Frame): string {
@@ -231,20 +252,15 @@ export class ConsoleFormatter {
231252
return result;
232253
}
233254

234-
#formatCause(cause: SymbolizedError | undefined): string {
255+
#formatCause(cause: SymbolizedError | undefined): string[] {
235256
if (!cause) {
236-
return '';
257+
return [];
237258
}
238259

239260
return [
240261
`Caused by: ${cause.message}`,
241-
this.#formatStackTrace(cause.stackTrace, cause.cause, {
242-
includeHeading: false,
243-
includeNote: false,
244-
}),
245-
]
246-
.filter(line => !!line)
247-
.join('\n');
262+
...this.#formatStackTraceInner(cause.stackTrace, cause.cause),
263+
];
248264
}
249265

250266
toJSON(): object {

tests/formatters/ConsoleFormatter.test.js.snapshot

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,61 @@ Message: log> Processing file:
102102
### Arguments
103103
Arg #0: <error: Argument 0 is no longer available>
104104
`;
105+
106+
exports[`ConsoleFormatter > toStringDetailed > limits the number lines for a stack trace 1`] = `
107+
ID: 11
108+
Message: log> Hello stack trace!
109+
### Stack trace
110+
at fn0 (main.js:0:0)
111+
at fn1 (main.js:1:1)
112+
at fn2 (main.js:2:2)
113+
at fn3 (main.js:3:3)
114+
at fn4 (main.js:4:4)
115+
at fn5 (main.js:5:5)
116+
at fn6 (main.js:6:6)
117+
at fn7 (main.js:7:7)
118+
at fn8 (main.js:8:8)
119+
at fn9 (main.js:9:9)
120+
at fn10 (main.js:10:10)
121+
at fn11 (main.js:11:11)
122+
at fn12 (main.js:12:12)
123+
at fn13 (main.js:13:13)
124+
at fn14 (main.js:14:14)
125+
at fn15 (main.js:15:15)
126+
at fn16 (main.js:16:16)
127+
at fn17 (main.js:17:17)
128+
at fn18 (main.js:18:18)
129+
at fn19 (main.js:19:19)
130+
at fn20 (main.js:20:20)
131+
at fn21 (main.js:21:21)
132+
at fn22 (main.js:22:22)
133+
at fn23 (main.js:23:23)
134+
at fn24 (main.js:24:24)
135+
at fn25 (main.js:25:25)
136+
at fn26 (main.js:26:26)
137+
at fn27 (main.js:27:27)
138+
at fn28 (main.js:28:28)
139+
at fn29 (main.js:29:29)
140+
at fn30 (main.js:30:30)
141+
at fn31 (main.js:31:31)
142+
at fn32 (main.js:32:32)
143+
at fn33 (main.js:33:33)
144+
at fn34 (main.js:34:34)
145+
at fn35 (main.js:35:35)
146+
at fn36 (main.js:36:36)
147+
at fn37 (main.js:37:37)
148+
at fn38 (main.js:38:38)
149+
at fn39 (main.js:39:39)
150+
at fn40 (main.js:40:40)
151+
at fn41 (main.js:41:41)
152+
at fn42 (main.js:42:42)
153+
at fn43 (main.js:43:43)
154+
at fn44 (main.js:44:44)
155+
at fn45 (main.js:45:45)
156+
at fn46 (main.js:46:46)
157+
at fn47 (main.js:47:47)
158+
at fn48 (main.js:48:48)
159+
at fn49 (main.js:49:49)
160+
... and 50 more frames
161+
Note: line and column numbers use 1-based indexing
162+
`;

tests/formatters/ConsoleFormatter.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,34 @@ describe('ConsoleFormatter', () => {
451451
).toStringDetailed();
452452
t.assert.snapshot?.(result);
453453
});
454+
455+
it('limits the number lines for a stack trace', async t => {
456+
const message = createMockMessage({
457+
type: () => 'log',
458+
text: () => 'Hello stack trace!',
459+
});
460+
const frames: DevTools.StackTrace.StackTrace.Frame[] = [];
461+
for (let i = 0; i < 100; ++i) {
462+
frames.push({
463+
line: i,
464+
column: i,
465+
url: 'main.js',
466+
name: `fn${i}`,
467+
});
468+
}
469+
const stackTrace = {
470+
syncFragment: {frames},
471+
asyncFragments: [],
472+
} as unknown as DevTools.StackTrace.StackTrace.StackTrace;
473+
474+
const result = (
475+
await ConsoleFormatter.from(message, {
476+
id: 11,
477+
resolvedStackTraceForTesting: stackTrace,
478+
})
479+
).toStringDetailed();
480+
t.assert.snapshot?.(result);
481+
});
454482
});
455483
describe('toJSON', () => {
456484
it('formats a console.log message', async () => {

0 commit comments

Comments
 (0)