11import { Backend , DSN , Options , SentryError } from '@sentry/core' ;
2- import { addBreadcrumb , captureEvent } from '@sentry/minimal ' ;
2+ import { getDefaultHub } from '@sentry/hub ' ;
33import { SentryEvent , SentryResponse } from '@sentry/types' ;
4- import { Raven } from './raven' ;
4+ import { isError , isPlainObject } from '@sentry/utils/is' ;
5+ import {
6+ limitObjectDepthToSize ,
7+ serializeKeysToEventMessage ,
8+ } from '@sentry/utils/object' ;
9+ import * as md5 from 'md5' ;
10+ import * as stacktrace from 'stack-trace' ;
11+ import { parseError , parseStack , prepareFramesForEvent } from './parsers' ;
512import { HTTPSTransport , HTTPTransport } from './transports' ;
613
7- /** Extension to the Function type. */
8- interface FunctionExt extends Function {
9- __SENTRY_CAPTURE__ ?: boolean ;
10- }
11-
12- /** Prepares an event so it can be send with raven-js. */
13- function normalizeEvent ( ravenEvent : any ) : SentryEvent {
14- const event = ravenEvent ;
15- // tslint:disable-next-line:no-unsafe-any
16- if ( ravenEvent . exception && ! ravenEvent . exception . values ) {
17- // tslint:disable-next-line:no-unsafe-any
18- event . exception = { values : ravenEvent . exception } ;
19- }
20- return event as SentryEvent ;
21- }
22-
2314/**
2415 * Configuration options for the Sentry Node SDK.
2516 * @see NodeClient for more information.
2617 */
2718export interface NodeOptions extends Options {
28- /**
29- * Whether unhandled Promise rejections should be captured or not. If true,
30- * this will install an error handler and prevent the process from crashing.
31- * Defaults to false.
32- */
33- captureUnhandledRejections ?: boolean ;
34-
3519 /** Callback that is executed when a fatal global error occurs. */
3620 onFatalError ?( error : Error ) : void ;
3721}
@@ -44,77 +28,58 @@ export class NodeBackend implements Backend {
4428 /**
4529 * @inheritDoc
4630 */
47- public install ( ) : boolean {
48- // We are only called by the client if the SDK is enabled and a valid DSN
49- // has been configured. If no DSN is present, this indicates a programming
50- // error.
51- const dsn = this . options . dsn ;
52- if ( ! dsn ) {
53- throw new SentryError (
54- 'Invariant exception: install() must not be called when disabled' ,
55- ) ;
56- }
57-
58- Raven . config ( dsn , this . options ) ;
59-
60- // We need to leave it here for now, as we are skipping `install` call,
61- // due to integrations migration
62- // TODO: Remove it once we fully migrate our code
63- const { onFatalError } = this . options ;
64- if ( onFatalError ) {
65- Raven . onFatalError = onFatalError ;
66- }
67- Raven . installed = true ;
68-
69- // Hook into Raven's breadcrumb mechanism. This allows us to intercept both
70- // breadcrumbs created internally by Raven and pass them to the Client
71- // first, before actually capturing them.
72- Raven . captureBreadcrumb = breadcrumb => {
73- addBreadcrumb ( breadcrumb ) ;
74- } ;
75-
76- // Hook into Raven's internal event sending mechanism. This allows us to
77- // pass events to the client, before they will be sent back here for
78- // actual submission.
79- Raven . send = ( event , callback ) => {
80- if ( callback && ( callback as FunctionExt ) . __SENTRY_CAPTURE__ ) {
81- callback ( normalizeEvent ( event ) ) ;
31+ public async eventFromException ( exception : any ) : Promise < SentryEvent > {
32+ let stack : stacktrace . StackFrame [ ] | undefined ;
33+ let ex : any = exception ;
34+
35+ if ( ! isError ( exception ) ) {
36+ if ( isPlainObject ( exception ) ) {
37+ // This will allow us to group events based on top-level keys
38+ // which is much better than creating new group when any key/value change
39+ const keys = Object . keys ( exception as { } ) . sort ( ) ;
40+ const message = `Non-Error exception captured with keys: ${ serializeKeysToEventMessage (
41+ keys ,
42+ ) } `;
43+
44+ // TODO: We also set `event.message` here previously, check if it works without it as well
45+ getDefaultHub ( ) . configureScope ( scope => {
46+ scope . setExtra (
47+ '__serialized__' ,
48+ limitObjectDepthToSize ( exception as { } ) ,
49+ ) ;
50+ scope . setFingerprint ( [ md5 ( keys . join ( '' ) ) ] ) ;
51+ } ) ;
52+
53+ ex = new Error ( message ) ;
8254 } else {
83- // This "if" needs to be here in order for raven-node to propergate
84- // internal callbacks like in makeErrorHandler -> captureException
85- // correctly. Also the setTimeout is a dirty hack because in case of a
86- // fatal error, raven-node exits the process and our consturct of
87- // hub -> client doesn't have enough time to send the event.
88- if ( callback ) {
89- setTimeout ( ( ) => {
90- callback ( event ) ;
91- } , 1000 ) ;
92- }
93- captureEvent ( normalizeEvent ( event ) ) ;
55+ // This handles when someone does: `throw "something awesome";`
56+ // We synthesize an Error here so we can extract a (rough) stack trace.
57+ ex = new Error ( exception as string ) ;
9458 }
95- } ;
9659
97- return true ;
98- }
60+ stack = stacktrace . get ( ) ;
61+ }
9962
100- /**
101- * @inheritDoc
102- */
103- public async eventFromException ( exception : any ) : Promise < SentryEvent > {
104- return new Promise < SentryEvent > ( resolve => {
105- ( resolve as FunctionExt ) . __SENTRY_CAPTURE__ = true ;
106- Raven . captureException ( exception , resolve ) ;
107- } ) ;
63+ const event : SentryEvent = stack
64+ ? await parseError ( ex as Error , stack )
65+ : await parseError ( ex as Error ) ;
66+
67+ return event ;
10868 }
10969
11070 /**
11171 * @inheritDoc
11272 */
11373 public async eventFromMessage ( message : string ) : Promise < SentryEvent > {
114- return new Promise < SentryEvent > ( resolve => {
115- ( resolve as FunctionExt ) . __SENTRY_CAPTURE__ = true ;
116- Raven . captureMessage ( message , resolve ) ;
117- } ) ;
74+ const stack = stacktrace . get ( ) ;
75+ const frames = await parseStack ( stack ) ;
76+ const event : SentryEvent = {
77+ message,
78+ stacktrace : {
79+ frames : prepareFramesForEvent ( frames ) ,
80+ } ,
81+ } ;
82+ return event ;
11883 }
11984
12085 /**
0 commit comments