Skip to content

Commit a257a8a

Browse files
authored
feat: Provide normalizeDepth option and sensible default for scope methods (getsentry#2404)
1 parent 8be101f commit a257a8a

File tree

9 files changed

+225
-31
lines changed

9 files changed

+225
-31
lines changed

CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
## Unreleased
44

5+
- [core] feat: Provide `normalizeDepth` option and sensible default for scope methods (#2404)
6+
- [browser] fix: Export `EventHint` type (#2407)
7+
58
## 5.11.2
69

710
- [apm] fix: Add new option to `Tracing` `maxTransactionTimeout` determines the max length of a transaction
811
- [hub] ref: Always also set transaction name on the top span in the scope
9-
- [core] fix: Use event_id from hint given by top-level hub calls
12+
- [core] fix: Use `event_id` from hint given by top-level hub calls
1013

1114
## 5.11.1
1215

packages/browser/src/helpers.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { captureException, withScope } from '@sentry/core';
22
import { Event as SentryEvent, Mechanism, Scope, WrappedFunction } from '@sentry/types';
3-
import { addExceptionMechanism, addExceptionTypeValue, normalize } from '@sentry/utils';
3+
import { addExceptionMechanism, addExceptionTypeValue } from '@sentry/utils';
44

55
let ignoreOnError: number = 0;
66

@@ -98,7 +98,7 @@ export function wrap(
9898

9999
processedEvent.extra = {
100100
...processedEvent.extra,
101-
arguments: normalize(args, 3),
101+
arguments: args,
102102
};
103103

104104
return processedEvent;

packages/browser/src/integrations/breadcrumbs.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
getGlobalObject,
77
htmlTreeAsString,
88
logger,
9-
normalize,
109
parseUrl,
1110
safeJoin,
1211
} from '@sentry/utils';
@@ -75,9 +74,7 @@ export class Breadcrumbs implements Integration {
7574
const breadcrumb = {
7675
category: 'console',
7776
data: {
78-
extra: {
79-
arguments: normalize(handlerData.args, 3),
80-
},
77+
arguments: handlerData.args,
8178
logger: 'console',
8279
},
8380
level: Severity.fromString(handlerData.level),
@@ -87,7 +84,7 @@ export class Breadcrumbs implements Integration {
8784
if (handlerData.level === 'assert') {
8885
if (handlerData.args[0] === false) {
8986
breadcrumb.message = `Assertion failed: ${safeJoin(handlerData.args.slice(1), ' ') || 'console.assert'}`;
90-
breadcrumb.data.extra.arguments = normalize(handlerData.args.slice(1), 3);
87+
breadcrumb.data.arguments = handlerData.args.slice(1);
9188
} else {
9289
// Don't capture a breadcrumb for passed assertions
9390
return;

packages/core/src/baseclient.ts

+47-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Scope } from '@sentry/hub';
22
import { Client, Event, EventHint, Integration, IntegrationClass, Options, SdkInfo, Severity } from '@sentry/types';
3-
import { Dsn, isPrimitive, isThenable, logger, SyncPromise, truncate, uuid4 } from '@sentry/utils';
3+
import { Dsn, isPrimitive, isThenable, logger, normalize, SyncPromise, truncate, uuid4 } from '@sentry/utils';
44

55
import { Backend, BackendClass } from './basebackend';
66
import { IntegrationIndex, setupIntegrations } from './integration';
@@ -256,7 +256,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
256256
* @returns A new event with more information.
257257
*/
258258
protected _prepareEvent(event: Event, scope?: Scope, hint?: EventHint): PromiseLike<Event | null> {
259-
const { environment, release, dist, maxValueLength = 250 } = this.getOptions();
259+
const { environment, release, dist, maxValueLength = 250, normalizeDepth = 3 } = this.getOptions();
260260

261261
const prepared: Event = { ...event };
262262
if (prepared.environment === undefined && environment !== undefined) {
@@ -300,7 +300,51 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
300300
result = scope.applyToEvent(prepared, hint);
301301
}
302302

303-
return result;
303+
return result.then(evt => {
304+
// tslint:disable-next-line:strict-type-predicates
305+
if (typeof normalizeDepth === 'number' && normalizeDepth > 0) {
306+
return this._normalizeEvent(evt, normalizeDepth);
307+
}
308+
return evt;
309+
});
310+
}
311+
312+
/**
313+
* Applies `normalize` function on necessary `Event` attributes to make them safe for serialization.
314+
* Normalized keys:
315+
* - `breadcrumbs.data`
316+
* - `user`
317+
* - `contexts`
318+
* - `extra`
319+
* @param event Event
320+
* @returns Normalized event
321+
*/
322+
protected _normalizeEvent(event: Event | null, depth: number): Event | null {
323+
if (!event) {
324+
return null;
325+
}
326+
327+
// tslint:disable:no-unsafe-any
328+
return {
329+
...event,
330+
...(event.breadcrumbs && {
331+
breadcrumbs: event.breadcrumbs.map(b => ({
332+
...b,
333+
...(b.data && {
334+
data: normalize(b.data, depth),
335+
}),
336+
})),
337+
}),
338+
...(event.user && {
339+
user: normalize(event.user, depth),
340+
}),
341+
...(event.contexts && {
342+
contexts: normalize(event.contexts, depth),
343+
}),
344+
...(event.extra && {
345+
extra: normalize(event.extra, depth),
346+
}),
347+
};
304348
}
305349

306350
/**

packages/core/test/lib/base.test.ts

+137
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,143 @@ describe('BaseClient', () => {
329329
});
330330
});
331331

332+
test('normalizes event with default depth of 3', () => {
333+
expect.assertions(1);
334+
const client = new TestClient({ dsn: PUBLIC_DSN });
335+
const fourLevelsObject = {
336+
a: {
337+
b: {
338+
c: 'wat',
339+
d: {
340+
e: 'wat',
341+
},
342+
},
343+
},
344+
};
345+
const normalizedObject = {
346+
a: {
347+
b: {
348+
c: 'wat',
349+
d: '[Object]',
350+
},
351+
},
352+
};
353+
const fourLevelBreadcrumb = {
354+
data: fourLevelsObject,
355+
message: 'wat',
356+
};
357+
const normalizedBreadcrumb = {
358+
data: normalizedObject,
359+
message: 'wat',
360+
};
361+
client.captureEvent({
362+
breadcrumbs: [fourLevelBreadcrumb, fourLevelBreadcrumb, fourLevelBreadcrumb],
363+
contexts: fourLevelsObject,
364+
extra: fourLevelsObject,
365+
user: fourLevelsObject,
366+
});
367+
expect(TestBackend.instance!.event!).toEqual({
368+
breadcrumbs: [normalizedBreadcrumb, normalizedBreadcrumb, normalizedBreadcrumb],
369+
contexts: normalizedObject,
370+
event_id: '42',
371+
extra: normalizedObject,
372+
user: normalizedObject,
373+
});
374+
});
375+
376+
test('normalization respects `normalizeDepth` option', () => {
377+
expect.assertions(1);
378+
const client = new TestClient({
379+
dsn: PUBLIC_DSN,
380+
normalizeDepth: 2,
381+
});
382+
const fourLevelsObject = {
383+
a: {
384+
b: {
385+
c: 'wat',
386+
d: {
387+
e: 'wat',
388+
},
389+
},
390+
},
391+
};
392+
const normalizedObject = {
393+
a: {
394+
b: '[Object]',
395+
},
396+
};
397+
const fourLevelBreadcrumb = {
398+
data: fourLevelsObject,
399+
message: 'wat',
400+
};
401+
const normalizedBreadcrumb = {
402+
data: normalizedObject,
403+
message: 'wat',
404+
};
405+
client.captureEvent({
406+
breadcrumbs: [fourLevelBreadcrumb, fourLevelBreadcrumb, fourLevelBreadcrumb],
407+
contexts: fourLevelsObject,
408+
extra: fourLevelsObject,
409+
user: fourLevelsObject,
410+
});
411+
expect(TestBackend.instance!.event!).toEqual({
412+
breadcrumbs: [normalizedBreadcrumb, normalizedBreadcrumb, normalizedBreadcrumb],
413+
contexts: normalizedObject,
414+
event_id: '42',
415+
extra: normalizedObject,
416+
user: normalizedObject,
417+
});
418+
});
419+
420+
test('skips normalization when `normalizeDepth: 0`', () => {
421+
expect.assertions(1);
422+
const client = new TestClient({
423+
dsn: PUBLIC_DSN,
424+
normalizeDepth: 0,
425+
});
426+
const fourLevelsObject = {
427+
a: {
428+
b: {
429+
c: 'wat',
430+
d: {
431+
e: 'wat',
432+
},
433+
},
434+
},
435+
};
436+
const normalizedObject = {
437+
a: {
438+
b: {
439+
c: 'wat',
440+
d: {
441+
e: 'wat',
442+
},
443+
},
444+
},
445+
};
446+
const fourLevelBreadcrumb = {
447+
data: fourLevelsObject,
448+
message: 'wat',
449+
};
450+
const normalizedBreadcrumb = {
451+
data: normalizedObject,
452+
message: 'wat',
453+
};
454+
client.captureEvent({
455+
breadcrumbs: [fourLevelBreadcrumb, fourLevelBreadcrumb, fourLevelBreadcrumb],
456+
contexts: fourLevelsObject,
457+
extra: fourLevelsObject,
458+
user: fourLevelsObject,
459+
});
460+
expect(TestBackend.instance!.event!).toEqual({
461+
breadcrumbs: [normalizedBreadcrumb, normalizedBreadcrumb, normalizedBreadcrumb],
462+
contexts: normalizedObject,
463+
event_id: '42',
464+
extra: normalizedObject,
465+
user: normalizedObject,
466+
});
467+
});
468+
332469
test('calls beforeSend and uses original event without any changes', () => {
333470
expect.assertions(1);
334471
const beforeSend = jest.fn(event => event);

packages/hub/src/scope.ts

+17-15
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
Span,
99
User,
1010
} from '@sentry/types';
11-
import { getGlobalObject, isThenable, normalize, SyncPromise, timestampWithMs } from '@sentry/utils';
11+
import { getGlobalObject, isThenable, SyncPromise, timestampWithMs } from '@sentry/utils';
1212

1313
/**
1414
* Holds additional event information. {@link Scope.applyToEvent} will be
@@ -115,7 +115,7 @@ export class Scope implements ScopeInterface {
115115
* @inheritDoc
116116
*/
117117
public setUser(user: User | null): this {
118-
this._user = normalize(user);
118+
this._user = user || {};
119119
this._notifyScopeListeners();
120120
return this;
121121
}
@@ -126,7 +126,7 @@ export class Scope implements ScopeInterface {
126126
public setTags(tags: { [key: string]: string }): this {
127127
this._tags = {
128128
...this._tags,
129-
...normalize(tags),
129+
...tags,
130130
};
131131
this._notifyScopeListeners();
132132
return this;
@@ -136,18 +136,18 @@ export class Scope implements ScopeInterface {
136136
* @inheritDoc
137137
*/
138138
public setTag(key: string, value: string): this {
139-
this._tags = { ...this._tags, [key]: normalize(value) };
139+
this._tags = { ...this._tags, [key]: value };
140140
this._notifyScopeListeners();
141141
return this;
142142
}
143143

144144
/**
145145
* @inheritDoc
146146
*/
147-
public setExtras(extra: { [key: string]: any }): this {
147+
public setExtras(extras: { [key: string]: any }): this {
148148
this._extra = {
149149
...this._extra,
150-
...normalize(extra),
150+
...extras,
151151
};
152152
this._notifyScopeListeners();
153153
return this;
@@ -157,7 +157,7 @@ export class Scope implements ScopeInterface {
157157
* @inheritDoc
158158
*/
159159
public setExtra(key: string, extra: any): this {
160-
this._extra = { ...this._extra, [key]: normalize(extra) };
160+
this._extra = { ...this._extra, [key]: extra };
161161
this._notifyScopeListeners();
162162
return this;
163163
}
@@ -166,7 +166,7 @@ export class Scope implements ScopeInterface {
166166
* @inheritDoc
167167
*/
168168
public setFingerprint(fingerprint: string[]): this {
169-
this._fingerprint = normalize(fingerprint);
169+
this._fingerprint = fingerprint;
170170
this._notifyScopeListeners();
171171
return this;
172172
}
@@ -175,7 +175,7 @@ export class Scope implements ScopeInterface {
175175
* @inheritDoc
176176
*/
177177
public setLevel(level: Severity): this {
178-
this._level = normalize(level);
178+
this._level = level;
179179
this._notifyScopeListeners();
180180
return this;
181181
}
@@ -195,8 +195,8 @@ export class Scope implements ScopeInterface {
195195
/**
196196
* @inheritDoc
197197
*/
198-
public setContext(name: string, context: { [key: string]: any } | null): this {
199-
this._context[name] = context ? normalize(context) : undefined;
198+
public setContext(key: string, context: { [key: string]: any } | null): this {
199+
this._context = { ...this._context, [key]: context };
200200
this._notifyScopeListeners();
201201
return this;
202202
}
@@ -260,13 +260,15 @@ export class Scope implements ScopeInterface {
260260
* @inheritDoc
261261
*/
262262
public addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number): this {
263-
const timestamp = timestampWithMs();
264-
const mergedBreadcrumb = { timestamp, ...breadcrumb };
263+
const mergedBreadcrumb = {
264+
timestamp: timestampWithMs(),
265+
...breadcrumb,
266+
};
265267

266268
this._breadcrumbs =
267269
maxBreadcrumbs !== undefined && maxBreadcrumbs >= 0
268-
? [...this._breadcrumbs, normalize(mergedBreadcrumb)].slice(-maxBreadcrumbs)
269-
: [...this._breadcrumbs, normalize(mergedBreadcrumb)];
270+
? [...this._breadcrumbs, mergedBreadcrumb].slice(-maxBreadcrumbs)
271+
: [...this._breadcrumbs, mergedBreadcrumb];
270272
this._notifyScopeListeners();
271273
return this;
272274
}

packages/hub/test/scope.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describe('Scope', () => {
3535
const scope = new Scope();
3636
scope.setExtra('a', 1);
3737
scope.setExtras({ a: undefined });
38-
expect((scope as any)._extra).toEqual({ a: '[undefined]' });
38+
expect((scope as any)._extra).toEqual({ a: undefined });
3939
});
4040
});
4141

@@ -63,7 +63,7 @@ describe('Scope', () => {
6363
const scope = new Scope();
6464
scope.setUser({ id: '1' });
6565
scope.setUser(null);
66-
expect((scope as any)._user).toEqual(null);
66+
expect((scope as any)._user).toEqual({});
6767
});
6868
});
6969

0 commit comments

Comments
 (0)