diff --git a/packages/core/src/server-runtime-client.ts b/packages/core/src/server-runtime-client.ts index 4165aec8fc46..719e2b81f086 100644 --- a/packages/core/src/server-runtime-client.ts +++ b/packages/core/src/server-runtime-client.ts @@ -16,7 +16,7 @@ import { eventFromMessage, eventFromUnknownInput, logger, resolvedSyncPromise, u import { BaseClient } from './baseclient'; import { createCheckInEnvelope } from './checkin'; import { DEBUG_BUILD } from './debug-build'; -import { getCurrentHub } from './hub'; +import { getClient } from './exports'; import type { Scope } from './scope'; import { SessionFlusher } from './sessionflusher'; import { addTracingExtensions, getDynamicSamplingContextFromClient } from './tracing'; @@ -50,7 +50,7 @@ export class ServerRuntimeClient< * @inheritDoc */ public eventFromException(exception: unknown, hint?: EventHint): PromiseLike { - return resolvedSyncPromise(eventFromUnknownInput(getCurrentHub, this._options.stackParser, exception, hint)); + return resolvedSyncPromise(eventFromUnknownInput(getClient(), this._options.stackParser, exception, hint)); } /** diff --git a/packages/deno/src/integrations/globalhandlers.ts b/packages/deno/src/integrations/globalhandlers.ts index d173780cfa50..9914764d4c45 100644 --- a/packages/deno/src/integrations/globalhandlers.ts +++ b/packages/deno/src/integrations/globalhandlers.ts @@ -1,5 +1,6 @@ import type { ServerRuntimeClient } from '@sentry/core'; -import { flush, getCurrentHub } from '@sentry/core'; +import { getClient, getCurrentHub, getCurrentScope } from '@sentry/core'; +import { flush } from '@sentry/core'; import type { Event, Hub, Integration, Primitive, StackParser } from '@sentry/types'; import { eventFromUnknownInput, isPrimitive } from '@sentry/utils'; @@ -70,7 +71,7 @@ function installGlobalErrorHandler(): void { const [hub, stackParser] = getHubAndOptions(); const { message, error } = data; - const event = eventFromUnknownInput(getCurrentHub, stackParser, error || message); + const event = eventFromUnknownInput(getClient(), stackParser, error || message); event.level = 'fatal'; @@ -113,7 +114,7 @@ function installGlobalUnhandledRejectionHandler(): void { const event = isPrimitive(error) ? eventFromRejectionWithPrimitive(error) - : eventFromUnknownInput(getCurrentHub, stackParser, error, undefined); + : eventFromUnknownInput(getClient(), stackParser, error, undefined); event.level = 'fatal'; diff --git a/packages/node/test/eventbuilders.test.ts b/packages/node/test/eventbuilders.test.ts index 3c2ae88f03e3..ead2d01e9b44 100644 --- a/packages/node/test/eventbuilders.test.ts +++ b/packages/node/test/eventbuilders.test.ts @@ -1,40 +1,51 @@ -import type { Client } from '@sentry/types'; +import type { Hub } from '@sentry/types'; import { eventFromUnknownInput } from '@sentry/utils'; -import { Scope, defaultStackParser, getCurrentHub } from '../src'; +import { defaultStackParser } from '../src'; -const testScope = new Scope(); - -jest.mock('@sentry/core', () => { - const original = jest.requireActual('@sentry/core'); - return { - ...original, - getCurrentHub(): { - getClient(): Client; - getScope(): Scope; - } { - return { - getClient(): any { - return { - getOptions(): any { - return { normalizeDepth: 6 }; +describe('eventFromUnknownInput', () => { + test('uses normalizeDepth from init options', () => { + const deepObject = { + a: { + b: { + c: { + d: { + e: { + f: { + g: 'foo', + }, + }, }, - }; - }, - getScope(): Scope { - return testScope; + }, }, - }; - }, - }; -}); + }, + }; -afterEach(() => { - jest.resetAllMocks(); -}); + const client = { + getOptions(): any { + return { normalizeDepth: 6 }; + }, + } as any; + const event = eventFromUnknownInput(client, defaultStackParser, deepObject); -describe('eventFromUnknownInput', () => { - test('uses normalizeDepth from init options', () => { + const serializedObject = event.extra?.__serialized__; + expect(serializedObject).toBeDefined(); + expect(serializedObject).toEqual({ + a: { + b: { + c: { + d: { + e: { + f: '[Object]', + }, + }, + }, + }, + }, + }); + }); + + test('uses normalizeDepth from init options (passing getCurrentHub)', () => { const deepObject = { a: { b: { @@ -51,9 +62,19 @@ describe('eventFromUnknownInput', () => { }, }; - eventFromUnknownInput(getCurrentHub, defaultStackParser, deepObject); + const getCurrentHub = jest.fn(() => { + return { + getClient: () => ({ + getOptions(): any { + return { normalizeDepth: 6 }; + }, + }), + } as unknown as Hub; + }); + + const event = eventFromUnknownInput(getCurrentHub, defaultStackParser, deepObject); - const serializedObject = (testScope as any)._extra.__serialized__; + const serializedObject = event.extra?.__serialized__; expect(serializedObject).toBeDefined(); expect(serializedObject).toEqual({ a: { diff --git a/packages/utils/src/eventbuilder.ts b/packages/utils/src/eventbuilder.ts index 5dac2f583bb6..28b2d94b0c4f 100644 --- a/packages/utils/src/eventbuilder.ts +++ b/packages/utils/src/eventbuilder.ts @@ -1,7 +1,9 @@ import type { + Client, Event, EventHint, Exception, + Extras, Hub, Mechanism, Severity, @@ -61,14 +63,18 @@ function getMessageForObject(exception: object): string { /** * Builds and Event from a Exception + * + * TODO(v8): Remove getHub fallback * @hidden */ export function eventFromUnknownInput( - getCurrentHub: () => Hub, + getHubOrClient: (() => Hub) | Client | undefined, stackParser: StackParser, exception: unknown, hint?: EventHint, ): Event { + const client = typeof getHubOrClient === 'function' ? getHubOrClient().getClient() : getHubOrClient; + let ex: unknown = exception; const providedMechanism: Mechanism | undefined = hint && hint.data && (hint.data as { mechanism: Mechanism }).mechanism; @@ -77,12 +83,12 @@ export function eventFromUnknownInput( type: 'generic', }; + let extras: Extras | undefined; + if (!isError(exception)) { if (isPlainObject(exception)) { - const hub = getCurrentHub(); - const client = hub.getClient(); const normalizeDepth = client && client.getOptions().normalizeDepth; - hub.getScope().setExtra('__serialized__', normalizeToSize(exception, normalizeDepth)); + extras = { ['__serialized__']: normalizeToSize(exception as Record, normalizeDepth) }; const message = getMessageForObject(exception); ex = (hint && hint.syntheticException) || new Error(message); @@ -96,12 +102,16 @@ export function eventFromUnknownInput( mechanism.synthetic = true; } - const event = { + const event: Event = { exception: { values: [exceptionFromError(stackParser, ex as Error)], }, }; + if (extras) { + event.extra = extras; + } + addExceptionTypeValue(event, undefined, undefined); addExceptionMechanism(event, mechanism); diff --git a/packages/utils/test/eventbuilder.test.ts b/packages/utils/test/eventbuilder.test.ts index b1c46630de08..ec3fdf4bf6ee 100644 --- a/packages/utils/test/eventbuilder.test.ts +++ b/packages/utils/test/eventbuilder.test.ts @@ -36,4 +36,9 @@ describe('eventFromUnknownInput', () => { const event = eventFromUnknownInput(getCurrentHub, stackParser, { foo: { bar: 'baz' }, message: 'Some message' }); expect(event.exception?.values?.[0].value).toBe('Some message'); }); + + test('passing client directly', () => { + const event = eventFromUnknownInput(undefined, stackParser, { foo: { bar: 'baz' }, prop: 1 }); + expect(event.exception?.values?.[0].value).toBe('Object captured as exception with keys: foo, prop'); + }); });