Skip to content

Commit 98d8260

Browse files
committed
fix: Async client calls were not captured by flush
1 parent 6216757 commit 98d8260

File tree

3 files changed

+91
-65
lines changed

3 files changed

+91
-65
lines changed

README.md

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ package. Please refer to the README and instructions of those SDKs for more deta
4545
- [`@sentry/electron`](https://github.com/getsentry/sentry-electron): SDK for Electron with support for native crashes
4646
- [`sentry-cordova`](https://github.com/getsentry/sentry-cordova): SDK for Cordova Apps and Ionic with support for
4747
native crashes
48-
- [`raven-js`](https://github.com/getsentry/sentry-javascript/tree/master/packages/raven-js): Our old stable Javascript
48+
- [`raven-js`](https://github.com/getsentry/sentry-javascript/tree/master/packages/raven-js): Our old stable JavaScript
4949
SDK, we still support and release bug fixes for the SDK but all new features will be implemented in `@sentry/browser`
5050
which is the successor.
5151
- [`raven`](https://github.com/getsentry/sentry-javascript/tree/master/packages/raven-node): Our old stable Node SDK,
@@ -74,18 +74,6 @@ init({
7474
captureMessage('Hello, world!');
7575
```
7676

77-
If you want sentry to be customized for the browsers you want to support use the `esm` build:
78-
79-
```javascript
80-
import { init, captureMessage } from '@sentry/browser/esm';
81-
```
82-
83-
and add it to your babel/... build, if you want to support older browsers
84-
85-
> TIP: You can use
86-
> [`<script type="module" src="newbrowser.js">` + `<script nomodule src="oldbrowser.js">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#Module_Fallback)
87-
> to provide builds for newer and older browsers
88-
8977
## Other Packages
9078

9179
Besides the high-level SDKs, this repository contains shared packages, helpers and configuration used for SDK

packages/core/src/baseclient.ts

Lines changed: 79 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
5353
/** Options passed to the SDK. */
5454
protected readonly _options: O;
5555

56-
/**
57-
* The client Dsn, if specified in options. Without this Dsn, the SDK will be
58-
* disabled.
59-
*/
56+
/** The client Dsn, if specified in options. Without this Dsn, the SDK will be disabled. */
6057
protected readonly _dsn?: Dsn;
6158

6259
/** Array of used integrations. */
6360
protected readonly _integrations: IntegrationIndex;
6461

62+
/** Is the client still processing a call? */
63+
protected _processing: boolean = false;
64+
6565
/**
6666
* Initializes this client instance.
6767
*
@@ -84,15 +84,19 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
8484
*/
8585
public captureException(exception: any, hint?: EventHint, scope?: Scope): string | undefined {
8686
let eventId: string | undefined = hint && hint.event_id;
87+
this._processing = true;
8788

8889
this._getBackend()
8990
.eventFromException(exception, hint)
9091
.then(event => this._processEvent(event, hint, scope))
9192
.then(finalEvent => {
92-
eventId = finalEvent.event_id;
93+
// We need to check for finalEvent in case beforeSend returned null
94+
eventId = finalEvent && finalEvent.event_id;
95+
this._processing = false;
9396
})
9497
.catch(reason => {
9598
logger.log(reason);
99+
this._processing = false;
96100
});
97101

98102
return eventId;
@@ -104,17 +108,22 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
104108
public captureMessage(message: string, level?: Severity, hint?: EventHint, scope?: Scope): string | undefined {
105109
let eventId: string | undefined = hint && hint.event_id;
106110

111+
this._processing = true;
112+
107113
const promisedEvent = isPrimitive(message)
108114
? this._getBackend().eventFromMessage(`${message}`, level, hint)
109115
: this._getBackend().eventFromException(message, hint);
110116

111117
promisedEvent
112118
.then(event => this._processEvent(event, hint, scope))
113119
.then(finalEvent => {
114-
eventId = finalEvent.event_id;
120+
// We need to check for finalEvent in case beforeSend returned null
121+
eventId = finalEvent && finalEvent.event_id;
122+
this._processing = false;
115123
})
116124
.catch(reason => {
117125
logger.log(reason);
126+
this._processing = false;
118127
});
119128

120129
return eventId;
@@ -125,12 +134,17 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
125134
*/
126135
public captureEvent(event: Event, hint?: EventHint, scope?: Scope): string | undefined {
127136
let eventId: string | undefined = hint && hint.event_id;
137+
this._processing = true;
138+
128139
this._processEvent(event, hint, scope)
129140
.then(finalEvent => {
130-
eventId = finalEvent.event_id;
141+
// We need to check for finalEvent in case beforeSend returned null
142+
eventId = finalEvent && finalEvent.event_id;
143+
this._processing = false;
131144
})
132145
.catch(reason => {
133146
logger.log(reason);
147+
this._processing = false;
134148
});
135149
return eventId;
136150
}
@@ -149,6 +163,64 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
149163
return this._options;
150164
}
151165

166+
/**
167+
* @inheritDoc
168+
*/
169+
public async flush(timeout?: number): Promise<boolean> {
170+
return (await Promise.all([
171+
this._getBackend()
172+
.getTransport()
173+
.close(timeout),
174+
this._isClientProcessing(),
175+
])).reduce((prev, current) => prev && current);
176+
}
177+
178+
/**
179+
* @inheritDoc
180+
*/
181+
public async close(timeout?: number): Promise<boolean> {
182+
return this.flush(timeout).finally(() => {
183+
this.getOptions().enabled = false;
184+
});
185+
}
186+
187+
/**
188+
* @inheritDoc
189+
*/
190+
public getIntegrations(): IntegrationIndex {
191+
return this._integrations || {};
192+
}
193+
194+
/**
195+
* @inheritDoc
196+
*/
197+
public getIntegration<T extends Integration>(integration: IntegrationClass<T>): T | null {
198+
try {
199+
return (this._integrations[integration.id] as T) || null;
200+
} catch (_oO) {
201+
logger.warn(`Cannot retrieve integration ${integration.id} from the current Client`);
202+
return null;
203+
}
204+
}
205+
206+
/** Waits for the client to be done with processing. */
207+
protected async _isClientProcessing(counter: number = 0): Promise<boolean> {
208+
return new Promise<boolean>(resolve => {
209+
if (this._processing) {
210+
// Safeguard in case of endless recursion
211+
if (counter >= 10) {
212+
resolve(false);
213+
} else {
214+
setTimeout(async () => {
215+
resolve(await this._isClientProcessing(counter + 1));
216+
}, 10);
217+
}
218+
} else {
219+
resolve(true);
220+
}
221+
});
222+
}
223+
152224
/** Returns the current backend. */
153225
protected _getBackend(): B {
154226
return this._backend;
@@ -315,41 +387,4 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
315387
reject(`beforeSend rejected with ${e}`);
316388
});
317389
}
318-
319-
/**
320-
* @inheritDoc
321-
*/
322-
public async flush(timeout?: number): Promise<boolean> {
323-
return this._getBackend()
324-
.getTransport()
325-
.close(timeout);
326-
}
327-
328-
/**
329-
* @inheritDoc
330-
*/
331-
public async close(timeout?: number): Promise<boolean> {
332-
return this.flush(timeout).finally(() => {
333-
this.getOptions().enabled = false;
334-
});
335-
}
336-
337-
/**
338-
* @inheritDoc
339-
*/
340-
public getIntegrations(): IntegrationIndex {
341-
return this._integrations || {};
342-
}
343-
344-
/**
345-
* @inheritDoc
346-
*/
347-
public getIntegration<T extends Integration>(integration: IntegrationClass<T>): T | null {
348-
try {
349-
return (this._integrations[integration.id] as T) || null;
350-
} catch (_oO) {
351-
logger.warn(`Cannot retrieve integration ${integration.id} from the current Client`);
352-
return null;
353-
}
354-
}
355390
}

packages/node/src/backend.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export class NodeBackend extends BaseBackend<NodeOptions> {
8181
handled: true,
8282
type: 'generic',
8383
};
84+
8485
if (!isError(exception)) {
8586
if (isPlainObject(exception)) {
8687
// This will allow us to group events based on top-level keys
@@ -102,14 +103,16 @@ export class NodeBackend extends BaseBackend<NodeOptions> {
102103
mechanism.synthetic = true;
103104
}
104105

105-
return new SyncPromise<Event>(resolve =>
106-
parseError(ex as Error, this._options).then(event => {
107-
addExceptionTypeValue(event, undefined, undefined, mechanism);
108-
resolve({
109-
...event,
110-
event_id: hint && hint.event_id,
111-
});
112-
}),
106+
return new SyncPromise<Event>((resolve, reject) =>
107+
parseError(ex as Error, this._options)
108+
.then(event => {
109+
addExceptionTypeValue(event, undefined, undefined, mechanism);
110+
resolve({
111+
...event,
112+
event_id: hint && hint.event_id,
113+
});
114+
})
115+
.catch(reject),
113116
);
114117
}
115118

0 commit comments

Comments
 (0)