Skip to content

Commit

Permalink
fix: add max breadcrumbs limitation (#28)
Browse files Browse the repository at this point in the history
* feat: add max breadcrumbs limitation

* feat: add breadcrumbs limitation to setBreadcrumbs method
  • Loading branch information
ihsnow authored Jul 23, 2024
1 parent 28a5c0c commit 859c7d7
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 4 deletions.
1 change: 1 addition & 0 deletions libs/browser/src/lib/consts/max-breadcrumbs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const MAX_BREADCRUMBS = 100;
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export interface BrowserSentryClientOptions extends SentryClientOptions {
ignoreErrors?: Array<string | RegExp>;
blacklistUrls?: Array<string | RegExp>;
release?: string;
maxBreadcrumbs?: number;
}
102 changes: 101 additions & 1 deletion libs/browser/src/lib/services/browser-micro-sentry-client.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { BrowserMicroSentryClient } from './browser-micro-sentry-client';
import { Severity } from '@micro-sentry/core';
import { MAX_BREADCRUMBS } from '../consts/max-breadcrumbs';

describe('BrowserMicroSentryClient', () => {
let client: BrowserMicroSentryClient;
const maxBreadcrumbs = 10;
const getBreadcrumbs = (amount: number) =>
[...Array(amount).keys()].map((index) => ({
event_id: `id${index}`,
type: 'console',
level: Severity.critical,
}));

beforeAll(() => {
client = new BrowserMicroSentryClient({
dsn: 'http://secret@exampl.dsn/2',
release: '1.0.0',
maxBreadcrumbs,
});
});

Expand Down Expand Up @@ -205,6 +214,97 @@ describe('BrowserMicroSentryClient', () => {
});
});

it('should limit breadcrumbs amount - with custom limit; incremental addition', () => {
getBreadcrumbs(maxBreadcrumbs + 2).forEach((bc) =>
client.addBreadcrumb(bc)
);

expect(client.state.breadcrumbs?.length).toBe(maxBreadcrumbs);
});

it('should limit breadcrumbs amount - with custom limit; all at once', () => {
client.setBreadcrumbs(getBreadcrumbs(maxBreadcrumbs + 2));

expect(client.state.breadcrumbs?.length).toBe(maxBreadcrumbs);
});

it('should limit breadcrumbs amount - with default limit; incremental addition', () => {
const anotherClient = new BrowserMicroSentryClient({});

getBreadcrumbs(MAX_BREADCRUMBS + 2).forEach((bc) =>
anotherClient.addBreadcrumb(bc)
);

expect(anotherClient.state.breadcrumbs?.length).toBe(MAX_BREADCRUMBS);
});

it('should limit breadcrumbs amount - with default limit; all at once', () => {
const anotherClient = new BrowserMicroSentryClient({});
anotherClient.setBreadcrumbs(getBreadcrumbs(MAX_BREADCRUMBS + 2));

expect(anotherClient.state.breadcrumbs?.length).toBe(MAX_BREADCRUMBS);
});

it('should save only last breadcrumbs; incremental addition', () => {
getBreadcrumbs(maxBreadcrumbs + 2).forEach((bc) =>
client.addBreadcrumb(bc)
);

expect(
(client.state.breadcrumbs ?? [])
.map((breadcrumb) => breadcrumb.event_id)
.join(',')
).toEqual('id2,id3,id4,id5,id6,id7,id8,id9,id10,id11');
});

it('should save only last breadcrumbs; all at once', () => {
client.setBreadcrumbs(getBreadcrumbs(maxBreadcrumbs + 2));

expect(
(client.state.breadcrumbs ?? [])
.map((breadcrumb) => breadcrumb.event_id)
.join(',')
).toEqual('id2,id3,id4,id5,id6,id7,id8,id9,id10,id11');
});

it('should not add breadcrumbs at all if maxBreadcrumbs is set to 0; incremental addition', () => {
const anotherClient = new BrowserMicroSentryClient({ maxBreadcrumbs: 0 });

getBreadcrumbs(1).forEach((bc) => anotherClient.addBreadcrumb(bc));

expect(anotherClient.state.breadcrumbs?.length).toBe(0);
});

it('should not add breadcrumbs at all if maxBreadcrumbs is set to 0; all at once', () => {
const anotherClient = new BrowserMicroSentryClient({ maxBreadcrumbs: 0 });

anotherClient.setBreadcrumbs(getBreadcrumbs(1));

expect(anotherClient.state.breadcrumbs?.length).toBe(0);
});

it('should ignore maxBreadcrumbs option if maxBreadcrumbs is a negative number; incremental addition', () => {
const anotherClient = new BrowserMicroSentryClient({
maxBreadcrumbs: -100,
});

getBreadcrumbs(MAX_BREADCRUMBS + 2).forEach((bc) =>
anotherClient.addBreadcrumb(bc)
);

expect(anotherClient.state.breadcrumbs?.length).toBe(MAX_BREADCRUMBS);
});

it('should ignore maxBreadcrumbs option if maxBreadcrumbs is a negative number; all at once', () => {
const anotherClient = new BrowserMicroSentryClient({
maxBreadcrumbs: -100,
});

anotherClient.setBreadcrumbs(getBreadcrumbs(MAX_BREADCRUMBS + 2));

expect(anotherClient.state.breadcrumbs?.length).toBe(MAX_BREADCRUMBS);
});

it('should skip breadcrumbs if beforeBreadcrumb returns null', () => {
client = new BrowserMicroSentryClient({
dsn: 'http://secret@exampl.dsn/2',
Expand Down Expand Up @@ -237,7 +337,7 @@ describe('BrowserMicroSentryClient', () => {
]);
});

afterAll(() => {
afterEach(() => {
client.clearState();
});
});
Expand Down
29 changes: 26 additions & 3 deletions libs/browser/src/lib/services/browser-micro-sentry-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import { State } from '../models/state';
import { MicroSentryPlugin } from '../models/plugin';
import { BrowserSentryClientOptions } from '../models/browser-sentry-client-options';
import { isMatchingPattern } from '../utils/is-matching-pattern';
import { MAX_BREADCRUMBS } from '../consts/max-breadcrumbs';

function getWindow(): Window {
return window;
}

export class BrowserMicroSentryClient extends MicroSentryClient {
private readonly breadcrumbsKeyName = 'breadcrumbs';
private destroyed = false;
private readonly plugins: MicroSentryPlugin[];
private readonly beforeSend: NonNullable<
Expand All @@ -33,6 +35,7 @@ export class BrowserMicroSentryClient extends MicroSentryClient {
BrowserSentryClientOptions['ignoreErrors']
>;
private readonly release?: string;
private readonly maxBreadcrumbs: number;

constructor(
private options: BrowserSentryClientOptions,
Expand All @@ -47,14 +50,17 @@ export class BrowserMicroSentryClient extends MicroSentryClient {
blacklistUrls = [],
ignoreErrors = [],
release = undefined,
} = this.options || {};
maxBreadcrumbs = MAX_BREADCRUMBS,
} = this.options || {} || [];

this.plugins = plugins.map((Plugin) => new Plugin(this));
this.beforeSend = beforeSend;
this.beforeBreadcrumb = beforeBreadcrumb;
this.blacklistUrls = blacklistUrls;
this.ignoreErrors = ignoreErrors;
this.release = release;
this.maxBreadcrumbs =
maxBreadcrumbs >= 0 ? maxBreadcrumbs : MAX_BREADCRUMBS;
}

protected _state: State = {};
Expand Down Expand Up @@ -126,17 +132,20 @@ export class BrowserMicroSentryClient extends MicroSentryClient {
}

this.extendState({
breadcrumbs: [
[this.breadcrumbsKeyName]: [
{
timestamp: Date.now() / 1_000,
...result,
},
],
});

this.trimBreadcrumbs();
}

setBreadcrumbs(breadcrumbs: Breadcrumb[] | undefined) {
this.setKeyState('breadcrumbs', breadcrumbs);
this.setKeyState(this.breadcrumbsKeyName, breadcrumbs);
this.trimBreadcrumbs();
}

captureMessage(message: string, level?: Severity) {
Expand Down Expand Up @@ -307,4 +316,18 @@ export class BrowserMicroSentryClient extends MicroSentryClient {
private setKeyState<T extends keyof State>(key: T, value: State[T]) {
this._state[key] = value;
}

private getKeyState<T extends keyof State>(key: T): State[T] {
return this._state[key];
}

private trimBreadcrumbs(): void {
const breadcrumbs = this.getKeyState(this.breadcrumbsKeyName);
if (breadcrumbs && (breadcrumbs.length ?? 0) > this.maxBreadcrumbs) {
this.setKeyState(
this.breadcrumbsKeyName,
this.maxBreadcrumbs > 0 ? breadcrumbs.slice(-this.maxBreadcrumbs) : []
);
}
}
}

0 comments on commit 859c7d7

Please sign in to comment.