@@ -11,7 +11,7 @@ import type {
1111 StackParser ,
1212} from '@sentry/types' ;
1313
14- import { isError , isParameterizedString , isPlainObject } from './is' ;
14+ import { isError , isErrorEvent , isParameterizedString , isPlainObject } from './is' ;
1515import { addExceptionMechanism , addExceptionTypeValue } from './misc' ;
1616import { normalizeToSize } from './normalize' ;
1717import { extractExceptionKeysForMessage } from './object' ;
@@ -40,7 +40,21 @@ export function exceptionFromError(stackParser: StackParser, error: Error): Exce
4040 return exception ;
4141}
4242
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 {
4458 if ( 'name' in exception && typeof exception . name === 'string' ) {
4559 let message = `'${ exception . name } ' captured as exception` ;
4660
@@ -51,13 +65,67 @@ function getMessageForObject(exception: object): string {
5165 return message ;
5266 } else if ( 'message' in exception && typeof exception . message === 'string' ) {
5367 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- ) } `;
6068 }
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 ] ;
61129}
62130
63131/**
@@ -70,36 +138,18 @@ export function eventFromUnknownInput(
70138 exception : unknown ,
71139 hint ?: EventHint ,
72140) : Event {
73- let ex : unknown = exception ;
74141 const providedMechanism : Mechanism | undefined =
75142 hint && hint . data && ( hint . data as { mechanism : Mechanism } ) . mechanism ;
76143 const mechanism : Mechanism = providedMechanism || {
77144 handled : true ,
78145 type : 'generic' ,
79146 } ;
80147
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 ) ;
99149
100150 const event : Event = {
101151 exception : {
102- values : [ exceptionFromError ( stackParser , ex as Error ) ] ,
152+ values : [ exceptionFromError ( stackParser , ex ) ] ,
103153 } ,
104154 } ;
105155
0 commit comments