Skip to content

Commit 8f25abb

Browse files
DIME-8714 stacktrace now contains the recursive cause in one string with truncation
1 parent fbd47a8 commit 8f25abb

File tree

3 files changed

+72
-34
lines changed

3 files changed

+72
-34
lines changed

package-lock.json

Lines changed: 0 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/logger/logger.spec.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -270,16 +270,9 @@ describe('Logger', () => {
270270
const logArguments = JSON.parse(outputStub.args[0][0]);
271271
expect(logArguments.error.type).to.eql('MainError');
272272
expect(logArguments.error.message).to.eql('Main error occurred');
273-
expect(logArguments.error.cause).to.eql({
274-
type: 'IntermediateError',
275-
message: 'Intermediate error',
276-
stack_trace: intermediateError.stack,
277-
cause: {
278-
type: 'RootCauseError',
279-
message: 'Root cause error',
280-
stack_trace: rootCause.stack,
281-
},
282-
});
273+
expect(logArguments.error.stack_trace).to.eql(
274+
[mainError.stack, 'Caused by: ' + intermediateError.stack, 'Caused by: ' + rootCause.stack].join('\n'),
275+
);
283276
});
284277

285278
describe('#customError', () => {
@@ -350,7 +343,7 @@ describe('Logger', () => {
350343
const logArguments = JSON.parse(outputStub.args[0][0]);
351344

352345
expect(logArguments.error.type).to.eql(errorObject.name);
353-
expect(logArguments.error.stack_trace).to.eql(errorObject.stack);
346+
expect(logArguments.error.stack_trace).to.eql([errorObject.name, errorObject.stack].join('\n'));
354347
expect(logArguments.error.message).to.eql(errorObject.message);
355348
expect(logArguments.error.context).to.eql(JSON.stringify(errorObject.data));
356349
});

src/logger/logger.ts

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -171,32 +171,90 @@ export class Logger {
171171
}
172172

173173
private getBaseErrorDetails(error: Error) {
174+
const shortenedData = this.shortenData((error as ErrorWithData).data);
175+
174176
if (Logger.config.outputFormat === 'legacy') {
175177
return {
176178
error_name: error.name,
177179
error_stack: this.shortenStackTrace(error.stack || ''),
178180
error_message: error.message,
179-
error_data: this.shortenData((error as ErrorWithData).data),
181+
error_data: shortenedData,
180182
};
181183
}
182184

183185
return {
184-
error: this.extractError(error),
186+
error: {
187+
type: error.name,
188+
message: error.message,
189+
...(shortenedData && { context: shortenedData }),
190+
stack_trace: this.getShortenedStackTrace(error),
191+
},
185192
event: {
186193
reason: error.message,
187194
},
188195
};
189196
}
190197

191-
private extractError(error: Error): unknown {
192-
const shortenedData = this.shortenData((error as ErrorWithData).data);
193-
return {
194-
type: error.name,
195-
message: error.message,
196-
...(shortenedData && { context: shortenedData }),
197-
stack_trace: this.shortenStackTrace(error.stack || ''),
198-
...(error.cause instanceof Error && { cause: this.extractError(error.cause) }),
198+
private getShortenedStackTrace(error: Error): string {
199+
const getNumberOfCommonFrames = (ownTrace: string[], enclosingTrace: string[]): number => {
200+
let m = ownTrace.length - 1;
201+
let n = enclosingTrace.length - 1;
202+
203+
while (m > 0 && n > 0 && ownTrace[m] === enclosingTrace[n]) {
204+
m--;
205+
n--;
206+
}
207+
return ownTrace.length - 1 - m;
199208
};
209+
210+
const getEnclosedStackTrace = (error: Error, enclosingTrace: string[], caption: string): string[] => {
211+
const output: string[] = [];
212+
213+
let errorName: string;
214+
let errorStack: string[];
215+
const errorStackLines = error.stack ? error.stack.split('\n') : [];
216+
const firstLine = errorStackLines.at(0);
217+
218+
if (error.stack) {
219+
if (firstLine?.includes(error.name)) {
220+
errorName = firstLine!;
221+
errorStack = errorStackLines.slice(1);
222+
} else {
223+
errorName = error.name;
224+
errorStack = errorStackLines;
225+
}
226+
} else {
227+
errorName = error.name;
228+
errorStack = [];
229+
}
230+
231+
const commonFrames = getNumberOfCommonFrames(errorStack, enclosingTrace);
232+
const uniqueFrames = errorStack.length - commonFrames;
233+
234+
output.push(caption + errorName);
235+
errorStack.slice(0, uniqueFrames).forEach((line) => output.push(line));
236+
if (commonFrames > 0) {
237+
output.push(`\t... ${commonFrames} more`);
238+
}
239+
240+
if (error.cause instanceof Error) {
241+
output.push(...getEnclosedStackTrace(error.cause, errorStackLines, 'Caused by: '));
242+
}
243+
244+
return output;
245+
};
246+
247+
const stackTrace = getEnclosedStackTrace(error, [], '');
248+
const joinedStackTrace = stackTrace.join('\n');
249+
let resultStackTraceStr: string;
250+
251+
if (joinedStackTrace.length > STACK_TRACE_LIMIT) {
252+
resultStackTraceStr = joinedStackTrace.substring(0, STACK_TRACE_LIMIT) + ' ...';
253+
} else {
254+
resultStackTraceStr = joinedStackTrace;
255+
}
256+
257+
return resultStackTraceStr;
200258
}
201259

202260
private getAxiosErrorDetails(error: AxiosError) {

0 commit comments

Comments
 (0)