Skip to content

ref(integrations): Refactor pluggable integrations to use processEvent #9021

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 1 commit into from
Oct 3, 2023
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
23 changes: 13 additions & 10 deletions packages/integrations/src/contextlines.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Event, EventProcessor, Hub, Integration, StackFrame } from '@sentry/types';
import type { Event, Integration, StackFrame } from '@sentry/types';
import { addContextToFrame, GLOBAL_OBJ, stripUrlQueryAndFragment } from '@sentry/utils';

const WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;
Expand Down Expand Up @@ -44,17 +44,20 @@ export class ContextLines implements Integration {
/**
* @inheritDoc
*/
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
addGlobalEventProcessor(event => {
const self = getCurrentHub().getIntegration(ContextLines);
if (!self) {
return event;
}
return this.addSourceContext(event);
});
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
// noop
}

/** @inheritDoc */
public processEvent(event: Event): Event {
return this.addSourceContext(event);
}

/** Processes an event and adds context lines */
/**
* Processes an event and adds context lines.
*
* TODO (v8): Make this internal/private
*/
public addSourceContext(event: Event): Event {
const doc = WINDOW.document;
const htmlFilename = WINDOW.location && stripUrlQueryAndFragment(WINDOW.location.href);
Expand Down
46 changes: 21 additions & 25 deletions packages/integrations/src/dedupe.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Event, EventProcessor, Exception, Hub, Integration, StackFrame } from '@sentry/types';
import type { Event, Exception, Integration, StackFrame } from '@sentry/types';
import { logger } from '@sentry/utils';

/** Deduplication filter */
Expand All @@ -22,36 +22,32 @@ export class Dedupe implements Integration {
this.name = Dedupe.id;
}

/** @inheritDoc */
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
// noop
}

/**
* @inheritDoc
*/
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
const eventProcessor: EventProcessor = currentEvent => {
// We want to ignore any non-error type events, e.g. transactions or replays
// These should never be deduped, and also not be compared against as _previousEvent.
if (currentEvent.type) {
return currentEvent;
}
public processEvent(currentEvent: Event): Event | null {
// We want to ignore any non-error type events, e.g. transactions or replays
// These should never be deduped, and also not be compared against as _previousEvent.
if (currentEvent.type) {
return currentEvent;
}

const self = getCurrentHub().getIntegration(Dedupe);
if (self) {
// Juuust in case something goes wrong
try {
if (_shouldDropEvent(currentEvent, self._previousEvent)) {
__DEBUG_BUILD__ && logger.warn('Event dropped due to being a duplicate of previously captured event.');
return null;
}
} catch (_oO) {
return (self._previousEvent = currentEvent);
}

return (self._previousEvent = currentEvent);
// Juuust in case something goes wrong
try {
if (_shouldDropEvent(currentEvent, this._previousEvent)) {
__DEBUG_BUILD__ && logger.warn('Event dropped due to being a duplicate of previously captured event.');
return null;
}
return currentEvent;
};
} catch (_oO) {
return (this._previousEvent = currentEvent);
}

eventProcessor.id = this.name;
addGlobalEventProcessor(eventProcessor);
return (this._previousEvent = currentEvent);
}
}

Expand Down
149 changes: 77 additions & 72 deletions packages/integrations/src/extraerrordata.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Contexts, Event, EventHint, EventProcessor, ExtendedError, Hub, Integration } from '@sentry/types';
import type { Contexts, Event, EventHint, ExtendedError, Integration } from '@sentry/types';
import { addNonEnumerableProperty, isError, isPlainObject, logger, normalize } from '@sentry/utils';

/** JSDoc */
interface ExtraErrorDataOptions {
depth?: number;
depth: number;
}

