Skip to content

Commit 48995b1

Browse files
author
Luca Forstner
authored
ref: Always return an immediately generated event ID from captureException(), captureMessage(), and captureEvent() (#11805)
1 parent 79818ff commit 48995b1

File tree

7 files changed

+66
-48
lines changed

7 files changed

+66
-48
lines changed

MIGRATION.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,7 @@ Sentry.init({
11191119
- [Updated behaviour of `transactionContext` passed to `tracesSampler`](./MIGRATION.md#transactioncontext-no-longer-passed-to-tracessampler)
11201120
- [Updated behaviour of `getClient()`](./MIGRATION.md#getclient-always-returns-a-client)
11211121
- [Updated behaviour of the SDK in combination with `onUncaughtException` handlers in Node.js](./MIGRATION.md#behaviour-in-combination-with-onuncaughtexception-handlers-in-node.js)
1122+
- [Updated expected return value for `captureException()`, `captureMessage()` and `captureEvent` methods on Clients](./MIGRATION.md#updated-expected-return-value-for-captureexception-capturemessage-and-captureevent-methods-on-clients)
11221123
- [Removal of Client-Side health check transaction filters](./MIGRATION.md#removal-of-client-side-health-check-transaction-filters)
11231124
- [Change of Replay default options (`unblock` and `unmask`)](./MIGRATION.md#change-of-replay-default-options-unblock-and-unmask)
11241125
- [Angular Tracing Decorator renaming](./MIGRATION.md#angular-tracing-decorator-renaming)
@@ -1179,6 +1180,11 @@ for this option defaulted to `true`.
11791180
Going forward, the default value for `exitEvenIfOtherHandlersAreRegistered` will be `false`, meaning that the SDK will
11801181
not exit your process when you have registered other `onUncaughtException` handlers.
11811182

1183+
#### Updated expected return value for `captureException()`, `captureMessage()` and `captureEvent` methods on Clients
1184+
1185+
The `Client` interface now expects implementations to always return a string representing the generated event ID for the
1186+
`captureException()`, `captureMessage()`, `captureEvent()` methods. Previously `undefined` was a valid return value.
1187+
11821188
#### Removal of Client-Side health check transaction filters
11831189

11841190
The SDK no longer filters out health check transactions by default. Instead, they are sent to Sentry but still dropped

packages/core/src/baseclient.ts

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import type {
1717
Integration,
1818
Outcome,
1919
ParameterizedString,
20-
Scope,
2120
SdkMetadata,
2221
Session,
2322
SessionAggregates,
@@ -44,6 +43,7 @@ import {
4443
makeDsn,
4544
rejectedSyncPromise,
4645
resolvedSyncPromise,
46+
uuid4,
4747
} from '@sentry/utils';
4848

4949
import { getEnvelopeEndpointWithUrlEncodedAuth } from './api';
@@ -53,6 +53,7 @@ import { createEventEnvelope, createSessionEnvelope } from './envelope';
5353
import type { IntegrationIndex } from './integration';
5454
import { afterSetupIntegrations } from './integration';
5555
import { setupIntegration, setupIntegrations } from './integration';
56+
import type { Scope } from './scope';
5657
import { updateSession } from './session';
5758
import { getDynamicSamplingContextFromClient } from './tracing/dynamicSamplingContext';
5859
import { parseSampleRate } from './utils/parseSampleRate';
@@ -151,24 +152,27 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
151152
* @inheritDoc
152153
*/
153154
// eslint-disable-next-line @typescript-eslint/no-explicit-any
154-
public captureException(exception: any, hint?: EventHint, currentScope?: Scope): string | undefined {
155+
public captureException(exception: any, hint?: EventHint, scope?: Scope): string {
156+
const eventId = uuid4();
157+
155158
// ensure we haven't captured this very object before
156159
if (checkOrSetAlreadyCaught(exception)) {
157160
DEBUG_BUILD && logger.log(ALREADY_SEEN_ERROR);
158-
return;
161+
return eventId;
159162
}
160163

161-
let eventId: string | undefined = hint && hint.event_id;
164+
const hintWithEventId = {
165+
event_id: eventId,
166+
...hint,
167+
};
162168

163169
this._process(
164-
this.eventFromException(exception, hint)
165-
.then(event => this._captureEvent(event, hint, currentScope))
166-
.then(result => {
167-
eventId = result;
168-
}),
170+
this.eventFromException(exception, hintWithEventId).then(event =>
171+
this._captureEvent(event, hintWithEventId, scope),
172+
),
169173
);
170174

171-
return eventId;
175+
return hintWithEventId.event_id;
172176
}
173177

174178
/**
@@ -179,48 +183,46 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
179183
level?: SeverityLevel,
180184
hint?: EventHint,
181185
currentScope?: Scope,
182-
): string | undefined {
183-
let eventId: string | undefined = hint && hint.event_id;
186+
): string {
187+
const hintWithEventId = {
188+
event_id: uuid4(),
189+
...hint,
190+
};
184191

185192
const eventMessage = isParameterizedString(message) ? message : String(message);
186193

187194
const promisedEvent = isPrimitive(message)
188-
? this.eventFromMessage(eventMessage, level, hint)
189-
: this.eventFromException(message, hint);
195+
? this.eventFromMessage(eventMessage, level, hintWithEventId)
196+
: this.eventFromException(message, hintWithEventId);
190197

191-
this._process(
192-
promisedEvent
193-
.then(event => this._captureEvent(event, hint, currentScope))
194-
.then(result => {
195-
eventId = result;
196-
}),
197-
);
198+
this._process(promisedEvent.then(event => this._captureEvent(event, hintWithEventId, currentScope)));
198199

199-
return eventId;
200+
return hintWithEventId.event_id;
200201
}
201202

202203
/**
203204
* @inheritDoc
204205
*/
205-
public captureEvent(event: Event, hint?: EventHint, currentScope?: Scope): string | undefined {
206+
public captureEvent(event: Event, hint?: EventHint, currentScope?: Scope): string {
207+
const eventId = uuid4();
208+
206209
// ensure we haven't captured this very object before
207210
if (hint && hint.originalException && checkOrSetAlreadyCaught(hint.originalException)) {
208211
DEBUG_BUILD && logger.log(ALREADY_SEEN_ERROR);
209-
return;
212+
return eventId;
210213
}
211214

212-
let eventId: string | undefined = hint && hint.event_id;
215+
const hintWithEventId = {
216+
event_id: eventId,
217+
...hint,
218+
};
213219

214220
const sdkProcessingMetadata = event.sdkProcessingMetadata || {};
215221
const capturedSpanScope: Scope | undefined = sdkProcessingMetadata.capturedSpanScope;
216222

217-
this._process(
218-
this._captureEvent(event, hint, capturedSpanScope || currentScope).then(result => {
219-
eventId = result;
220-
}),
221-
);
223+
this._process(this._captureEvent(event, hintWithEventId, capturedSpanScope || currentScope));
222224

223-
return eventId;
225+
return hintWithEventId.event_id;
224226
}
225227

226228
/**

packages/core/src/server-runtime-client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class ServerRuntimeClient<
7676
* @inheritDoc
7777
*/
7878
// eslint-disable-next-line @typescript-eslint/no-explicit-any
79-
public captureException(exception: any, hint?: EventHint, scope?: Scope): string | undefined {
79+
public captureException(exception: any, hint?: EventHint, scope?: Scope): string {
8080
// Check if the flag `autoSessionTracking` is enabled, and if `_sessionFlusher` exists because it is initialised only
8181
// when the `requestHandler` middleware is used, and hence the expectation is to have SessionAggregates payload
8282
// sent to the Server only when the `requestHandler` middleware is used
@@ -96,7 +96,7 @@ export class ServerRuntimeClient<
9696
/**
9797
* @inheritDoc
9898
*/
99-
public captureEvent(event: Event, hint?: EventHint, scope?: Scope): string | undefined {
99+
public captureEvent(event: Event, hint?: EventHint, scope?: Scope): string {
100100
// Check if the flag `autoSessionTracking` is enabled, and if `_sessionFlusher` exists because it is initialised only
101101
// when the `requestHandler` middleware is used, and hence the expectation is to have SessionAggregates payload
102102
// sent to the Server only when the `requestHandler` middleware is used

packages/core/test/lib/hint.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ describe('Hint', () => {
3636
client.captureEvent({});
3737

3838
const [, hint] = sendEvent.mock.calls[0];
39-
expect(hint).toEqual({ attachments: [{ filename: 'another.file', data: 'more text' }] });
39+
expect(hint).toMatchObject({ attachments: [{ filename: 'another.file', data: 'more text' }] });
4040
});
4141

4242
test('gets passed through to `beforeSend` and can be further mutated', () => {
@@ -54,7 +54,7 @@ describe('Hint', () => {
5454
client.captureEvent({}, { attachments: [{ filename: 'some-file.txt', data: 'Hello' }] });
5555

5656
const [, hint] = sendEvent.mock.calls[0];
57-
expect(hint).toEqual({
57+
expect(hint).toMatchObject({
5858
attachments: [
5959
{ filename: 'some-file.txt', data: 'Hello' },
6060
{ filename: 'another.file', data: 'more text' },
@@ -80,7 +80,7 @@ describe('Hint', () => {
8080
);
8181

8282
const [, hint] = sendEvent.mock.calls[0];
83-
expect(hint).toEqual({
83+
expect(hint).toMatchObject({
8484
attachments: [
8585
{ filename: 'some-file.txt', data: 'Hello' },
8686
{ filename: 'another.file', data: 'more text' },

packages/core/test/lib/integration.test.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,16 @@ describe('setupIntegration', () => {
457457
expect(integration3.preprocessEvent).toHaveBeenCalledTimes(3);
458458
expect(integration4.preprocessEvent).toHaveBeenCalledTimes(0);
459459

460-
expect(integration1.preprocessEvent).toHaveBeenLastCalledWith({ event_id: '1b' }, {}, client1);
461-
expect(integration3.preprocessEvent).toHaveBeenLastCalledWith({ event_id: '2c' }, {}, client2);
460+
expect(integration1.preprocessEvent).toHaveBeenLastCalledWith(
461+
{ event_id: '1b' },
462+
{ event_id: expect.any(String) },
463+
client1,
464+
);
465+
expect(integration3.preprocessEvent).toHaveBeenLastCalledWith(
466+
{ event_id: '2c' },
467+
{ event_id: expect.any(String) },
468+
client2,
469+
);
462470
});
463471

464472
it('allows to mutate events in preprocessEvent', async () => {
@@ -484,7 +492,9 @@ describe('setupIntegration', () => {
484492
await client.flush();
485493

486494
expect(sendEvent).toHaveBeenCalledTimes(1);
487-
expect(sendEvent).toHaveBeenCalledWith(expect.objectContaining({ event_id: 'mutated' }), {});
495+
expect(sendEvent).toHaveBeenCalledWith(expect.objectContaining({ event_id: 'mutated' }), {
496+
event_id: expect.any(String),
497+
});
488498
});
489499

490500
it('binds processEvent for each client', () => {
@@ -531,12 +541,12 @@ describe('setupIntegration', () => {
531541

532542
expect(integration1.processEvent).toHaveBeenLastCalledWith(
533543
expect.objectContaining({ event_id: '1b' }),
534-
{},
544+
{ event_id: expect.any(String) },
535545
client1,
536546
);
537547
expect(integration3.processEvent).toHaveBeenLastCalledWith(
538548
expect.objectContaining({ event_id: '2c' }),
539-
{},
549+
{ event_id: expect.any(String) },
540550
client2,
541551
);
542552
});
@@ -564,7 +574,9 @@ describe('setupIntegration', () => {
564574
await client.flush();
565575

566576
expect(sendEvent).toHaveBeenCalledTimes(1);
567-
expect(sendEvent).toHaveBeenCalledWith(expect.objectContaining({ event_id: 'mutated' }), {});
577+
expect(sendEvent).toHaveBeenCalledWith(expect.objectContaining({ event_id: 'mutated' }), {
578+
event_id: expect.any(String),
579+
});
568580
});
569581

570582
it('allows to drop events in processEvent', async () => {

packages/nextjs/test/clientSdk.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BaseClient, getGlobalScope, getIsolationScope } from '@sentry/core';
1+
import { getGlobalScope, getIsolationScope } from '@sentry/core';
22
import * as SentryReact from '@sentry/react';
33
import type { BrowserClient } from '@sentry/react';
44
import { WINDOW, getClient, getCurrentScope } from '@sentry/react';
@@ -9,7 +9,6 @@ import { JSDOM } from 'jsdom';
99
import { breadcrumbsIntegration, browserTracingIntegration, init } from '../src/client';
1010

1111
const reactInit = jest.spyOn(SentryReact, 'init');
12-
const captureEvent = jest.spyOn(BaseClient.prototype, 'captureEvent');
1312
const loggerLogSpy = jest.spyOn(logger, 'log');
1413

1514
// We're setting up JSDom here because the Next.js routing instrumentations requires a few things to be present on pageload:
@@ -96,7 +95,6 @@ describe('Client init()', () => {
9695
});
9796

9897
expect(transportSend).not.toHaveBeenCalled();
99-
expect(captureEvent.mock.results[0].value).toBeUndefined();
10098
expect(loggerLogSpy).toHaveBeenCalledWith('An event processor returned `null`, will not send event.');
10199
});
102100

packages/types/src/client.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface Client<O extends ClientOptions = ClientOptions> {
3838
* @param currentScope An optional scope containing event metadata.
3939
* @returns The event id
4040
*/
41-
captureException(exception: any, hint?: EventHint, currentScope?: Scope): string | undefined;
41+
captureException(exception: any, hint?: EventHint, currentScope?: Scope): string;
4242

4343
/**
4444
* Captures a message event and sends it to Sentry.
@@ -51,7 +51,7 @@ export interface Client<O extends ClientOptions = ClientOptions> {
5151
* @param currentScope An optional scope containing event metadata.
5252
* @returns The event id
5353
*/
54-
captureMessage(message: string, level?: SeverityLevel, hint?: EventHint, currentScope?: Scope): string | undefined;
54+
captureMessage(message: string, level?: SeverityLevel, hint?: EventHint, currentScope?: Scope): string;
5555

5656
/**
5757
* Captures a manually created event and sends it to Sentry.
@@ -63,7 +63,7 @@ export interface Client<O extends ClientOptions = ClientOptions> {
6363
* @param currentScope An optional scope containing event metadata.
6464
* @returns The event id
6565
*/
66-
captureEvent(event: Event, hint?: EventHint, currentScope?: Scope): string | undefined;
66+
captureEvent(event: Event, hint?: EventHint, currentScope?: Scope): string;
6767

6868
/**
6969
* Captures a session

0 commit comments

Comments
 (0)