Skip to content

Commit 859c7d7

Browse files
authored
fix: add max breadcrumbs limitation (#28)
* feat: add max breadcrumbs limitation * feat: add breadcrumbs limitation to setBreadcrumbs method
1 parent 28a5c0c commit 859c7d7

File tree

4 files changed

+129
-4
lines changed

4 files changed

+129
-4
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const MAX_BREADCRUMBS = 100;

libs/browser/src/lib/models/browser-sentry-client-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ export interface BrowserSentryClientOptions extends SentryClientOptions {
1212
ignoreErrors?: Array<string | RegExp>;
1313
blacklistUrls?: Array<string | RegExp>;
1414
release?: string;
15+
maxBreadcrumbs?: number;
1516
}

libs/browser/src/lib/services/browser-micro-sentry-client.spec.ts

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
import { BrowserMicroSentryClient } from './browser-micro-sentry-client';
22
import { Severity } from '@micro-sentry/core';
3+
import { MAX_BREADCRUMBS } from '../consts/max-breadcrumbs';
34

45
describe('BrowserMicroSentryClient', () => {
56
let client: BrowserMicroSentryClient;
7+
const maxBreadcrumbs = 10;
8+
const getBreadcrumbs = (amount: number) =>
9+
[...Array(amount).keys()].map((index) => ({
10+
event_id: `id${index}`,
11+
type: 'console',
12+
level: Severity.critical,
13+
}));
614

715
beforeAll(() => {
816
client = new BrowserMicroSentryClient({
917
dsn: 'http://[email protected]/2',
1018
release: '1.0.0',
19+
maxBreadcrumbs,
1120
});
1221
});
1322

@@ -205,6 +214,97 @@ describe('BrowserMicroSentryClient', () => {
205214
});
206215
});
207216

217+
it('should limit breadcrumbs amount - with custom limit; incremental addition', () => {
218+
getBreadcrumbs(maxBreadcrumbs + 2).forEach((bc) =>
219+
client.addBreadcrumb(bc)
220+
);
221+
222+
expect(client.state.breadcrumbs?.length).toBe(maxBreadcrumbs);
223+
});
224+
225+
it('should limit breadcrumbs amount - with custom limit; all at once', () => {
226+
client.setBreadcrumbs(getBreadcrumbs(maxBreadcrumbs + 2));
227+
228+
expect(client.state.breadcrumbs?.length).toBe(maxBreadcrumbs);
229+
});
230+
231+
it('should limit breadcrumbs amount - with default limit; incremental addition', () => {
232+
const anotherClient = new BrowserMicroSentryClient({});
233+
234+
getBreadcrumbs(MAX_BREADCRUMBS + 2).forEach((bc) =>
235+
anotherClient.addBreadcrumb(bc)
236+
);
237+
238+
expect(anotherClient.state.breadcrumbs?.length).toBe(MAX_BREADCRUMBS);
239+
});
240+
241+
it('should limit breadcrumbs amount - with default limit; all at once', () => {
242+
const anotherClient = new BrowserMicroSentryClient({});
243+
anotherClient.setBreadcrumbs(getBreadcrumbs(MAX_BREADCRUMBS + 2));
244+
245+
expect(anotherClient.state.breadcrumbs?.length).toBe(MAX_BREADCRUMBS);
246+
});
247+
248+
it('should save only last breadcrumbs; incremental addition', () => {
249+
getBreadcrumbs(maxBreadcrumbs + 2).forEach((bc) =>
250+
client.addBreadcrumb(bc)
251+
);
252+
253+
expect(
254+
(client.state.breadcrumbs ?? [])
255+
.map((breadcrumb) => breadcrumb.event_id)
256+
.join(',')
257+
).toEqual('id2,id3,id4,id5,id6,id7,id8,id9,id10,id11');
258+
});
259+
260+
it('should save only last breadcrumbs; all at once', () => {
261+
client.setBreadcrumbs(getBreadcrumbs(maxBreadcrumbs + 2));
262+
263+
expect(
264+
(client.state.breadcrumbs ?? [])
265+
.map((breadcrumb) => breadcrumb.event_id)
266+
.join(',')
267+
).toEqual('id2,id3,id4,id5,id6,id7,id8,id9,id10,id11');
268+
});
269+
270+
it('should not add breadcrumbs at all if maxBreadcrumbs is set to 0; incremental addition', () => {
271+
const anotherClient = new BrowserMicroSentryClient({ maxBreadcrumbs: 0 });
272+
273+
getBreadcrumbs(1).forEach((bc) => anotherClient.addBreadcrumb(bc));
274+
275+
expect(anotherClient.state.breadcrumbs?.length).toBe(0);
276+
});
277+
278+
it('should not add breadcrumbs at all if maxBreadcrumbs is set to 0; all at once', () => {
279+
const anotherClient = new BrowserMicroSentryClient({ maxBreadcrumbs: 0 });
280+
281+
anotherClient.setBreadcrumbs(getBreadcrumbs(1));
282+
283+
expect(anotherClient.state.breadcrumbs?.length).toBe(0);
284+
});
285+
286+
it('should ignore maxBreadcrumbs option if maxBreadcrumbs is a negative number; incremental addition', () => {
287+
const anotherClient = new BrowserMicroSentryClient({
288+
maxBreadcrumbs: -100,
289+
});
290+
291+
getBreadcrumbs(MAX_BREADCRUMBS + 2).forEach((bc) =>
292+
anotherClient.addBreadcrumb(bc)
293+
);
294+
295+
expect(anotherClient.state.breadcrumbs?.length).toBe(MAX_BREADCRUMBS);
296+
});
297+
298+
it('should ignore maxBreadcrumbs option if maxBreadcrumbs is a negative number; all at once', () => {
299+
const anotherClient = new BrowserMicroSentryClient({
300+
maxBreadcrumbs: -100,
301+
});
302+
303+
anotherClient.setBreadcrumbs(getBreadcrumbs(MAX_BREADCRUMBS + 2));
304+
305+
expect(anotherClient.state.breadcrumbs?.length).toBe(MAX_BREADCRUMBS);
306+
});
307+
208308
it('should skip breadcrumbs if beforeBreadcrumb returns null', () => {
209309
client = new BrowserMicroSentryClient({
210310
dsn: 'http://[email protected]/2',
@@ -237,7 +337,7 @@ describe('BrowserMicroSentryClient', () => {
237337
]);
238338
});
239339

240-
afterAll(() => {
340+
afterEach(() => {
241341
client.clearState();
242342
});
243343
});

libs/browser/src/lib/services/browser-micro-sentry-client.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ import { State } from '../models/state';
1212
import { MicroSentryPlugin } from '../models/plugin';
1313
import { BrowserSentryClientOptions } from '../models/browser-sentry-client-options';
1414
import { isMatchingPattern } from '../utils/is-matching-pattern';
15+
import { MAX_BREADCRUMBS } from '../consts/max-breadcrumbs';
1516

1617
function getWindow(): Window {
1718
return window;
1819
}
1920

2021
export class BrowserMicroSentryClient extends MicroSentryClient {
22+
private readonly breadcrumbsKeyName = 'breadcrumbs';
2123
private destroyed = false;
2224
private readonly plugins: MicroSentryPlugin[];
2325
private readonly beforeSend: NonNullable<
@@ -33,6 +35,7 @@ export class BrowserMicroSentryClient extends MicroSentryClient {
3335
BrowserSentryClientOptions['ignoreErrors']
3436
>;
3537
private readonly release?: string;
38+
private readonly maxBreadcrumbs: number;
3639

3740
constructor(
3841
private options: BrowserSentryClientOptions,
@@ -47,14 +50,17 @@ export class BrowserMicroSentryClient extends MicroSentryClient {
4750
blacklistUrls = [],
4851
ignoreErrors = [],
4952
release = undefined,
50-
} = this.options || {};
53+
maxBreadcrumbs = MAX_BREADCRUMBS,
54+
} = this.options || {} || [];
5155

5256
this.plugins = plugins.map((Plugin) => new Plugin(this));
5357
this.beforeSend = beforeSend;
5458
this.beforeBreadcrumb = beforeBreadcrumb;
5559
this.blacklistUrls = blacklistUrls;
5660
this.ignoreErrors = ignoreErrors;
5761
this.release = release;
62+
this.maxBreadcrumbs =
63+
maxBreadcrumbs >= 0 ? maxBreadcrumbs : MAX_BREADCRUMBS;
5864
}
5965

6066
protected _state: State = {};
@@ -126,17 +132,20 @@ export class BrowserMicroSentryClient extends MicroSentryClient {
126132
}
127133

128134
this.extendState({
129-
breadcrumbs: [
135+
[this.breadcrumbsKeyName]: [
130136
{
131137
timestamp: Date.now() / 1_000,
132138
...result,
133139
},
134140
],
135141
});
142+
143+
this.trimBreadcrumbs();
136144
}
137145

138146
setBreadcrumbs(breadcrumbs: Breadcrumb[] | undefined) {
139-
this.setKeyState('breadcrumbs', breadcrumbs);
147+
this.setKeyState(this.breadcrumbsKeyName, breadcrumbs);
148+
this.trimBreadcrumbs();
140149
}
141150

142151
captureMessage(message: string, level?: Severity) {
@@ -307,4 +316,18 @@ export class BrowserMicroSentryClient extends MicroSentryClient {
307316
private setKeyState<T extends keyof State>(key: T, value: State[T]) {
308317
this._state[key] = value;
309318
}
319+
320+
private getKeyState<T extends keyof State>(key: T): State[T] {
321+
return this._state[key];
322+
}
323+
324+
private trimBreadcrumbs(): void {
325+
const breadcrumbs = this.getKeyState(this.breadcrumbsKeyName);
326+
if (breadcrumbs && (breadcrumbs.length ?? 0) > this.maxBreadcrumbs) {
327+
this.setKeyState(
328+
this.breadcrumbsKeyName,
329+
this.maxBreadcrumbs > 0 ? breadcrumbs.slice(-this.maxBreadcrumbs) : []
330+
);
331+
}
332+
}
310333
}

0 commit comments

Comments
 (0)