Skip to content

Commit

Permalink
feat(core): Add beforeSendTransaction (#6121)
Browse files Browse the repository at this point in the history
This adds a new `beforeSendTransaction` option to the SDK. As one would expect from the name, it works just like `beforeSend` (in other words, it's a guaranteed-to-be-last event processor), except that it acts upon transaction events, which the original `beforeSend` doesn't.

Docs PR will be linked in the PR once it's done.

Ref: getsentry/rfcs#19 (comment)
  • Loading branch information
lobsterkatie committed Nov 8, 2022
1 parent 59047ec commit f214732
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 14 deletions.
21 changes: 13 additions & 8 deletions packages/core/src/baseclient.ts
Expand Up @@ -613,13 +613,17 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
* @returns A SyncPromise that resolves with the event or rejects in case event was/will not be send.
*/
protected _processEvent(event: Event, hint: EventHint, scope?: Scope): PromiseLike<Event> {
const { beforeSend, sampleRate } = this.getOptions();
const options = this.getOptions();
const { sampleRate } = options;

if (!this._isEnabled()) {
return rejectedSyncPromise(new SentryError('SDK not enabled, will not capture event.', 'log'));
}

const isTransaction = event.type === 'transaction';
const beforeSendProcessorName = isTransaction ? 'beforeSendTransaction' : 'beforeSend';
const beforeSendProcessor = options[beforeSendProcessorName];

// 1.0 === 100% events are sent
// 0.0 === 0% events are sent
// Sampling for transaction happens somewhere else
Expand All @@ -641,17 +645,17 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
}

const isInternalException = hint.data && (hint.data as { __sentry__: boolean }).__sentry__ === true;
if (isInternalException || isTransaction || !beforeSend) {
if (isInternalException || !beforeSendProcessor) {
return prepared;
}

const beforeSendResult = beforeSend(prepared, hint);
return _validateBeforeSendResult(beforeSendResult);
const beforeSendResult = beforeSendProcessor(prepared, hint);
return _validateBeforeSendResult(beforeSendResult, beforeSendProcessorName);
})
.then(processedEvent => {
if (processedEvent === null) {
this.recordDroppedEvent('before_send', event.type || 'error');
throw new SentryError('`beforeSend` returned `null`, will not send event.', 'log');
throw new SentryError(`\`${beforeSendProcessorName}\` returned \`null\`, will not send event.`, 'log');
}

const session = scope && scope.getSession();
Expand Down Expand Up @@ -764,12 +768,13 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
}

/**
* Verifies that return value of configured `beforeSend` is of expected type, and returns the value if so.
* Verifies that return value of configured `beforeSend` or `beforeSendTransaction` is of expected type, and returns the value if so.
*/
function _validateBeforeSendResult(
beforeSendResult: PromiseLike<Event | null> | Event | null,
beforeSendProcessorName: 'beforeSend' | 'beforeSendTransaction',
): PromiseLike<Event | null> | Event | null {
const invalidValueError = '`beforeSend` must return `null` or a valid event.';
const invalidValueError = `\`${beforeSendProcessorName}\` must return \`null\` or a valid event.`;
if (isThenable(beforeSendResult)) {
return beforeSendResult.then(
event => {
Expand All @@ -779,7 +784,7 @@ function _validateBeforeSendResult(
return event;
},
e => {
throw new SentryError(`beforeSend rejected with ${e}`);
throw new SentryError(`\`${beforeSendProcessorName}\` rejected with ${e}`);
},
);
} else if (!isPlainObject(beforeSendResult) && beforeSendResult !== null) {
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/integration.ts
Expand Up @@ -65,10 +65,10 @@ export function getIntegrationsToSetup(options: Options): Integration[] {

const finalIntegrations = filterDuplicates(integrations);

// The `Debug` integration prints copies of the `event` and `hint` which will be passed to `beforeSend`. It therefore
// has to run after all other integrations, so that the changes of all event processors will be reflected in the
// printed values. For lack of a more elegant way to guarantee that, we therefore locate it and, assuming it exists,
// pop it out of its current spot and shove it onto the end of the array.
// The `Debug` integration prints copies of the `event` and `hint` which will be passed to `beforeSend` or
// `beforeSendTransaction`. It therefore has to run after all other integrations, so that the changes of all event
// processors will be reflected in the printed values. For lack of a more elegant way to guarantee that, we therefore
// locate it and, assuming it exists, pop it out of its current spot and shove it onto the end of the array.
const debugIndex = finalIntegrations.findIndex(integration => integration.name === 'Debug');
if (debugIndex !== -1) {
const [debugInstance] = finalIntegrations.splice(debugIndex, 1);
Expand Down

0 comments on commit f214732

Please sign in to comment.