Skip to content

feat: Allow for attaching metadata and pass it to the API and transports #3177

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 4 commits into from
Jan 18, 2021
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
19 changes: 0 additions & 19 deletions packages/angular/src/errorhandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,6 @@ class SentryErrorHandler implements AngularErrorHandler {
logErrors: true,
...options,
};

Sentry.configureScope(scope => {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.angular',
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/angular',
version: Sentry.SDK_VERSION,
},
],
version: Sentry.SDK_VERSION,
};

return event;
});
});
}

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/angular/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from '@sentry/browser';

export { init } from './sdk';
export { createErrorHandler, ErrorHandlerOptions } from './errorhandler';
export {
getActiveTransaction,
Expand Down
19 changes: 19 additions & 0 deletions packages/angular/src/sdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { BrowserOptions, init as browserInit, SDK_VERSION } from '@sentry/browser';

/**
* Inits the Angular SDK
*/
export function init(options: BrowserOptions): void {
options._metadata = options._metadata || {};
options._metadata.sdk = {
name: 'sentry.javascript.angular',
packages: [
{
name: 'npm:@sentry/angular',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};
browserInit(options);
}
1 change: 0 additions & 1 deletion packages/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
"size:check": "run-p size:check:es5 size:check:es6",
"size:check:es5": "cat build/bundle.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES5: \",$1,\"kB\";}'",
"size:check:es6": "cat build/bundle.es6.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES6: \",$1,\"kB\";}'",
"version": "node ../../scripts/versionbump.js src/version.ts",
"pack": "npm pack"
},
"volta": {
Expand Down
1 change: 1 addition & 0 deletions packages/browser/src/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class BrowserBackend extends BaseBackend<BrowserOptions> {
const transportOptions = {
...this._options.transportOptions,
dsn: this._options.dsn,
_metadata: this._options._metadata,
};

if (this._options.transport) {
Expand Down
14 changes: 0 additions & 14 deletions packages/browser/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { getGlobalObject, logger } from '@sentry/utils';
import { BrowserBackend, BrowserOptions } from './backend';
import { injectReportDialog, ReportDialogOptions } from './helpers';
import { Breadcrumbs } from './integrations';
import { SDK_NAME, SDK_VERSION } from './version';

/**
* The Sentry Browser SDK Client.
Expand Down Expand Up @@ -51,19 +50,6 @@ export class BrowserClient extends BaseClient<BrowserBackend, BrowserOptions> {
*/
protected _prepareEvent(event: Event, scope?: Scope, hint?: EventHint): PromiseLike<Event | null> {
event.platform = event.platform || 'javascript';
event.sdk = {
...event.sdk,
name: SDK_NAME,
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/browser',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

return super._prepareEvent(event, scope, hint);
}

Expand Down
3 changes: 2 additions & 1 deletion packages/browser/src/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export {
makeMain,
Scope,
startTransaction,
SDK_VERSION,
setContext,
setExtra,
setExtras,
Expand All @@ -42,4 +43,4 @@ export { BrowserClient } from './client';
export { injectReportDialog, ReportDialogOptions } from './helpers';
export { eventFromException, eventFromMessage } from './eventbuilder';
export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog, flush, close, wrap } from './sdk';
export { SDK_NAME, SDK_VERSION } from './version';
export { SDK_NAME } from './version';
14 changes: 13 additions & 1 deletion packages/browser/src/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core';
import { getCurrentHub, initAndBind, Integrations as CoreIntegrations, SDK_VERSION } from '@sentry/core';
import { getGlobalObject, SyncPromise } from '@sentry/utils';

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

options._metadata = options._metadata || {};
options._metadata.sdk = {
name: 'sentry.javascript.browser',
packages: [
{
name: 'npm:@sentry/browser',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

initAndBind(BrowserClient, options);

if (options.autoSessionTracking) {
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/transports/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export abstract class BaseTransport implements Transport {
protected readonly _rateLimits: Record<string, Date> = {};

public constructor(public options: TransportOptions) {
this._api = new API(this.options.dsn);
this._api = new API(options.dsn, options._metadata);
// eslint-disable-next-line deprecation/deprecation
this.url = this._api.getStoreEndpointWithUrlEncodedAuth();
}
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// TODO: Remove in the next major release and rely only on @sentry/core SDK_VERSION and SdkInfo metadata
export const SDK_NAME = 'sentry.javascript.browser';
export const SDK_VERSION = '5.30.0';
3 changes: 2 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"fix:eslint": "eslint . --format stylish --fix",
"test": "jest",
"test:watch": "jest --watch",
"pack": "npm pack"
"pack": "npm pack",
"version": "node ../../scripts/versionbump.js src/version.ts"
},
"volta": {
"extends": "../../package.json"
Expand Down
11 changes: 8 additions & 3 deletions packages/core/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { DsnLike } from '@sentry/types';
import { DsnLike, SdkMetadata } from '@sentry/types';
import { Dsn, urlEncode } from '@sentry/utils';

const SENTRY_API_VERSION = '7';

/** Helper class to provide urls to different Sentry endpoints. */
/**
* Helper class to provide urls, headers and metadata that can be used to form
* different types of requests to Sentry endpoints.
* Supports both envelopes and regular event requests.
**/
export class API {
/** The internally used Dsn object. */
private readonly _dsnObject: Dsn;
/** Create a new instance of API */
public constructor(public dsn: DsnLike) {
public constructor(public dsn: DsnLike, public metadata: SdkMetadata = {}) {
this._dsnObject = new Dsn(dsn);
}

Expand Down Expand Up @@ -59,6 +63,7 @@ export class API {
* This is needed for node and the old /store endpoint in sentry
*/
public getRequestHeaders(clientName: string, clientVersion: string): { [key: string]: string } {
// CHANGE THIS to use metadata but keep clientName and clientVersion compatible
const dsn = this._dsnObject;
const header = [`Sentry sentry_version=${SENTRY_API_VERSION}`];
header.push(`sentry_client=${clientName}/${clientVersion}`);
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { BackendClass, BaseBackend } from './basebackend';
export { eventToSentryRequest, sessionToSentryRequest } from './request';
export { initAndBind, ClientClass } from './sdk';
export { NoopTransport } from './transports/noop';
export { SDK_VERSION } from './version';

import * as Integrations from './integrations';

Expand Down
42 changes: 38 additions & 4 deletions packages/core/src/request.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,42 @@
import { Event, SentryRequest, Session } from '@sentry/types';
import { Event, SdkInfo, SentryRequest, Session } from '@sentry/types';

import { API } from './api';

/** Extract sdk info from from the API metadata */
function getSdkMetadataForEnvelopeHeader(api: API): SdkInfo | undefined {
if (!api.metadata || !api.metadata.sdk) {
return;
}
const { name, version } = api.metadata.sdk;
return { name, version };
}

/**
* Apply SdkInfo (name, version, packages, integrations) to the corresponding event key.
* Merge with existing data if any.
**/
function enhanceEventWithSdkInfo(event: Event, sdkInfo?: SdkInfo): Event {
if (!sdkInfo) {
return event;
}

event.sdk = event.sdk || {
name: sdkInfo.name,
version: sdkInfo.version,
};
event.sdk.name = event.sdk.name || sdkInfo.name;
event.sdk.version = event.sdk.version || sdkInfo.version;
event.sdk.integrations = [...(event.sdk.integrations || []), ...(sdkInfo.integrations || [])];
event.sdk.packages = [...(event.sdk.packages || []), ...(sdkInfo.packages || [])];
return event;
}

/** Creates a SentryRequest from an event. */
export function sessionToSentryRequest(session: Session, api: API): SentryRequest {
const sdkInfo = getSdkMetadataForEnvelopeHeader(api);
const envelopeHeaders = JSON.stringify({
sent_at: new Date().toISOString(),
...(sdkInfo && { sdk: sdkInfo }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only want name and version here not everything.

});
const itemHeaders = JSON.stringify({
type: 'session',
Expand All @@ -24,11 +55,13 @@ export function eventToSentryRequest(event: Event, api: API): SentryRequest {
const { __sentry_samplingMethod: samplingMethod, __sentry_sampleRate: sampleRate, ...otherTags } = event.tags || {};
event.tags = otherTags;

const useEnvelope = event.type === 'transaction';
const sdkInfo = getSdkMetadataForEnvelopeHeader(api);
const eventType = event.type || 'event';
const useEnvelope = eventType === 'transaction';

const req: SentryRequest = {
body: JSON.stringify(event),
type: event.type || 'event',
body: JSON.stringify(sdkInfo ? enhanceEventWithSdkInfo(event, api.metadata.sdk) : event),
type: eventType,
url: useEnvelope ? api.getEnvelopeEndpointWithUrlEncodedAuth() : api.getStoreEndpointWithUrlEncodedAuth(),
};

Expand All @@ -42,6 +75,7 @@ export function eventToSentryRequest(event: Event, api: API): SentryRequest {
const envelopeHeaders = JSON.stringify({
event_id: event.event_id,
sent_at: new Date().toISOString(),
...(sdkInfo && { sdk: sdkInfo }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here: We only want name and version here, not everything.

});
const itemHeaders = JSON.stringify({
type: event.type,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const SDK_VERSION = '5.30.0';
92 changes: 80 additions & 12 deletions packages/core/test/lib/request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,29 @@ import { API } from '../../src/api';
import { eventToSentryRequest } from '../../src/request';

describe('eventToSentryRequest', () => {
const api = new API('https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012');
const event: Event = {
contexts: { trace: { trace_id: '1231201211212012', span_id: '12261980', op: 'pageload' } },
environment: 'dogpark',
event_id: '0908201304152013',
release: 'off.leash.park',
spans: [],
transaction: '/dogs/are/great/',
type: 'transaction',
user: { id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12' },
};
let api: API;
let event: Event;

beforeEach(() => {
api = new API('https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', {
sdk: {
integrations: ['AWSLambda'],
name: 'sentry.javascript.browser',
version: `12.31.12`,
packages: [{ name: 'npm:@sentry/browser', version: `6.6.6` }],
},
});
event = {
contexts: { trace: { trace_id: '1231201211212012', span_id: '12261980', op: 'pageload' } },
environment: 'dogpark',
event_id: '0908201304152013',
release: 'off.leash.park',
spans: [],
transaction: '/dogs/are/great/',
type: 'transaction',
user: { id: '1121', username: 'CharlieDog', ip_address: '11.21.20.12' },
};
});

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

const result = eventToSentryRequest(event as Event, api);
const result = eventToSentryRequest(event, api);

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

Expand All @@ -53,4 +65,60 @@ describe('eventToSentryRequest', () => {
expect('dog' in envelope.event.tags).toBe(true);
});
});

it('adds sdk info to envelope header', () => {
const result = eventToSentryRequest(event, api);

const envelopeHeaderString = result.body.split('\n')[0];
const parsedHeader = JSON.parse(envelopeHeaderString);

expect(parsedHeader).toEqual(
expect.objectContaining({ sdk: { name: 'sentry.javascript.browser', version: '12.31.12' } }),
);
});

it('adds sdk info to event body', () => {
const result = eventToSentryRequest(event, api);

const eventString = result.body.split('\n')[2];
const parsedEvent = JSON.parse(eventString);

expect(parsedEvent).toEqual(
expect.objectContaining({
sdk: {
integrations: ['AWSLambda'],
name: 'sentry.javascript.browser',
version: `12.31.12`,
packages: [{ name: 'npm:@sentry/browser', version: `6.6.6` }],
},
}),
);
});

it('merges existing sdk info if one is present on the event body', () => {
event.sdk = {
integrations: ['Clojure'],
name: 'foo',
packages: [{ name: 'npm:@sentry/clj', version: `6.6.6` }],
version: '1337',
};
const result = eventToSentryRequest(event, api);

const eventString = result.body.split('\n')[2];
const parsedEvent = JSON.parse(eventString);

expect(parsedEvent).toEqual(
expect.objectContaining({
sdk: {
integrations: ['Clojure', 'AWSLambda'],
name: 'foo',
packages: [
{ name: 'npm:@sentry/clj', version: `6.6.6` },
{ name: 'npm:@sentry/browser', version: `6.6.6` },
],
version: '1337',
},
}),
);
});
});
Loading