@@ -11,7 +11,7 @@ import type {
11
11
StackParser ,
12
12
} from '@sentry/types' ;
13
13
14
- import { isError , isParameterizedString , isPlainObject } from './is' ;
14
+ import { isError , isErrorEvent , isParameterizedString , isPlainObject } from './is' ;
15
15
import { addExceptionMechanism , addExceptionTypeValue } from './misc' ;
16
16
import { normalizeToSize } from './normalize' ;
17
17
import { extractExceptionKeysForMessage } from './object' ;
@@ -40,7 +40,21 @@ export function exceptionFromError(stackParser: StackParser, error: Error): Exce
40
40
return exception ;
41
41
}
42
42
43
- function getMessageForObject ( exception : object ) : string {
43
+ /** If a plain object has a property that is an `Error`, return this error. */
44
+ function getErrorPropertyFromObject ( obj : Record < string , unknown > ) : Error | undefined {
45
+ for ( const prop in obj ) {
46
+ if ( Object . prototype . hasOwnProperty . call ( obj , prop ) ) {
47
+ const value = obj [ prop ] ;
48
+ if ( value instanceof Error ) {
49
+ return value ;
50
+ }
51
+ }
52
+ }
53
+
54
+ return undefined ;
55
+ }
56
+
57
+ function getMessageForObject ( exception : Record < string , unknown > ) : string {
44
58
if ( 'name' in exception && typeof exception . name === 'string' ) {
45
59
let message = `'${ exception . name } ' captured as exception` ;
46
60
@@ -51,13 +65,67 @@ function getMessageForObject(exception: object): string {
51
65
return message ;
52
66
} else if ( 'message' in exception && typeof exception . message === 'string' ) {
53
67
return exception . message ;
54
- } else {
55
- // This will allow us to group events based on top-level keys
56
- // which is much better than creating new group when any key/value change
57
- return `Object captured as exception with keys: ${ extractExceptionKeysForMessage (
58
- exception as Record < string , unknown > ,
59
- ) } `;
60
68
}
69
+
70
+ const keys = extractExceptionKeysForMessage ( exception ) ;
71
+
72
+ // Some ErrorEvent instances do not have an `error` property, which is why they are not handled before
73
+ // We still want to try to get a decent message for these cases
74
+ if ( isErrorEvent ( exception ) ) {
75
+ return `Event \`ErrorEvent\` captured as exception with message \`${ exception . message } \`` ;
76
+ }
77
+
78
+ const className = getObjectClassName ( exception ) ;
79
+
80
+ return `${
81
+ className && className !== 'Object' ? `'${ className } '` : 'Object'
82
+ } captured as exception with keys: ${ keys } `;
83
+ }
84
+
85
+ function getObjectClassName ( obj : unknown ) : string | undefined | void {
86
+ try {
87
+ const prototype : unknown | null = Object . getPrototypeOf ( obj ) ;
88
+ return prototype ? prototype . constructor . name : undefined ;
89
+ } catch ( e ) {
90
+ // ignore errors here
91
+ }
92
+ }
93
+
94
+ function getException (
95
+ client : Client ,
96
+ mechanism : Mechanism ,
97
+ exception : unknown ,
98
+ hint ?: EventHint ,
99
+ ) : [ Error , Extras | undefined ] {
100
+ if ( isError ( exception ) ) {
101
+ return [ exception , undefined ] ;
102
+ }
103
+
104
+ // Mutate this!
105
+ mechanism . synthetic = true ;
106
+
107
+ if ( isPlainObject ( exception ) ) {
108
+ const normalizeDepth = client && client . getOptions ( ) . normalizeDepth ;
109
+ const extras = { [ '__serialized__' ] : normalizeToSize ( exception as Record < string , unknown > , normalizeDepth ) } ;
110
+
111
+ const errorFromProp = getErrorPropertyFromObject ( exception ) ;
112
+ if ( errorFromProp ) {
113
+ return [ errorFromProp , extras ] ;
114
+ }
115
+
116
+ const message = getMessageForObject ( exception ) ;
117
+ const ex = ( hint && hint . syntheticException ) || new Error ( message ) ;
118
+ ex . message = message ;
119
+
120
+ return [ ex , extras ] ;
121
+ }
122
+
123
+ // This handles when someone does: `throw "something awesome";`
124
+ // We use synthesized Error here so we can extract a (rough) stack trace.
125
+ const ex = ( hint && hint . syntheticException ) || new Error ( exception as string ) ;
126
+ ex . message = `${ exception } ` ;
127
+
128
+ return [ ex , undefined ] ;
61
129
}
62
130
63
131
/**
@@ -70,36 +138,18 @@ export function eventFromUnknownInput(
70
138
exception : unknown ,
71
139
hint ?: EventHint ,
72
140
) : Event {
73
- let ex : unknown = exception ;
74
141
const providedMechanism : Mechanism | undefined =
75
142
hint && hint . data && ( hint . data as { mechanism : Mechanism } ) . mechanism ;
76
143
const mechanism : Mechanism = providedMechanism || {
77
144
handled : true ,
78
145
type : 'generic' ,
79
146
} ;
80
147
81
- let extras : Extras | undefined ;
82
-
83
- if ( ! isError ( exception ) ) {
84
- if ( isPlainObject ( exception ) ) {
85
- const normalizeDepth = client && client . getOptions ( ) . normalizeDepth ;
86
- extras = { [ '__serialized__' ] : normalizeToSize ( exception as Record < string , unknown > , normalizeDepth ) } ;
87
-
88
- const message = getMessageForObject ( exception ) ;
89
- ex = ( hint && hint . syntheticException ) || new Error ( message ) ;
90
- ( ex as Error ) . message = message ;
91
- } else {
92
- // This handles when someone does: `throw "something awesome";`
93
- // We use synthesized Error here so we can extract a (rough) stack trace.
94
- ex = ( hint && hint . syntheticException ) || new Error ( exception as string ) ;
95
- ( ex as Error ) . message = exception as string ;
96
- }
97
- mechanism . synthetic = true ;
98
- }
148
+ const [ ex , extras ] = getException ( client , mechanism , exception , hint ) ;
99
149
100
150
const event : Event = {
101
151
exception : {
102
- values : [ exceptionFromError ( stackParser , ex as Error ) ] ,
152
+ values : [ exceptionFromError ( stackParser , ex ) ] ,
103
153
} ,
104
154
} ;
105
155
0 commit comments