/** Patch toString calls to return proper name for wrapped functions */
Expand All @@ -24,7 +24,7 @@ export class ExtraErrorData implements Integration {
/**
* @inheritDoc
*/
public constructor(options?: ExtraErrorDataOptions) {
public constructor(options?: Partial<ExtraErrorDataOptions>) {
this.name = ExtraErrorData.id;

this._options = {
Expand All @@ -36,94 +36,99 @@ export class ExtraErrorData implements Integration {
/**
* @inheritDoc
*/
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
addGlobalEventProcessor((event: Event, hint: EventHint) => {
const self = getCurrentHub().getIntegration(ExtraErrorData);
if (!self) {
return event;
}
return self.enhanceEventWithErrorData(event, hint);
});
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
// noop
}

/** @inheritDoc */
public processEvent(event: Event, hint: EventHint): Event {
return this.enhanceEventWithErrorData(event, hint);
}

/**
* Attaches extracted information from the Error object to extra field in the Event
* Attaches extracted information from the Error object to extra field in the Event.
*
* TODO (v8): Drop this public function.
*/
public enhanceEventWithErrorData(event: Event, hint: EventHint = {}): Event {
if (!hint.originalException || !isError(hint.originalException)) {
return event;
}
const exceptionName = (hint.originalException as ExtendedError).name || hint.originalException.constructor.name;
return _enhanceEventWithErrorData(event, hint, this._options.depth);
}
}

const errorData = this._extractErrorData(hint.originalException as ExtendedError);
function _enhanceEventWithErrorData(event: Event, hint: EventHint = {}, depth: number): Event {
if (!hint.originalException || !isError(hint.originalException)) {
return event;
}
const exceptionName = (hint.originalException as ExtendedError).name || hint.originalException.constructor.name;

if (errorData) {
const contexts: Contexts = {
...event.contexts,
};
const errorData = _extractErrorData(hint.originalException as ExtendedError);

const normalizedErrorData = normalize(errorData, this._options.depth);
if (errorData) {
const contexts: Contexts = {
...event.contexts,
};

if (isPlainObject(normalizedErrorData)) {
// We mark the error data as "already normalized" here, because we don't want other normalization procedures to
// potentially truncate the data we just already normalized, with a certain depth setting.
addNonEnumerableProperty(normalizedErrorData, '__sentry_skip_normalization__', true);
contexts[exceptionName] = normalizedErrorData;
}
const normalizedErrorData = normalize(errorData, depth);

return {
...event,
contexts,
};
if (isPlainObject(normalizedErrorData)) {
// We mark the error data as "already normalized" here, because we don't want other normalization procedures to
// potentially truncate the data we just already normalized, with a certain depth setting.
addNonEnumerableProperty(normalizedErrorData, '__sentry_skip_normalization__', true);
contexts[exceptionName] = normalizedErrorData;
}

return event;
return {
...event,
contexts,
};
}

/**
* Extract extra information from the Error object
*/
private _extractErrorData(error: ExtendedError): Record<string, unknown> | null {
// We are trying to enhance already existing event, so no harm done if it won't succeed
try {
const nativeKeys = [
'name',
'message',
'stack',
'line',
'column',
'fileName',
'lineNumber',
'columnNumber',
'toJSON',
];

const extraErrorInfo: Record<string, unknown> = {};

// We want only enumerable properties, thus `getOwnPropertyNames` is redundant here, as we filter keys anyway.
for (const key of Object.keys(error)) {
if (nativeKeys.indexOf(key) !== -1) {
continue;
}
const value = error[key];
extraErrorInfo[key] = isError(value) ? value.toString() : value;
return event;
}

/**
* Extract extra information from the Error object
*/
function _extractErrorData(error: ExtendedError): Record<string, unknown> | null {
// We are trying to enhance already existing event, so no harm done if it won't succeed
try {
const nativeKeys = [
'name',
'message',
'stack',
'line',
'column',
'fileName',
'lineNumber',
'columnNumber',
'toJSON',
];

const extraErrorInfo: Record<string, unknown> = {};

// We want only enumerable properties, thus `getOwnPropertyNames` is redundant here, as we filter keys anyway.
for (const key of Object.keys(error)) {
if (nativeKeys.indexOf(key) !== -1) {
continue;
}
const value = error[key];
extraErrorInfo[key] = isError(value) ? value.toString() : value;
}

// Check if someone attached `toJSON` method to grab even more properties (eg. axios is doing that)
if (typeof error.toJSON === 'function') {
const serializedError = error.toJSON() as Record<string, unknown>;
// Check if someone attached `toJSON` method to grab even more properties (eg. axios is doing that)
if (typeof error.toJSON === 'function') {
const serializedError = error.toJSON() as Record<string, unknown>;

for (const key of Object.keys(serializedError)) {
const value = serializedError[key];
extraErrorInfo[key] = isError(value) ? value.toString() : value;
}
for (const key of Object.keys(serializedError)) {
const value = serializedError[key];
extraErrorInfo[key] = isError(value) ? value.toString() : value;
}

return extraErrorInfo;
} catch (oO) {
__DEBUG_BUILD__ && logger.error('Unable to extract extra data from the Error object:', oO);
}

return null;
return extraErrorInfo;
} catch (oO) {
__DEBUG_BUILD__ && logger.error('Unable to extract extra data from the Error object:', oO);
}

return null;
}
21 changes: 11 additions & 10 deletions packages/integrations/src/rewriteframes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Event, EventProcessor, Hub, Integration, StackFrame, Stacktrace } from '@sentry/types';
import type { Event, Integration, StackFrame, Stacktrace } from '@sentry/types';
import { basename, relative } from '@sentry/utils';

type StackFrameIteratee = (frame: StackFrame) => StackFrame;
Expand Down Expand Up @@ -43,17 +43,18 @@ export class RewriteFrames implements Integration {
/**
* @inheritDoc
*/
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
addGlobalEventProcessor(event => {
const self = getCurrentHub().getIntegration(RewriteFrames);
if (self) {
return self.process(event);
}
return event;
});
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
// noop
}

/** JSDoc */
/** @inheritDoc */
public processEvent(event: Event): Event {
return this.process(event);
}

/**
* TODO (v8): Make this private/internal
*/
public process(originalEvent: Event): Event {
let processedEvent = originalEvent;

Expand Down
19 changes: 9 additions & 10 deletions packages/integrations/src/sessiontiming.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Event, EventProcessor, Hub, Integration } from '@sentry/types';
import type { Event, Integration } from '@sentry/types';

/** This function adds duration since Sentry was initialized till the time event was sent */
export class SessionTiming implements Integration {
Expand All @@ -23,18 +23,17 @@ export class SessionTiming implements Integration {
/**
* @inheritDoc
*/
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
addGlobalEventProcessor(event => {
const self = getCurrentHub().getIntegration(SessionTiming);
if (self) {
return self.process(event);
}
return event;
});
public setupOnce(_addGlobaleventProcessor: unknown, _getCurrentHub: unknown): void {
// noop
}

/** @inheritDoc */
public processEvent(event: Event): Event {
return this.process(event);
}

/**
* @inheritDoc
* TODO (v8): make this private/internal
*/
public process(event: Event): Event {
const now = Date.now();
Expand Down
Loading