diff --git a/packages/core/src/exports.ts b/packages/core/src/exports.ts index 09989215ef3e..1b8ed58a6f69 100644 --- a/packages/core/src/exports.ts +++ b/packages/core/src/exports.ts @@ -26,52 +26,51 @@ import type { Scope } from './scope'; import type { ExclusiveEventHintOrCaptureContext } from './utils/prepareEvent'; import { parseEventHintOrCaptureContext } from './utils/prepareEvent'; -// Note: All functions in this file are typed with a return value of `ReturnType`, -// where HUB_FUNCTION is some method on the Hub class. -// -// This is done to make sure the top level SDK methods stay in sync with the hub methods. -// Although every method here has an explicit return type, some of them (that map to void returns) do not -// contain `return` keywords. This is done to save on bundle size, as `return` is not minifiable. - /** * Captures an exception event and sends it to Sentry. - * This accepts an event hint as optional second parameter. - * Alternatively, you can also pass a CaptureContext directly as second parameter. + * + * @param exception The exception to capture. + * @param hint Optinal additional data to attach to the Sentry event. + * @returns the id of the captured Sentry event. */ export function captureException( // eslint-disable-next-line @typescript-eslint/no-explicit-any exception: any, hint?: ExclusiveEventHintOrCaptureContext, -): ReturnType { +): string { + // eslint-disable-next-line deprecation/deprecation return getCurrentHub().captureException(exception, parseEventHintOrCaptureContext(hint)); } /** * Captures a message event and sends it to Sentry. * - * @param message The message to send to Sentry. - * @param Severity Define the level of the message. - * @returns The generated eventId. + * @param exception The exception to capture. + * @param captureContext Define the level of the message or pass in additional data to attach to the message. + * @returns the id of the captured message. */ export function captureMessage( message: string, // eslint-disable-next-line deprecation/deprecation captureContext?: CaptureContext | Severity | SeverityLevel, -): ReturnType { +): string { // This is necessary to provide explicit scopes upgrade, without changing the original // arity of the `captureMessage(message, level)` method. const level = typeof captureContext === 'string' ? captureContext : undefined; const context = typeof captureContext !== 'string' ? { captureContext } : undefined; + // eslint-disable-next-line deprecation/deprecation return getCurrentHub().captureMessage(message, level, context); } /** * Captures a manually created event and sends it to Sentry. * - * @param event The event to send to Sentry. - * @returns The generated eventId. + * @param exception The event to send to Sentry. + * @param hint Optional additional data to attach to the Sentry event. + * @returns the id of the captured event. */ -export function captureEvent(event: Event, hint?: EventHint): ReturnType { +export function captureEvent(event: Event, hint?: EventHint): string { + // eslint-disable-next-line deprecation/deprecation return getCurrentHub().captureEvent(event, hint); } diff --git a/packages/core/src/hub.ts b/packages/core/src/hub.ts index d05b80410d06..d8258bc37e61 100644 --- a/packages/core/src/hub.ts +++ b/packages/core/src/hub.ts @@ -166,6 +166,7 @@ export class Hub implements HubInterface { public bindClient(client?: Client): void { const top = this.getStackTop(); top.client = client; + top.scope.setClient(client); if (client && client.setupIntegrations) { client.setupIntegrations(); } @@ -262,27 +263,26 @@ export class Hub implements HubInterface { /** * @inheritDoc + * + * @deprecated Use `Sentry.captureException()` instead. */ public captureException(exception: unknown, hint?: EventHint): string { const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4()); const syntheticException = new Error('Sentry syntheticException'); - this._withClient((client, scope) => { - client.captureException( - exception, - { - originalException: exception, - syntheticException, - ...hint, - event_id: eventId, - }, - scope, - ); + this.getScope().captureException(exception, { + originalException: exception, + syntheticException, + ...hint, + event_id: eventId, }); + return eventId; } /** * @inheritDoc + * + * @deprecated Use `Sentry.captureMessage()` instead. */ public captureMessage( message: string, @@ -292,24 +292,20 @@ export class Hub implements HubInterface { ): string { const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : uuid4()); const syntheticException = new Error(message); - this._withClient((client, scope) => { - client.captureMessage( - message, - level, - { - originalException: message, - syntheticException, - ...hint, - event_id: eventId, - }, - scope, - ); + this.getScope().captureMessage(message, level, { + originalException: message, + syntheticException, + ...hint, + event_id: eventId, }); + return eventId; } /** * @inheritDoc + * + * @deprecated Use `Sentry.captureEvent()` instead. */ public captureEvent(event: Event, hint?: EventHint): string { const eventId = hint && hint.event_id ? hint.event_id : uuid4(); @@ -317,9 +313,7 @@ export class Hub implements HubInterface { this._lastEventId = eventId; } - this._withClient((client, scope) => { - client.captureEvent(event, { ...hint, event_id: eventId }, scope); - }); + this.getScope().captureEvent(event, { ...hint, event_id: eventId }); return eventId; } diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index cff498bc85a3..8857329de3dd 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -24,7 +24,7 @@ import type { Transaction, User, } from '@sentry/types'; -import { dateTimestampInSeconds, isPlainObject, uuid4 } from '@sentry/utils'; +import { dateTimestampInSeconds, isPlainObject, logger, uuid4 } from '@sentry/utils'; import { getGlobalEventProcessors, notifyEventProcessors } from './eventProcessors'; import { updateSession } from './session'; @@ -581,6 +581,90 @@ export class Scope implements ScopeInterface { return this._propagationContext; } + /** + * Capture an exception for this scope. + * + * @param exception The exception to capture. + * @param hint Optinal additional data to attach to the Sentry event. + * @returns the id of the captured Sentry event. + */ + public captureException(exception: unknown, hint?: EventHint): string { + const eventId = hint && hint.event_id ? hint.event_id : uuid4(); + + if (!this._client) { + logger.warn('No client configured on scope - will not capture exception!'); + return eventId; + } + + const syntheticException = new Error('Sentry syntheticException'); + + this._client.captureException( + exception, + { + originalException: exception, + syntheticException, + ...hint, + event_id: eventId, + }, + this, + ); + + return eventId; + } + + /** + * Capture a message for this scope. + * + * @param message The message to capture. + * @param level An optional severity level to report the message with. + * @param hint Optional additional data to attach to the Sentry event. + * @returns the id of the captured message. + */ + public captureMessage(message: string, level?: SeverityLevel, hint?: EventHint): string { + const eventId = hint && hint.event_id ? hint.event_id : uuid4(); + + if (!this._client) { + logger.warn('No client configured on scope - will not capture message!'); + return eventId; + } + + const syntheticException = new Error(message); + + this._client.captureMessage( + message, + level, + { + originalException: message, + syntheticException, + ...hint, + event_id: eventId, + }, + this, + ); + + return eventId; + } + + /** + * Captures a manually created event for this scope and sends it to Sentry. + * + * @param exception The event to capture. + * @param hint Optional additional data to attach to the Sentry event. + * @returns the id of the captured event. + */ + public captureEvent(event: Event, hint?: EventHint): string { + const eventId = hint && hint.event_id ? hint.event_id : uuid4(); + + if (!this._client) { + logger.warn('No client configured on scope - will not capture event!'); + return eventId; + } + + this._client.captureEvent(event, { ...hint, event_id: eventId }, this); + + return eventId; + } + /** * This will be called on every set call. */ diff --git a/packages/core/src/tracing/transaction.ts b/packages/core/src/tracing/transaction.ts index 7142ec3419e7..adee0b116afe 100644 --- a/packages/core/src/tracing/transaction.ts +++ b/packages/core/src/tracing/transaction.ts @@ -153,6 +153,7 @@ export class Transaction extends SpanClass implements TransactionInterface { if (!transaction) { return undefined; } + // eslint-disable-next-line deprecation/deprecation return this._hub.captureEvent(transaction); } diff --git a/packages/core/test/lib/integrations/metadata.test.ts b/packages/core/test/lib/integrations/metadata.test.ts index 15678a66fdb6..7e8bfcea9fa4 100644 --- a/packages/core/test/lib/integrations/metadata.test.ts +++ b/packages/core/test/lib/integrations/metadata.test.ts @@ -61,6 +61,7 @@ describe('ModuleMetadata integration', () => { const client = new TestClient(options); const hub = getCurrentHub(); hub.bindClient(client); + // eslint-disable-next-line deprecation/deprecation hub.captureException(new Error('Some error')); }); }); diff --git a/packages/core/test/lib/scope.test.ts b/packages/core/test/lib/scope.test.ts index 3122f3b3e3e5..87af429d0859 100644 --- a/packages/core/test/lib/scope.test.ts +++ b/packages/core/test/lib/scope.test.ts @@ -1,4 +1,4 @@ -import type { Attachment, Breadcrumb, Client } from '@sentry/types'; +import type { Attachment, Breadcrumb, Client, Event } from '@sentry/types'; import { applyScopeDataToEvent } from '../../src'; import { Scope, getGlobalScope, setGlobalScope } from '../../src/scope'; @@ -212,4 +212,256 @@ describe('Scope', () => { expect(clonedScope.getClient()).toBe(fakeClient); }); }); + + describe('.captureException()', () => { + it('should call captureException() on client with newly generated event ID if not explicitly passed in', () => { + const fakeCaptureException = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureException: fakeCaptureException, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + const exception = new Error(); + + scope.captureException(exception); + + expect(fakeCaptureException).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ event_id: expect.any(String) }), + scope, + ); + }); + + it('should return event ID when no client is on the scope', () => { + const scope = new Scope(); + + const exception = new Error(); + + const eventId = scope.captureException(exception); + + expect(eventId).toEqual(expect.any(String)); + }); + + it('should pass exception to captureException() on client', () => { + const fakeCaptureException = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureException: fakeCaptureException, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + const exception = new Error(); + + scope.captureException(exception); + + expect(fakeCaptureException).toHaveBeenCalledWith(exception, expect.anything(), scope); + }); + + it('should call captureException() on client with a synthetic exception', () => { + const fakeCaptureException = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureException: fakeCaptureException, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + scope.captureException(new Error()); + + expect(fakeCaptureException).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ syntheticException: expect.any(Error) }), + scope, + ); + }); + + it('should pass the original exception to captureException() on client', () => { + const fakeCaptureException = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureException: fakeCaptureException, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + const exception = new Error(); + scope.captureException(exception); + + expect(fakeCaptureException).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ originalException: exception }), + scope, + ); + }); + + it('should forward hint to captureException() on client', () => { + const fakeCaptureException = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureException: fakeCaptureException, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + scope.captureException(new Error(), { event_id: 'asdf', data: { foo: 'bar' } }); + + expect(fakeCaptureException).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ event_id: 'asdf', data: { foo: 'bar' } }), + scope, + ); + }); + }); + + describe('.captureMessage()', () => { + it('should call captureMessage() on client with newly generated event ID if not explicitly passed in', () => { + const fakeCaptureMessage = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureMessage: fakeCaptureMessage, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + scope.captureMessage('foo'); + + expect(fakeCaptureMessage).toHaveBeenCalledWith( + expect.anything(), + undefined, + expect.objectContaining({ event_id: expect.any(String) }), + scope, + ); + }); + + it('should return event ID when no client is on the scope', () => { + const scope = new Scope(); + + const eventId = scope.captureMessage('foo'); + + expect(eventId).toEqual(expect.any(String)); + }); + + it('should pass exception to captureMessage() on client', () => { + const fakeCaptureMessage = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureMessage: fakeCaptureMessage, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + scope.captureMessage('bar'); + + expect(fakeCaptureMessage).toHaveBeenCalledWith('bar', undefined, expect.anything(), scope); + }); + + it('should call captureMessage() on client with a synthetic exception', () => { + const fakeCaptureMessage = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureMessage: fakeCaptureMessage, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + scope.captureMessage('foo'); + + expect(fakeCaptureMessage).toHaveBeenCalledWith( + expect.anything(), + undefined, + expect.objectContaining({ syntheticException: expect.any(Error) }), + scope, + ); + }); + + it('should pass the original exception to captureMessage() on client', () => { + const fakeCaptureMessage = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureMessage: fakeCaptureMessage, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + scope.captureMessage('baz'); + + expect(fakeCaptureMessage).toHaveBeenCalledWith( + expect.anything(), + undefined, + expect.objectContaining({ originalException: 'baz' }), + scope, + ); + }); + + it('should forward level and hint to captureMessage() on client', () => { + const fakeCaptureMessage = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureMessage: fakeCaptureMessage, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + scope.captureMessage('asdf', 'fatal', { event_id: 'asdf', data: { foo: 'bar' } }); + + expect(fakeCaptureMessage).toHaveBeenCalledWith( + expect.anything(), + 'fatal', + expect.objectContaining({ event_id: 'asdf', data: { foo: 'bar' } }), + scope, + ); + }); + }); + + describe('.captureEvent()', () => { + it('should call captureEvent() on client with newly generated event ID if not explicitly passed in', () => { + const fakeCaptureEvent = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureEvent: fakeCaptureEvent, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + scope.captureEvent({}); + + expect(fakeCaptureEvent).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ event_id: expect.any(String) }), + scope, + ); + }); + + it('should return event ID when no client is on the scope', () => { + const scope = new Scope(); + + const eventId = scope.captureEvent({}); + + expect(eventId).toEqual(expect.any(String)); + }); + + it('should pass event to captureEvent() on client', () => { + const fakeCaptureEvent = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureEvent: fakeCaptureEvent, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + const event: Event = { event_id: 'asdf' }; + + scope.captureEvent(event); + + expect(fakeCaptureEvent).toHaveBeenCalledWith(event, expect.anything(), scope); + }); + + it('should forward hint to captureEvent() on client', () => { + const fakeCaptureEvent = jest.fn(() => 'mock-event-id'); + const fakeClient = { + captureEvent: fakeCaptureEvent, + } as unknown as Client; + const scope = new Scope(); + scope.setClient(fakeClient); + + scope.captureEvent({}, { event_id: 'asdf', data: { foo: 'bar' } }); + + expect(fakeCaptureEvent).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ event_id: 'asdf', data: { foo: 'bar' } }), + scope, + ); + }); + }); }); diff --git a/packages/deno/test/__snapshots__/mod.test.ts.snap b/packages/deno/test/__snapshots__/mod.test.ts.snap index f278e370312b..607d87b968bc 100644 --- a/packages/deno/test/__snapshots__/mod.test.ts.snap +++ b/packages/deno/test/__snapshots__/mod.test.ts.snap @@ -77,8 +77,8 @@ snapshot[`captureException 1`] = ` lineno: 526, }, { - colno: 24, - context_line: " hub.captureException(something());", + colno: 27, + context_line: " client.captureException(something());", filename: "app:///test/mod.test.ts", function: "", in_app: true, @@ -112,7 +112,7 @@ snapshot[`captureException 1`] = ` post_context: [ " }", "", - " hub.captureException(something());", + " client.captureException(something());", "", " await delay(200);", " await assertSnapshot(t, ev);", @@ -121,7 +121,7 @@ snapshot[`captureException 1`] = ` pre_context: [ "Deno.test('captureException', async t => {", " let ev: sentryTypes.Event | undefined;", - " const [hub] = getTestClient(event => {", + " const [, client] = getTestClient(event => {", " ev = event;", " });", "", diff --git a/packages/deno/test/mod.test.ts b/packages/deno/test/mod.test.ts index a14e04b61ff9..1568584d8281 100644 --- a/packages/deno/test/mod.test.ts +++ b/packages/deno/test/mod.test.ts @@ -35,7 +35,7 @@ function delay(time: number): Promise { Deno.test('captureException', async t => { let ev: sentryTypes.Event | undefined; - const [hub] = getTestClient(event => { + const [, client] = getTestClient(event => { ev = event; }); @@ -43,7 +43,7 @@ Deno.test('captureException', async t => { return new Error('Some unhandled error'); } - hub.captureException(something()); + client.captureException(something()); await delay(200); await assertSnapshot(t, ev); @@ -51,11 +51,11 @@ Deno.test('captureException', async t => { Deno.test('captureMessage', async t => { let ev: sentryTypes.Event | undefined; - const [hub] = getTestClient(event => { + const [, client] = getTestClient(event => { ev = event; }); - hub.captureMessage('Some error message'); + client.captureMessage('Some error message'); await delay(200); await assertSnapshot(t, ev); diff --git a/packages/node-experimental/src/sdk/types.ts b/packages/node-experimental/src/sdk/types.ts index 90c61dceda86..2563989f474f 100644 --- a/packages/node-experimental/src/sdk/types.ts +++ b/packages/node-experimental/src/sdk/types.ts @@ -2,8 +2,6 @@ import type { Attachment, Breadcrumb, Contexts, - Event, - EventHint, EventProcessor, Extras, Hub, @@ -11,7 +9,6 @@ import type { Primitive, PropagationContext, Scope as BaseScope, - Severity, SeverityLevel, User, } from '@sentry/types'; @@ -35,14 +32,6 @@ export interface Scope extends BaseScope { isolationScope: typeof this | undefined; // @ts-expect-error typeof this is what we want here clone(scope?: Scope): typeof this; - captureException(exception: unknown, hint?: EventHint): string; - captureMessage( - message: string, - // eslint-disable-next-line deprecation/deprecation - level?: Severity | SeverityLevel, - hint?: EventHint, - ): string; - captureEvent(event: Event, hint?: EventHint): string; lastEventId(): string | undefined; getScopeData(): ScopeData; } diff --git a/packages/node-experimental/test/integration/breadcrumbs.test.ts b/packages/node-experimental/test/integration/breadcrumbs.test.ts index fea78a353011..c576cc85b11a 100644 --- a/packages/node-experimental/test/integration/breadcrumbs.test.ts +++ b/packages/node-experimental/test/integration/breadcrumbs.test.ts @@ -27,6 +27,7 @@ describe('Integration | breadcrumbs', () => { hub.addBreadcrumb({ timestamp: 123455, message: 'test3' }); const error = new Error('test'); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); await client.flush(); @@ -118,6 +119,7 @@ describe('Integration | breadcrumbs', () => { hub.addBreadcrumb({ timestamp: 123455, message: 'test3' }); }); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); }); @@ -171,6 +173,7 @@ describe('Integration | breadcrumbs', () => { hub.addBreadcrumb({ timestamp: 123457, message: 'test2-b' }); }); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); }); }); @@ -214,6 +217,7 @@ describe('Integration | breadcrumbs', () => { hub.addBreadcrumb({ timestamp: 123457, message: 'test2' }); }); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); }); @@ -261,6 +265,7 @@ describe('Integration | breadcrumbs', () => { startSpan({ name: 'inner3' }, () => { hub.addBreadcrumb({ timestamp: 123457, message: 'test4' }); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); startSpan({ name: 'inner4' }, () => { @@ -321,6 +326,7 @@ describe('Integration | breadcrumbs', () => { await new Promise(resolve => setTimeout(resolve, 10)); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); }); }); diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts index 14de421db5f7..3a3ba161098a 100644 --- a/packages/node/test/handlers.test.ts +++ b/packages/node/test/handlers.test.ts @@ -586,6 +586,7 @@ describe('errorHandler()', () => { // `sentryErrorMiddleware` uses `withScope`, and we need access to the temporary scope it creates, so monkeypatch // `captureException` in order to examine the scope as it exists inside the `withScope` callback + // eslint-disable-next-line deprecation/deprecation hub.captureException = function (this: sentryCore.Hub, _exception: any) { const scope = this.getScope(); expect((scope as any)._sdkProcessingMetadata.request).toEqual(req); diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 30658128d2b4..c2e003b14d94 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -288,6 +288,7 @@ describe('SentryNode', () => { hub.bindClient(client); expect(getCurrentHub().getClient()).toBe(client); expect(getClient()).toBe(client); + // eslint-disable-next-line deprecation/deprecation hub.captureEvent({ message: 'test domain' }); }); }); diff --git a/packages/opentelemetry-node/test/spanprocessor.test.ts b/packages/opentelemetry-node/test/spanprocessor.test.ts index 69ef554c132c..e9eb7b76f4db 100644 --- a/packages/opentelemetry-node/test/spanprocessor.test.ts +++ b/packages/opentelemetry-node/test/spanprocessor.test.ts @@ -888,6 +888,7 @@ describe('SentrySpanProcessor', () => { tracer.startActiveSpan('GET /users', parentOtelSpan => { tracer.startActiveSpan('SELECT * FROM users;', child => { + // eslint-disable-next-line deprecation/deprecation hub.captureException(new Error('oh nooooo!')); otelSpan = child as OtelSpan; child.end(); diff --git a/packages/opentelemetry/src/custom/scope.ts b/packages/opentelemetry/src/custom/scope.ts index c6bdfb164900..2455d616ff39 100644 --- a/packages/opentelemetry/src/custom/scope.ts +++ b/packages/opentelemetry/src/custom/scope.ts @@ -46,6 +46,7 @@ export class OpenTelemetryScope extends Scope { newScope._attachments = [...this['_attachments']]; newScope._sdkProcessingMetadata = { ...this['_sdkProcessingMetadata'] }; newScope._propagationContext = { ...this['_propagationContext'] }; + newScope._client = this._client; return newScope; } diff --git a/packages/opentelemetry/test/integration/breadcrumbs.test.ts b/packages/opentelemetry/test/integration/breadcrumbs.test.ts index af095be83f76..096f31c6e9bf 100644 --- a/packages/opentelemetry/test/integration/breadcrumbs.test.ts +++ b/packages/opentelemetry/test/integration/breadcrumbs.test.ts @@ -29,6 +29,7 @@ describe('Integration | breadcrumbs', () => { hub.addBreadcrumb({ timestamp: 123455, message: 'test3' }); const error = new Error('test'); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); await client.flush(); @@ -73,6 +74,7 @@ describe('Integration | breadcrumbs', () => { withScope(() => { hub.addBreadcrumb({ timestamp: 123456, message: 'test2' }); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); }); @@ -123,6 +125,7 @@ describe('Integration | breadcrumbs', () => { hub.addBreadcrumb({ timestamp: 123455, message: 'test3' }); }); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); }); @@ -173,6 +176,7 @@ describe('Integration | breadcrumbs', () => { hub.addBreadcrumb({ timestamp: 123457, message: 'test2-b' }); }); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); }); @@ -215,6 +219,7 @@ describe('Integration | breadcrumbs', () => { hub.addBreadcrumb({ timestamp: 123457, message: 'test2' }); }); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); }); @@ -262,6 +267,7 @@ describe('Integration | breadcrumbs', () => { startSpan({ name: 'inner3' }, () => { hub.addBreadcrumb({ timestamp: 123457, message: 'test4' }); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); startSpan({ name: 'inner4' }, () => { @@ -321,6 +327,7 @@ describe('Integration | breadcrumbs', () => { await new Promise(resolve => setTimeout(resolve, 10)); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); }); diff --git a/packages/opentelemetry/test/utils/setupEventContextTrace.test.ts b/packages/opentelemetry/test/utils/setupEventContextTrace.test.ts index 887a7f6bc803..a73067581b9a 100644 --- a/packages/opentelemetry/test/utils/setupEventContextTrace.test.ts +++ b/packages/opentelemetry/test/utils/setupEventContextTrace.test.ts @@ -45,6 +45,7 @@ describe('setupEventContextTrace', () => { it('works with no active span', async () => { const error = new Error('test'); + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); await client.flush(); @@ -79,6 +80,7 @@ describe('setupEventContextTrace', () => { client.tracer.startActiveSpan('inner', innerSpan => { innerId = innerSpan?.spanContext().spanId; + // eslint-disable-next-line deprecation/deprecation hub.captureException(error); }); }); diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts index 5dbdee05260d..7cbe7ff264b5 100644 --- a/packages/types/src/scope.ts +++ b/packages/types/src/scope.ts @@ -2,6 +2,7 @@ import type { Attachment } from './attachment'; import type { Breadcrumb } from './breadcrumb'; import type { Client } from './client'; import type { Context, Contexts } from './context'; +import type { Event, EventHint } from './event'; import type { EventProcessor } from './eventprocessor'; import type { Extra, Extras } from './extra'; import type { Primitive } from './misc'; @@ -229,4 +230,32 @@ export interface Scope { * Get propagation context from the scope, used for distributed tracing */ getPropagationContext(): PropagationContext; + + /** + * Capture an exception for this scope. + * + * @param exception The exception to capture. + * @param hint Optinal additional data to attach to the Sentry event. + * @returns the id of the captured Sentry event. + */ + captureException(exception: unknown, hint?: EventHint): string; + + /** + * Capture a message for this scope. + * + * @param exception The exception to capture. + * @param level An optional severity level to report the message with. + * @param hint Optional additional data to attach to the Sentry event. + * @returns the id of the captured message. + */ + captureMessage(message: string, level?: SeverityLevel, hint?: EventHint): string; + + /** + * Capture a Sentry event for this scope. + * + * @param exception The event to capture. + * @param hint Optional additional data to attach to the Sentry event. + * @returns the id of the captured event. + */ + captureEvent(event: Event, hint?: EventHint): string; } diff --git a/packages/vue/src/errorhandler.ts b/packages/vue/src/errorhandler.ts index 9d09bdf8c181..725f9b56c714 100644 --- a/packages/vue/src/errorhandler.ts +++ b/packages/vue/src/errorhandler.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/browser'; +import { captureException } from '@sentry/core'; import { consoleSandbox } from '@sentry/utils'; import type { ViewModel, Vue, VueOptions } from './types'; @@ -30,7 +30,7 @@ export const attachErrorHandler = (app: Vue, options: VueOptions): void => { // Capture exception in the next event loop, to make sure that all breadcrumbs are recorded in time. setTimeout(() => { - getCurrentHub().captureException(error, { + captureException(error, { captureContext: { contexts: { vue: metadata } }, mechanism: { handled: false }, });