Skip to content

feat(core): Update Sentry.addBreadcrumb to skip hub #10601

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions packages/core/src/breadcrumbs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { Breadcrumb, BreadcrumbHint } from '@sentry/types';
import { consoleSandbox, dateTimestampInSeconds } from '@sentry/utils';
import { getClient } from './exports';
import { getIsolationScope } from './hub';

/**
* Default maximum number of breadcrumbs added to an event. Can be overwritten
* with {@link Options.maxBreadcrumbs}.
*/
const DEFAULT_BREADCRUMBS = 100;

/**
* Records a new breadcrumb which will be attached to future events.
*
* Breadcrumbs will be added to subsequent events to provide more context on
* user's actions prior to an error or crash.
*/
export function addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void {
const client = getClient();
const isolationScope = getIsolationScope();

if (!client) return;

const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = client.getOptions();

if (maxBreadcrumbs <= 0) return;

const timestamp = dateTimestampInSeconds();
const mergedBreadcrumb = { timestamp, ...breadcrumb };
const finalBreadcrumb = beforeBreadcrumb
? (consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint)) as Breadcrumb | null)
: mergedBreadcrumb;

if (finalBreadcrumb === null) return;

if (client.emit) {
client.emit('beforeAddBreadcrumb', finalBreadcrumb, hint);
}

isolationScope.addBreadcrumb(finalBreadcrumb, maxBreadcrumbs);
}
15 changes: 0 additions & 15 deletions packages/core/src/exports.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import type {
Breadcrumb,
BreadcrumbHint,
CaptureContext,
CheckIn,
Client,
Expand Down Expand Up @@ -76,19 +74,6 @@ export function captureEvent(event: Event, hint?: EventHint): string {
return getCurrentHub().captureEvent(event, hint);
}

/**
* Records a new breadcrumb which will be attached to future events.
*
* Breadcrumbs will be added to subsequent events to provide more context on
* user's actions prior to an error or crash.
*
* @param breadcrumb The breadcrumb to record.
*/
export function addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): ReturnType<Hub['addBreadcrumb']> {
// eslint-disable-next-line deprecation/deprecation
getCurrentHub().addBreadcrumb(breadcrumb, hint);
}

/**
* Sets context data with the given name.
* @param name of the context
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export * from './tracing';
export * from './semanticAttributes';
export { createEventEnvelope, createSessionEnvelope } from './envelope';
export {
addBreadcrumb,
captureCheckIn,
withMonitor,
captureException,
Expand Down Expand Up @@ -94,6 +93,7 @@ export { RequestData } from './integrations/requestdata';
export { InboundFilters } from './integrations/inboundfilters';
export { FunctionToString } from './integrations/functiontostring';
export { LinkedErrors } from './integrations/linkederrors';
export { addBreadcrumb } from './breadcrumbs';
/* eslint-enable deprecation/deprecation */
import * as INTEGRATIONS from './integrations';
export { functionToStringIntegration } from './integrations/functiontostring';
Expand Down
146 changes: 65 additions & 81 deletions packages/core/test/lib/base.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import type { Client, Envelope, Event, Span, Transaction } from '@sentry/types';
import { SentryError, SyncPromise, dsnToString, logger } from '@sentry/utils';

import { Hub, Scope, makeSession, setGlobalScope } from '../../src';
import {
Scope,
addBreadcrumb,
getCurrentScope,
getIsolationScope,
makeSession,
setCurrentClient,
setGlobalScope,
} from '../../src';
import * as integrationModule from '../../src/integration';
import { TestClient, getDefaultTestClientOptions } from '../mocks/client';
import { AdHocIntegration, TestIntegration } from '../mocks/integration';
Expand Down Expand Up @@ -55,6 +63,8 @@ describe('BaseClient', () => {
TestClient.sendEventCalled = undefined;
TestClient.instance = undefined;
setGlobalScope(undefined);
getCurrentScope().clear();
getIsolationScope().clear();
});

