Skip to content

Commit 06d6bd8

Browse files
authored
feat: Allow for attaching metadata and pass it to the API and transports (#3177)
* feat: Allow for attaching metadata and pass it to the API and transports
1 parent 7095822 commit 06d6bd8

40 files changed

+368
-375
lines changed

packages/angular/src/errorhandler.ts

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,6 @@ class SentryErrorHandler implements AngularErrorHandler {
2929
logErrors: true,
3030
...options,
3131
};
32-
33-
Sentry.configureScope(scope => {
34-
scope.addEventProcessor(event => {
35-
event.sdk = {
36-
...event.sdk,
37-
name: 'sentry.javascript.angular',
38-
packages: [
39-
...((event.sdk && event.sdk.packages) || []),
40-
{
41-
name: 'npm:@sentry/angular',
42-
version: Sentry.SDK_VERSION,
43-
},
44-
],
45-
version: Sentry.SDK_VERSION,
46-
};
47-
48-
return event;
49-
});
50-
});
5132
}
5233

5334
/**

packages/angular/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export * from '@sentry/browser';
2+
3+
export { init } from './sdk';
24
export { createErrorHandler, ErrorHandlerOptions } from './errorhandler';
35
export {
46
getActiveTransaction,

packages/angular/src/sdk.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { BrowserOptions, init as browserInit, SDK_VERSION } from '@sentry/browser';
2+
3+
/**
4+
* Inits the Angular SDK
5+
*/
6+
export function init(options: BrowserOptions): void {
7+
options._metadata = options._metadata || {};
8+
options._metadata.sdk = {
9+
name: 'sentry.javascript.angular',
10+
packages: [
11+
{
12+
name: 'npm:@sentry/angular',
13+
version: SDK_VERSION,
14+
},
15+
],
16+
version: SDK_VERSION,
17+
};
18+
browserInit(options);
19+
}

