1
1
import c from 'ansi-colors' ;
2
2
import { Logger } from './logger' ;
3
- import { LEVEL_TO_STRING , LogLevel , PREFIX_DELIMITER } from './log_consts' ;
3
+ import { IS_APIFY_LOGGER_EXCEPTION , LEVEL_TO_STRING , LogLevel , PREFIX_DELIMITER } from './log_consts' ;
4
+ import { LimitedError } from './log_helpers' ;
5
+ import { getStackFrames } from './node_internals' ;
4
6
5
7
const SHORTEN_LEVELS = {
6
8
SOFT_FAIL : 'SFAIL' ,
@@ -31,18 +33,12 @@ const DEFAULT_OPTIONS = {
31
33
skipTime : true ,
32
34
} ;
33
35
34
- export interface Exception extends Error {
35
- type ?: string ;
36
- details ?: Record < string , any > ;
37
- reason ?: string ;
38
- }
39
-
40
36
export class LoggerText extends Logger {
41
37
constructor ( options = { } ) {
42
38
super ( { ...DEFAULT_OPTIONS , ...options } ) ;
43
39
}
44
40
45
- _log ( level : LogLevel , message : string , data ?: any , exception ?: Exception , opts : Record < string , any > = { } ) {
41
+ _log ( level : LogLevel , message : string , data ?: any , exception ?: unknown , opts : Record < string , any > = { } ) {
46
42
let { prefix, suffix } = opts ;
47
43
48
44
let maybeDate = '' ;
@@ -65,35 +61,67 @@ export class LoggerText extends Logger {
65
61
return line ;
66
62
}
67
63
68
- _parseException ( exception : Exception ) {
69
- let errStack = '' ;
64
+ protected _parseException ( exception : unknown , indentLevel = 1 ) {
65
+ if ( [ 'string' , 'boolean' , 'number' , 'undefined' , 'bigint' ] . includes ( typeof exception ) ) {
66
+ return `\n${ exception } ` ;
67
+ }
68
+
69
+ if ( exception === null ) {
70
+ return '\nnull' ;
71
+ }
72
+
73
+ // We need to manually call toString on symbols
74
+ if ( typeof exception === 'symbol' ) {
75
+ return `\n${ exception . toString ( ) } ` ;
76
+ }
77
+
78
+ if ( typeof exception === 'object' && IS_APIFY_LOGGER_EXCEPTION in exception ) {
79
+ return this . _parseLoggerException ( exception as LimitedError , indentLevel ) ;
80
+ }
81
+
82
+ // Anything else we just stringify
83
+ return `\n${ JSON . stringify ( exception , null , 2 ) } ` ;
84
+ }
70
85
71
- // Parse error.type and error.details from ApifyClientError.
86
+ private _parseLoggerException ( exception : LimitedError , indentLevel = 1 ) {
72
87
const errDetails = [ ] ;
73
- if ( exception . type ) errDetails . push ( `type=${ exception . type } ` ) ;
88
+
89
+ if ( exception . type ) {
90
+ errDetails . push ( `type=${ exception . type } ` ) ;
91
+ }
92
+
74
93
if ( exception . details ) {
75
94
Object . entries ( exception . details ) . map ( ( [ key , val ] ) => errDetails . push ( `${ key } =${ val } ` ) ) ;
76
95
}
77
96
78
- // Parse error stack lines.
79
- // NOTE: Reason is here to support Meteor.js like errors.
80
- const errorString = exception . stack || exception . reason || exception . toString ( ) ;
81
- const errorLines = errorString . split ( '\n' ) ;
82
- // const causeString = exception.cause
83
- // ? inspect(exception.cause, { colors: true, maxArrayLength: 20 }) : null;
97
+ // Parse the stack lines
98
+ const errorString = exception . stack || exception . reason || exception . message ;
99
+ const isStack = errorString === exception . stack ;
100
+ const errorLines = getStackFrames ( exception , errorString ) ;
101
+
102
+ if ( isStack ) {
103
+ // Remove the useless `Error` prefix from stack traces
104
+ errorLines [ 0 ] = exception . message || errorLines [ 0 ] ;
105
+ }
106
+
107
+ // Add details to the first line.
108
+ if ( errDetails . length ) {
109
+ errorLines [ 0 ] += c . gray ( `(details: ${ errDetails . join ( ', ' ) } )` ) ;
110
+ }
84
111
85
- // Add details to a first line.
86
- if ( errDetails . length ) errorLines [ 0 ] += c . gray ( `(details: ${ errDetails . join ( ', ' ) } )` ) ;
112
+ // Make stack lines gray
113
+ for ( let i = 1 ; i < errorLines . length ; i ++ ) {
114
+ errorLines [ i ] = c . gray ( errorLines [ i ] ) ;
115
+ }
87
116
88
- // if (causeString) {
89
- // const causeLines = causeString.split('\n');
90
- // errorLines.push(c.gray(`Cause: ${causeLines[0]}`), ...causeLines.slice(1).map((line) => ` ${line}`) );
91
- // }
117
+ // Recursively parse the cause.
118
+ if ( exception . cause ) {
119
+ const causeString = this . _parseException ( exception . cause , indentLevel + 1 ) ;
120
+ const causeLines = causeString . trim ( ) . split ( '\n' ) ;
92
121
93
- // Compose it back.
94
- errStack = errorLines . map ( ( line ) => ` ${ line } ` ) . join ( '\n' ) ;
95
- errStack = `\n${ errStack } ` ;
122
+ errorLines . push ( c . red ( ` CAUSE: ${ c . reset ( causeLines [ 0 ] ) } ` ) , ...causeLines . slice ( 1 ) ) ;
123
+ }
96
124
97
- return errStack ;
125
+ return `\n ${ errorLines . map ( ( line ) => ` ${ ' ' . repeat ( indentLevel * 2 ) } ${ line } ` ) . join ( '\n' ) } ` ;
98
126
}
99
127
}
0 commit comments