afterEach(() => {
Expand Down Expand Up @@ -114,133 +124,108 @@ describe('BaseClient', () => {

describe('getBreadcrumbs() / addBreadcrumb()', () => {
test('adds a breadcrumb', () => {
expect.assertions(1);

const options = getDefaultTestClientOptions({});
const client = new TestClient(options);
setCurrentClient(client);
client.init();

const scope = new Scope();
// eslint-disable-next-line deprecation/deprecation
const hub = new Hub(client, scope);

scope.addBreadcrumb({ message: 'hello' }, 100);
// eslint-disable-next-line deprecation/deprecation
hub.addBreadcrumb({ message: 'world' });
addBreadcrumb({ message: 'world' });

expect((scope as any)._breadcrumbs[1].message).toEqual('world');
});
const breadcrumbs = scope.getScopeData().breadcrumbs;
const isolationScopeBreadcrumbs = getIsolationScope().getScopeData().breadcrumbs;

test('adds a timestamp to new breadcrumbs', () => {
expect.assertions(1);
expect(breadcrumbs).toEqual([{ message: 'hello', timestamp: expect.any(Number) }]);
expect(isolationScopeBreadcrumbs).toEqual([{ message: 'world', timestamp: expect.any(Number) }]);
});

test('accepts a timestamp for new breadcrumbs', () => {
const options = getDefaultTestClientOptions({});
const client = new TestClient(options);
setCurrentClient(client);
client.init();

const scope = new Scope();
// eslint-disable-next-line deprecation/deprecation
const hub = new Hub(client, scope);

scope.addBreadcrumb({ message: 'hello' }, 100);
// eslint-disable-next-line deprecation/deprecation
hub.addBreadcrumb({ message: 'world' });
scope.addBreadcrumb({ message: 'hello', timestamp: 1234 }, 100);
addBreadcrumb({ message: 'world', timestamp: 12345 });

expect((scope as any)._breadcrumbs[1].timestamp).toBeGreaterThan(1);
const breadcrumbs = scope.getScopeData().breadcrumbs;
const isolationScopeBreadcrumbs = getIsolationScope().getScopeData().breadcrumbs;

expect(breadcrumbs).toEqual([{ message: 'hello', timestamp: 1234 }]);
expect(isolationScopeBreadcrumbs).toEqual([{ message: 'world', timestamp: 12345 }]);
});

test('discards breadcrumbs beyond `maxBreadcrumbs`', () => {
expect.assertions(2);

const options = getDefaultTestClientOptions({ maxBreadcrumbs: 1 });
const client = new TestClient(options);
const scope = new Scope();
// eslint-disable-next-line deprecation/deprecation
const hub = new Hub(client, scope);

scope.addBreadcrumb({ message: 'hello' }, 100);
// eslint-disable-next-line deprecation/deprecation
hub.addBreadcrumb({ message: 'world' });

expect((scope as any)._breadcrumbs.length).toEqual(1);
expect((scope as any)._breadcrumbs[0].message).toEqual('world');
});

test('allows concurrent updates', () => {
expect.assertions(1);
setCurrentClient(client);
client.init();

const options = getDefaultTestClientOptions({});
const client = new TestClient(options);
const scope = new Scope();
// eslint-disable-next-line deprecation/deprecation
const hub = new Hub(client, scope);
addBreadcrumb({ message: 'hello1' });
addBreadcrumb({ message: 'hello2' });
addBreadcrumb({ message: 'hello3' });

scope.addBreadcrumb({ message: 'hello' });
// eslint-disable-next-line deprecation/deprecation
hub.addBreadcrumb({ message: 'world' });
const isolationScopeBreadcrumbs = getIsolationScope().getScopeData().breadcrumbs;

expect((scope as any)._breadcrumbs).toHaveLength(2);
expect(isolationScopeBreadcrumbs).toEqual([{ message: 'hello3', timestamp: expect.any(Number) }]);
});

test('calls `beforeBreadcrumb` and adds the breadcrumb without any changes', () => {
expect.assertions(1);

const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb);
const options = getDefaultTestClientOptions({ beforeBreadcrumb });
const client = new TestClient(options);
const scope = new Scope();
// eslint-disable-next-line deprecation/deprecation
const hub = new Hub(client, scope);
setCurrentClient(client);
client.init();

// eslint-disable-next-line deprecation/deprecation
hub.addBreadcrumb({ message: 'hello' });
addBreadcrumb({ message: 'hello' });

expect((scope as any)._breadcrumbs[0].message).toEqual('hello');
const isolationScopeBreadcrumbs = getIsolationScope().getScopeData().breadcrumbs;
expect(isolationScopeBreadcrumbs).toEqual([{ message: 'hello', timestamp: expect.any(Number) }]);
});

test('calls `beforeBreadcrumb` and uses the new one', () => {
expect.assertions(1);

const beforeBreadcrumb = jest.fn(() => ({ message: 'changed' }));
const options = getDefaultTestClientOptions({ beforeBreadcrumb });
const client = new TestClient(options);
const scope = new Scope();
// eslint-disable-next-line deprecation/deprecation
const hub = new Hub(client, scope);
setCurrentClient(client);
client.init();

// eslint-disable-next-line deprecation/deprecation
hub.addBreadcrumb({ message: 'hello' });
addBreadcrumb({ message: 'hello' });

expect((scope as any)._breadcrumbs[0].message).toEqual('changed');
const isolationScopeBreadcrumbs = getIsolationScope().getScopeData().breadcrumbs;
expect(isolationScopeBreadcrumbs).toEqual([{ message: 'changed', timestamp: expect.any(Number) }]);
});

test('calls `beforeBreadcrumb` and discards the breadcrumb when returned `null`', () => {
expect.assertions(1);

const beforeBreadcrumb = jest.fn(() => null);
const options = getDefaultTestClientOptions({ beforeBreadcrumb });
const client = new TestClient(options);
const scope = new Scope();
// eslint-disable-next-line deprecation/deprecation
const hub = new Hub(client, scope);
setCurrentClient(client);
client.init();

// eslint-disable-next-line deprecation/deprecation
hub.addBreadcrumb({ message: 'hello' });
addBreadcrumb({ message: 'hello' });

expect((scope as any)._breadcrumbs.length).toEqual(0);
const isolationScopeBreadcrumbs = getIsolationScope().getScopeData().breadcrumbs;
expect(isolationScopeBreadcrumbs).toEqual([]);
});

test('`beforeBreadcrumb` gets an access to a hint as a second argument', () => {
expect.assertions(2);

const beforeBreadcrumb = jest.fn((breadcrumb, hint) => ({ ...breadcrumb, data: hint.data }));
const options = getDefaultTestClientOptions({ beforeBreadcrumb });
const client = new TestClient(options);
const scope = new Scope();
// eslint-disable-next-line deprecation/deprecation
const hub = new Hub(client, scope);
setCurrentClient(client);
client.init();

// eslint-disable-next-line deprecation/deprecation
hub.addBreadcrumb({ message: 'hello' }, { data: 'someRandomThing' });
addBreadcrumb({ message: 'hello' }, { data: 'someRandomThing' });

expect((scope as any)._breadcrumbs[0].message).toEqual('hello');
expect((scope as any)._breadcrumbs[0].data).toEqual('someRandomThing');
const isolationScopeBreadcrumbs = getIsolationScope().getScopeData().breadcrumbs;
expect(isolationScopeBreadcrumbs).toEqual([
{ message: 'hello', data: 'someRandomThing', timestamp: expect.any(Number) },
]);
});
});

Expand Down Expand Up @@ -627,13 +612,12 @@ describe('BaseClient', () => {

const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, maxBreadcrumbs: 1 });
const client = new TestClient(options);
setCurrentClient(client);
client.init();
const scope = new Scope();
// eslint-disable-next-line deprecation/deprecation
const hub = new Hub(client, scope);
// eslint-disable-next-line deprecation/deprecation
hub.addBreadcrumb({ message: '1' });
// eslint-disable-next-line deprecation/deprecation
hub.addBreadcrumb({ message: '2' });

addBreadcrumb({ message: '1' });
addBreadcrumb({ message: '2' });

client.captureEvent({ message: 'message' }, undefined, scope);

Expand Down
2 changes: 1 addition & 1 deletion packages/node-experimental/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export { startSpan, startSpanManual, startInactiveSpan, getActiveSpan } from '@s
export {
getClient,
isInitialized,
addBreadcrumb,
captureException,
captureEvent,
captureMessage,
Expand All @@ -55,6 +54,7 @@ export { getCurrentHub, makeMain } from './sdk/hub';
export { Scope } from './sdk/scope';

export {
addBreadcrumb,
makeNodeTransport,
defaultStackParser,
getSentryRelease,
Expand Down
26 changes: 0 additions & 26 deletions packages/node-experimental/src/sdk/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import type { Span } from '@opentelemetry/api';
import { context, trace } from '@opentelemetry/api';
import type {
Breadcrumb,
BreadcrumbHint,
CaptureContext,
Event,
EventHint,
Expand All @@ -15,7 +13,6 @@ import type {
SeverityLevel,
User,
} from '@sentry/types';
import { consoleSandbox, dateTimestampInSeconds } from '@sentry/utils';
import { getContextFromScope, getScopesFromContext, setScopesOnContext } from '../utils/contextData';

import type { ExclusiveEventHintOrCaptureContext } from '../utils/prepareEvent';
Expand Down Expand Up @@ -116,29 +113,6 @@ export function captureEvent(event: Event, hint?: EventHint): string {
return getCurrentScope().captureEvent(event, hint);
}

/**
* Add a breadcrumb to the current isolation scope.
*/
export function addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void {
const client = getClient();

const { beforeBreadcrumb, maxBreadcrumbs } = client.getOptions();

if (maxBreadcrumbs && maxBreadcrumbs <= 0) return;

const timestamp = dateTimestampInSeconds();
const mergedBreadcrumb = { timestamp, ...breadcrumb };
const finalBreadcrumb = beforeBreadcrumb
? (consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint)) as Breadcrumb | null)
: mergedBreadcrumb;

if (finalBreadcrumb === null) return;

client.emit('beforeAddBreadcrumb', finalBreadcrumb, hint);

getIsolationScope().addBreadcrumb(finalBreadcrumb, maxBreadcrumbs);
}

/**
* Add a global event processor.
*/
Expand Down
3 changes: 1 addition & 2 deletions packages/node-experimental/src/sdk/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import type {
TransactionContext,
} from '@sentry/types';

import { endSession, startSession } from '@sentry/core';
import { addBreadcrumb, endSession, startSession } from '@sentry/core';
import {
addBreadcrumb,
captureEvent,
getClient,
getCurrentScope,
Expand Down
Loading