Skip to content

feat: Breadcrumbs hint implementation #1505

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 1 commit into from
Sep 3, 2018
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
86 changes: 57 additions & 29 deletions packages/browser/src/integrations/breadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,20 @@ export interface SentryWrappedXMLHttpRequest extends XMLHttpRequest {
function addSentryBreadcrumb(serializedData: string): void {
// There's always something that can go wrong with deserialization...
try {
const data: { [key: string]: any } = deserialize(serializedData);
const exception = data.exception && data.exception.values && data.exception.values[0];

getCurrentHub().addBreadcrumb({
category: 'sentry',
event_id: data.event_id,
level: data.level || Severity.fromString('error'),
message: exception ? `${exception.type ? `${exception.type}: ` : ''}${exception.value}` : data.message,
});
const event: { [key: string]: any } = deserialize(serializedData);
const exception = event.exception && event.exception.values && event.exception.values[0];

getCurrentHub().addBreadcrumb(
{
category: 'sentry',
event_id: event.event_id,
level: event.level || Severity.fromString('error'),
message: exception ? `${exception.type ? `${exception.type}: ` : ''}${exception.value}` : event.message,
},
{
event,
},
);
} catch (_oO) {
logger.error('Error while adding sentry type breadcrumb');
}
Expand Down Expand Up @@ -98,17 +103,20 @@ export class Breadcrumbs implements Integration {
}

// What is wrong with you TypeScript...
const crumb = ({
const breadcrumbData = ({
category: 'beacon',
data,
type: 'http',
} as any) as { [key: string]: any };

if (!result) {
crumb.level = Severity.Error;
breadcrumbData.level = Severity.Error;
}

getCurrentHub().addBreadcrumb(crumb);
getCurrentHub().addBreadcrumb(breadcrumbData, {
input: args,
result,
});

return result;
};
Expand Down Expand Up @@ -153,7 +161,10 @@ export class Breadcrumbs implements Integration {
}
}

getCurrentHub().addBreadcrumb(breadcrumbData);
getCurrentHub().addBreadcrumb(breadcrumbData, {
input: args,
level,
});

// this fails for some browsers. :(
if (originalConsoleLevel) {
Expand Down Expand Up @@ -223,20 +234,32 @@ export class Breadcrumbs implements Integration {
.apply(global, args)
.then((response: Response) => {
fetchData.status_code = response.status;
getCurrentHub().addBreadcrumb({
category: 'fetch',
data: fetchData,
type: 'http',
});
getCurrentHub().addBreadcrumb(
{
category: 'fetch',
data: fetchData,
type: 'http',
},
{
input: args,
response,
},
);
return response;
})
.catch((error: Error) => {
getCurrentHub().addBreadcrumb({
category: 'fetch',
data: fetchData,
level: Severity.Error,
type: 'http',
});
getCurrentHub().addBreadcrumb(
{
category: 'fetch',
data: fetchData,
level: Severity.Error,
type: 'http',
},
{
error,
input: args,
},
);

throw error;
});
Expand Down Expand Up @@ -385,11 +408,16 @@ export class Breadcrumbs implements Integration {
} catch (e) {
/* do nothing */
}
getCurrentHub().addBreadcrumb({
category: 'xhr',
data: xhr.__sentry_xhr__,
type: 'http',
});
getCurrentHub().addBreadcrumb(
{
category: 'xhr',
data: xhr.__sentry_xhr__,
type: 'http',
},
{
xhr,
},
);
}
}

Expand Down
14 changes: 10 additions & 4 deletions packages/browser/src/integrations/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,16 @@ export function breadcrumbEventHandler(eventName: string): (event: Event) => voi
target = '<unknown>';
}

getCurrentHub().addBreadcrumb({
category: `ui.${eventName}`, // e.g. ui.click, ui.input
message: target,
});
getCurrentHub().addBreadcrumb(
{
category: `ui.${eventName}`, // e.g. ui.click, ui.input
message: target,
},
{
event,
name: eventName,
},
);
};
}