packages/browser/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@
8181
"size:check": "run-p size:check:es5 size:check:es6",
8282
"size:check:es5": "cat build/bundle.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES5: \",$1,\"kB\";}'",
8383
"size:check:es6": "cat build/bundle.es6.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES6: \",$1,\"kB\";}'",
84-
"version": "node ../../scripts/versionbump.js src/version.ts",
8584
"pack": "npm pack"
8685
},
8786
"volta": {

packages/browser/src/backend.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export class BrowserBackend extends BaseBackend<BrowserOptions> {
6767
const transportOptions = {
6868
...this._options.transportOptions,
6969
dsn: this._options.dsn,
70+
_metadata: this._options._metadata,
7071
};
7172

7273
if (this._options.transport) {

packages/browser/src/client.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { getGlobalObject, logger } from '@sentry/utils';
55
import { BrowserBackend, BrowserOptions } from './backend';
66
import { injectReportDialog, ReportDialogOptions } from './helpers';
77
import { Breadcrumbs } from './integrations';
8-
import { SDK_NAME, SDK_VERSION } from './version';
98

109
/**
1110
* The Sentry Browser SDK Client.
@@ -51,19 +50,6 @@ export class BrowserClient extends BaseClient<BrowserBackend, BrowserOptions> {
5150
*/
5251
protected _prepareEvent(event: Event, scope?: Scope, hint?: EventHint): PromiseLike<Event | null> {
5352
event.platform = event.platform || 'javascript';
54-
event.sdk = {
55-
...event.sdk,
56-
name: SDK_NAME,
57-
packages: [
58-
...((event.sdk && event.sdk.packages) || []),
59-
{
60-
name: 'npm:@sentry/browser',
61-
version: SDK_VERSION,
62-
},
63-
],
64-
version: SDK_VERSION,
65-
};
66-
6753
return super._prepareEvent(event, scope, hint);
6854
}
6955

packages/browser/src/exports.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export {
2828
makeMain,
2929
Scope,
3030
startTransaction,
31+
SDK_VERSION,
3132
setContext,
3233
setExtra,
3334
setExtras,
@@ -42,4 +43,4 @@ export { BrowserClient } from './client';
4243
export { injectReportDialog, ReportDialogOptions } from './helpers';
4344
export { eventFromException, eventFromMessage } from './eventbuilder';
4445
export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk';
45-
export { SDK_NAME, SDK_VERSION } from './version';
46+
export { SDK_NAME } from './version';

packages/browser/src/sdk.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core';
1+
import { getCurrentHub, initAndBind, Integrations as CoreIntegrations, SDK_VERSION } from '@sentry/core';
22
import { getGlobalObject, SyncPromise } from '@sentry/utils';
33

44
import { BrowserOptions } from './backend';
@@ -88,6 +88,18 @@ export function init(options: BrowserOptions = {}): void {
8888
options.autoSessionTracking = false;
8989
}
9090

91+
options._metadata = options._metadata || {};
92+
options._metadata.sdk = {
93+
name: 'sentry.javascript.browser',
94+
packages: [
95+
{
96+
name: 'npm:@sentry/browser',
97+
version: SDK_VERSION,
98+
},
99+
],
100+
version: SDK_VERSION,
101+
};
102+
91103
initAndBind(BrowserClient, options);
92104

93105
if (options.autoSessionTracking) {

packages/browser/src/transports/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export abstract class BaseTransport implements Transport {
2626
protected readonly _rateLimits: Record<string, Date> = {};
2727

2828
public constructor(public options: TransportOptions) {
29-
this._api = new API(this.options.dsn);
29+
this._api = new API(options.dsn, options._metadata);
3030
// eslint-disable-next-line deprecation/deprecation
3131
this.url = this._api.getStoreEndpointWithUrlEncodedAuth();
3232
}

packages/browser/src/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1+
// TODO: Remove in the next major release and rely only on @sentry/core SDK_VERSION and SdkInfo metadata
12
export const SDK_NAME = 'sentry.javascript.browser';
2-
export const SDK_VERSION = '5.30.0';

packages/core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"fix:eslint": "eslint . --format stylish --fix",
4949
"test": "jest",
5050
"test:watch": "jest --watch",
51-
"pack": "npm pack"
51+
"pack": "npm pack",
52+
"version": "node ../../scripts/versionbump.js src/version.ts"
5253
},
5354
"volta": {
5455
"extends": "../../package.json"

packages/core/src/api.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
import { DsnLike } from '@sentry/types';
1+
import { DsnLike, SdkMetadata } from '@sentry/types';
22
import { Dsn, urlEncode } from '@sentry/utils';
33

44
const SENTRY_API_VERSION = '7';
55

6-
/** Helper class to provide urls to different Sentry endpoints. */
6+
/**
7+
* Helper class to provide urls, headers and metadata that can be used to form
8+
* different types of requests to Sentry endpoints.
9+
* Supports both envelopes and regular event requests.
10+
**/
711
export class API {
812
/** The internally used Dsn object. */
913
private readonly _dsnObject: Dsn;
1014
/** Create a new instance of API */
11-
public constructor(public dsn: DsnLike) {
15+
public constructor(public dsn: DsnLike, public metadata: SdkMetadata = {}) {
1216
this._dsnObject = new Dsn(dsn);
1317
}
1418

@@ -59,6 +63,7 @@ export class API {
5963
* This is needed for node and the old /store endpoint in sentry
6064
*/
6165
public getRequestHeaders(clientName: string, clientVersion: string): { [key: string]: string } {
66+
// CHANGE THIS to use metadata but keep clientName and clientVersion compatible
6267
const dsn = this._dsnObject;
6368
const header = [`Sentry sentry_version=${SENTRY_API_VERSION}`];
6469
header.push(`sentry_client=${clientName}/${clientVersion}`);

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export { BackendClass, BaseBackend } from './basebackend';
2020
export { eventToSentryRequest, sessionToSentryRequest } from './request';
2121
export { initAndBind, ClientClass } from './sdk';
2222
export { NoopTransport } from './transports/noop';
23+
export { SDK_VERSION } from './version';
2324

2425
import * as Integrations from './integrations';
2526

packages/core/src/request.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,42 @@
1-
import { Event, SentryRequest, Session } from '@sentry/types';
1+
import { Event, SdkInfo, SentryRequest, Session } from '@sentry/types';
22

33
import { API } from './api';
44

5+
/** Extract sdk info from from the API metadata */
6+
function getSdkMetadataForEnvelopeHeader(api: API): SdkInfo | undefined {
7+
if (!api.metadata || !api.metadata.sdk) {
8+
return;
9+
}
10+
const { name, version } = api.metadata.sdk;
11+
return { name, version };
12+
}
13+
14+
/**
15+
* Apply SdkInfo (name, version, packages, integrations) to the corresponding event key.
16+
* Merge with existing data if any.
17+
**/
18+
function enhanceEventWithSdkInfo(event: Event, sdkInfo?: SdkInfo): Event {
19+
if (!sdkInfo) {
20+
return event;
21+
}
22+
23+
event.sdk = event.sdk || {
24+
name: sdkInfo.name,
25+
version: sdkInfo.version,
26+
};
27+
event.sdk.name = event.sdk.name || sdkInfo.name;
28+
event.sdk.version = event.sdk.version || sdkInfo.version;
29+
event.sdk.integrations = [...(event.sdk.integrations || []), ...(sdkInfo.integrations || [])];
30+
event.sdk.packages = [...(event.sdk.packages || []), ...(sdkInfo.packages || [])];
31+
return event;
32+
}
33+
534
/** Creates a SentryRequest from an event. */
635
export function sessionToSentryRequest(session: Session, api: API): SentryRequest {
36+
const sdkInfo = getSdkMetadataForEnvelopeHeader(api);
737
const envelopeHeaders = JSON.stringify({
838
sent_at: new Date().toISOString(),
39+
...(sdkInfo && { sdk: sdkInfo }),
940
});
1041
const itemHeaders = JSON.stringify({
1142
type: 'session',
@@ -24,11 +55,13 @@ export function eventToSentryRequest(event: Event, api: API): SentryRequest {
2455
const { __sentry_samplingMethod: samplingMethod, __sentry_sampleRate: sampleRate, ...otherTags } = event.tags || {};
2556
event.tags = otherTags;
2657

27-
const useEnvelope = event.type === 'transaction';
58+
const sdkInfo = getSdkMetadataForEnvelopeHeader(api);
59+
const eventType = event.type || 'event';
60+
const useEnvelope = eventType === 'transaction';
2861

2962
const req: SentryRequest = {
30-
body: JSON.stringify(event),
31-
type: event.type || 'event',
63+
body: JSON.stringify(sdkInfo ? enhanceEventWithSdkInfo(event, api.metadata.sdk) : event),
64+
type: eventType,
3265
url: useEnvelope ? api.getEnvelopeEndpointWithUrlEncodedAuth() : api.getStoreEndpointWithUrlEncodedAuth(),
3366
};
3467

@@ -42,6 +75,7 @@ export function eventToSentryRequest(event: Event, api: API): SentryRequest {
4275
const envelopeHeaders = JSON.stringify({
4376
event_id: event.event_id,
4477
sent_at: new Date().toISOString(),
78+
...(sdkInfo && { sdk: sdkInfo }),
4579
});
4680
const itemHeaders = JSON.stringify({
4781
type: event.type,

packages/core/src/version.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const SDK_VERSION = '5.30.0';

packages/core/test/lib/request.test.ts

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,29 @@ import { API } from '../../src/api';
44
import { eventToSentryRequest } from '../../src/request';
55

66
describe('eventToSentryRequest', () => {
7-
const api = new API('https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012');
8-
const event: Event = {
9-
contexts: { trace: { trace_id: '1231201211212012', span_id: '12261980', op: 'pageload' } },
10-
environment: 'dogpark',
11-
event_id: '0908201304152013',
12-
release: 'off.leash.park',
13-
spans: [],
14-
transaction: '/dogs/are/great/',
15-
type: 'transaction',
16-
user: { id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12' },
17-
};
7+
let api: API;
8+
let event: Event;
9+
10+
beforeEach(() => {
11+
api = new API('https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', {
12+
sdk: {
13+
integrations: ['AWSLambda'],
14+
name: 'sentry.javascript.browser',
15+
version: `12.31.12`,
16+
packages: [{ name: 'npm:@sentry/browser', version: `6.6.6` }],
17+
},
18+
});
19+
event = {
20+
contexts: { trace: { trace_id: '1231201211212012', span_id: '12261980', op: 'pageload' } },
21+
environment: 'dogpark',
22+
event_id: '0908201304152013',
23+
release: 'off.leash.park',
24+
spans: [],
25+
transaction: '/dogs/are/great/',
26+
type: 'transaction',
27+
user: { id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12' },
28+
};
29+
});
1830

1931
[
2032
{ method: TransactionSamplingMethod.Rate, rate: '0.1121', dog: 'Charlie' },
@@ -30,7 +42,7 @@ describe('eventToSentryRequest', () => {
3042
// TODO kmclb - once tag types are loosened, don't need to cast to string here
3143
event.tags = { __sentry_samplingMethod: String(method), __sentry_sampleRate: String(rate), dog };
3244

33-
const result = eventToSentryRequest(event as Event, api);
45+
const result = eventToSentryRequest(event, api);
3446

3547
const [envelopeHeaderString, itemHeaderString, eventString] = result.body.split('\n');
3648

@@ -53,4 +65,60 @@ describe('eventToSentryRequest', () => {
5365
expect('dog' in envelope.event.tags).toBe(true);
5466
});
5567
});
68+
69+
it('adds sdk info to envelope header', () => {
70+
const result = eventToSentryRequest(event, api);
71+
72+
const envelopeHeaderString = result.body.split('\n')[0];
73+
const parsedHeader = JSON.parse(envelopeHeaderString);
74+
75+
expect(parsedHeader).toEqual(
76+
expect.objectContaining({ sdk: { name: 'sentry.javascript.browser', version: '12.31.12' } }),
77+
);
78+
});
79+
80+
it('adds sdk info to event body', () => {
81+
const result = eventToSentryRequest(event, api);
82+
83+
const eventString = result.body.split('\n')[2];
84+
const parsedEvent = JSON.parse(eventString);
85+
86+
expect(parsedEvent).toEqual(
87+
expect.objectContaining({
88+
sdk: {
89+
integrations: ['AWSLambda'],
90+
name: 'sentry.javascript.browser',
91+
version: `12.31.12`,
92+
packages: [{ name: 'npm:@sentry/browser', version: `6.6.6` }],
93+
},
94+
}),
95+
);
96+
});
97+
98+
it('merges existing sdk info if one is present on the event body', () => {
99+
event.sdk = {
100+
integrations: ['Clojure'],
101+
name: 'foo',
102+
packages: [{ name: 'npm:@sentry/clj', version: `6.6.6` }],
103+
version: '1337',
104+
};
105+
const result = eventToSentryRequest(event, api);
106+
107+
const eventString = result.body.split('\n')[2];
108+
const parsedEvent = JSON.parse(eventString);
109+
110+
expect(parsedEvent).toEqual(
111+
expect.objectContaining({
112+
sdk: {
113+
integrations: ['Clojure', 'AWSLambda'],
114+
name: 'foo',
115+
packages: [
116+
{ name: 'npm:@sentry/clj', version: `6.6.6` },
117+
{ name: 'npm:@sentry/browser', version: `6.6.6` },
118+
],
119+
version: '1337',
120+
},
121+
}),
122+
);
123+
});
56124
});

0 commit comments

Comments
 (0)