Expand Down
14 changes: 11 additions & 3 deletions packages/core/src/base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { Scope } from '@sentry/hub';
import { Breadcrumb, SentryEvent, SentryEventHint, SentryResponse, Severity, Status } from '@sentry/types';
import {
Breadcrumb,
SentryBreadcrumbHint,
SentryEvent,
SentryEventHint,
SentryResponse,
Severity,
Status,
} from '@sentry/types';
import { uuid4 } from '@sentry/utils/misc';
import { truncate } from '@sentry/utils/string';
import { Dsn } from './dsn';
Expand Down Expand Up @@ -144,7 +152,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
/**
* @inheritDoc
*/
public async addBreadcrumb(breadcrumb: Breadcrumb, scope?: Scope): Promise<void> {
public async addBreadcrumb(breadcrumb: Breadcrumb, hint?: SentryBreadcrumbHint, scope?: Scope): Promise<void> {
const { beforeBreadcrumb, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = this.getOptions();

if (maxBreadcrumbs <= 0) {
Expand All @@ -153,7 +161,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement

const timestamp = new Date().getTime() / 1000;
const mergedBreadcrumb = { timestamp, ...breadcrumb };
const finalBreadcrumb = beforeBreadcrumb ? beforeBreadcrumb(mergedBreadcrumb) : mergedBreadcrumb;
const finalBreadcrumb = beforeBreadcrumb ? beforeBreadcrumb(mergedBreadcrumb, hint) : mergedBreadcrumb;

if (finalBreadcrumb === null) {
return;
Expand Down
14 changes: 8 additions & 6 deletions packages/core/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Breadcrumb,
Integration,
Repo,
SentryBreadcrumbHint,
SentryEvent,
SentryEventHint,
SentryResponse,
Expand Down Expand Up @@ -99,7 +100,7 @@ export interface Options {
* Returning null will case the event to be dropped.
*
* @param event The error or message event generated by the SDK.
* @param hint May contain additional informartion about the original exception.
* @param hint May contain additional information about the original exception.
* @returns A new event that will be sent | null.
*/
beforeSend?(event: SentryEvent, hint?: SentryEventHint): SentryEvent | null;
Expand All @@ -115,7 +116,7 @@ export interface Options {
* @param breadcrumb The breadcrumb as created by the SDK.
* @returns The breadcrumb that will be added | null.
*/
beforeBreadcrumb?(breadcrumb: Breadcrumb): Breadcrumb | null;
beforeBreadcrumb?(breadcrumb: Breadcrumb, hint?: SentryBreadcrumbHint): Breadcrumb | null;
}

/**
Expand Down Expand Up @@ -150,7 +151,7 @@ export interface Client<O extends Options = Options> {
* Captures an exception event and sends it to Sentry.
*
* @param exception An exception-like object.
* @param hint May contain additional informartion about the original exception.
* @param hint May contain additional information about the original exception.
* @param scope An optional scope containing event metadata.
* @returns SentryResponse status and event
*/
Expand All @@ -161,7 +162,7 @@ export interface Client<O extends Options = Options> {
*
* @param message The message to send to Sentry.
* @param level Define the level of the message.
* @param hint May contain additional informartion about the original exception.
* @param hint May contain additional information about the original exception.
* @param scope An optional scope containing event metadata.
* @returns SentryResponse status and event
*/
Expand All @@ -171,7 +172,7 @@ export interface Client<O extends Options = Options> {
* Captures a manually created event and sends it to Sentry.
*
* @param event The event to send to Sentry.
* @param hint May contain additional informartion about the original exception.
* @param hint May contain additional information about the original exception.
* @param scope An optional scope containing event metadata.
* @returns SentryResponse status and event
*/
Expand All @@ -185,9 +186,10 @@ export interface Client<O extends Options = Options> {
* of breadcrumbs, use {@link Options.maxBreadcrumbs}.
*
* @param breadcrumb The breadcrumb to record.
* @param hint May contain additional information about the original breadcrumb.
* @param scope An optional scope to store this breadcrumb in.
*/
addBreadcrumb(breadcrumb: Breadcrumb, scope?: Scope): void;
addBreadcrumb(breadcrumb: Breadcrumb, hint?: SentryBreadcrumbHint, scope?: Scope): void;

/** Returns the current Dsn. */
getDsn(): Dsn | undefined;
Expand Down
27 changes: 18 additions & 9 deletions packages/core/test/lib/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,23 @@ describe('BaseClient', () => {
const client = new TestClient({});
const scope = new Scope();
scope.addBreadcrumb({ message: 'hello' }, 100);
await client.addBreadcrumb({ message: 'world' }, scope);
await client.addBreadcrumb({ message: 'world' }, undefined, scope);
expect(scope.getBreadcrumbs()[1].message).toBe('world');
});

test('adds a timestamp to new breadcrumbs', async () => {
const client = new TestClient({});
const scope = new Scope();
scope.addBreadcrumb({ message: 'hello' }, 100);
await client.addBreadcrumb({ message: 'world' }, scope);
await client.addBreadcrumb({ message: 'world' }, undefined, scope);
expect(scope.getBreadcrumbs()[1].timestamp).toBeGreaterThan(1);
});

test('discards breadcrumbs beyond maxBreadcrumbs', async () => {
const client = new TestClient({ maxBreadcrumbs: 1 });
const scope = new Scope();
scope.addBreadcrumb({ message: 'hello' }, 100);
await client.addBreadcrumb({ message: 'world' }, scope);
await client.addBreadcrumb({ message: 'world' }, undefined, scope);
expect(scope.getBreadcrumbs().length).toBe(1);
expect(scope.getBreadcrumbs()[0].message).toBe('world');
});
Expand All @@ -109,8 +109,8 @@ describe('BaseClient', () => {
const client = new TestClient({});
const scope = new Scope();
await Promise.all([
client.addBreadcrumb({ message: 'hello' }, scope),
client.addBreadcrumb({ message: 'world' }, scope),
client.addBreadcrumb({ message: 'hello' }, undefined, scope),
client.addBreadcrumb({ message: 'world' }, undefined, scope),
]);
expect(scope.getBreadcrumbs()).toHaveLength(2);
});
Expand All @@ -119,25 +119,34 @@ describe('BaseClient', () => {
const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb);
const client = new TestClient({ beforeBreadcrumb });
const scope = new Scope();
await client.addBreadcrumb({ message: 'hello' }, scope);
await client.addBreadcrumb({ message: 'hello' }, undefined, scope);
expect(scope.getBreadcrumbs()[0].message).toBe('hello');
});

test('calls beforeBreadcrumb and uses the new one', async () => {
const beforeBreadcrumb = jest.fn(() => ({ message: 'changed' }));
const client = new TestClient({ beforeBreadcrumb });
const scope = new Scope();
await client.addBreadcrumb({ message: 'hello' }, scope);
await client.addBreadcrumb({ message: 'hello' }, undefined, scope);
expect(scope.getBreadcrumbs()[0].message).toBe('changed');
});

test('calls shouldAddBreadcrumb and discards the breadcrumb', async () => {
test('calls beforeBreadcrumb and discards the breadcrumb when returned null', async () => {
const beforeBreadcrumb = jest.fn(() => null);
const client = new TestClient({ beforeBreadcrumb });
const scope = new Scope();
await client.addBreadcrumb({ message: 'hello' }, scope);
await client.addBreadcrumb({ message: 'hello' }, undefined, scope);
expect(scope.getBreadcrumbs().length).toBe(0);
});

test('calls beforeBreadcrumb gets an access to a hint as a second argument', async () => {
const beforeBreadcrumb = jest.fn((breadcrumb, hint) => ({ ...breadcrumb, data: hint.data }));
const client = new TestClient({ beforeBreadcrumb });
const scope = new Scope();
await client.addBreadcrumb({ message: 'hello' }, { data: 'someRandomThing' }, scope);
expect(scope.getBreadcrumbs()[0].message).toBe('hello');
expect(scope.getBreadcrumbs()[0].data).toBe('someRandomThing');
});
});

describe('captures', () => {
Expand Down
13 changes: 7 additions & 6 deletions packages/hub/src/hub.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Breadcrumb, SentryEvent, SentryEventHint, Severity } from '@sentry/types';
import { Breadcrumb, SentryBreadcrumbHint, SentryEvent, SentryEventHint, Severity } from '@sentry/types';
import { uuid4 } from '@sentry/utils/misc';
import { Layer } from './interfaces';
import { Scope } from './scope';
Expand Down Expand Up @@ -168,7 +168,7 @@ export class Hub {
* Captures an exception event and sends it to Sentry.
*
* @param exception An exception-like object.
* @param hint May contain additional informartion about the original exception.
* @param hint May contain additional information about the original exception.
* @returns The generated eventId.
*/
public captureException(exception: any, hint?: SentryEventHint): string {
Expand All @@ -185,7 +185,7 @@ export class Hub {
*
* @param message The message to send to Sentry.
* @param level Define the level of the message.
* @param hint May contain additional informartion about the original exception.
* @param hint May contain additional information about the original exception.
* @returns The generated eventId.
*/
public captureMessage(message: string, level?: Severity, hint?: SentryEventHint): string {
Expand All @@ -201,7 +201,7 @@ export class Hub {
* Captures a manually created event and sends it to Sentry.
*
* @param event The event to send to Sentry.
* @param hint May contain additional informartion about the original exception.
* @param hint May contain additional information about the original exception.
*/
public captureEvent(event: SentryEvent, hint?: SentryEventHint): string {
const eventId = (this._lastEventId = uuid4());
Expand All @@ -228,9 +228,10 @@ export class Hub {
* user's actions prior to an error or crash.
*
* @param breadcrumb The breadcrumb to record.
* @param hint May contain additional information about the original breadcrumb.
*/
public addBreadcrumb(breadcrumb: Breadcrumb): void {
this.invokeClient('addBreadcrumb', breadcrumb);
public addBreadcrumb(breadcrumb: Breadcrumb, hint?: SentryBreadcrumbHint): void {
this.invokeClient('addBreadcrumb', breadcrumb, { ...hint });
}

/**
Expand Down
Loading