Skip to content

Commit

Permalink
Merge branch 'develop' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
curiousercreative authored Dec 12, 2023
2 parents 16e142d + a05de17 commit ed50b0c
Show file tree
Hide file tree
Showing 20 changed files with 235 additions and 104 deletions.
4 changes: 2 additions & 2 deletions packages/browser/src/profiling/integration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EventProcessor, Hub, Integration, Transaction } from '@sentry/types';
import type { EventEnvelope, EventProcessor, Hub, Integration, Transaction } from '@sentry/types';
import type { Profile } from '@sentry/types/src/profiling';
import { logger } from '@sentry/utils';

Expand Down Expand Up @@ -110,7 +110,7 @@ export class BrowserProfilingIntegration implements Integration {
}
}

addProfilesToEnvelope(envelope, profilesToAddToEnvelope);
addProfilesToEnvelope(envelope as EventEnvelope, profilesToAddToEnvelope);
});
} else {
logger.warn('[Profiling] Client does not support hooks, profiling will be disabled');
Expand Down
5 changes: 2 additions & 3 deletions packages/browser/src/profiling/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable max-lines */

import { DEFAULT_ENVIRONMENT, getClient, getCurrentHub } from '@sentry/core';
import type { DebugImage, Envelope, Event, StackFrame, StackParser, Transaction } from '@sentry/types';
import type { DebugImage, Envelope, Event, EventEnvelope, StackFrame, StackParser, Transaction } from '@sentry/types';
import type { Profile, ThreadCpuProfile } from '@sentry/types/src/profiling';
import { GLOBAL_OBJ, browserPerformanceTimeOrigin, forEachEnvelopeItem, logger, uuid4 } from '@sentry/utils';

Expand Down Expand Up @@ -300,13 +300,12 @@ export function convertJSSelfProfileToSampledFormat(input: JSSelfProfile): Profi
* Adds items to envelope if they are not already present - mutates the envelope.
* @param envelope
*/
export function addProfilesToEnvelope(envelope: Envelope, profiles: Profile[]): Envelope {
export function addProfilesToEnvelope(envelope: EventEnvelope, profiles: Profile[]): Envelope {
if (!profiles.length) {
return envelope;
}

for (const profile of profiles) {
// @ts-expect-error untyped envelope
envelope[1].push([{ type: 'profile' }, profile]);
}
return envelope;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export class Hub implements HubInterface {
*/
public pushScope(): Scope {
// We want to clone the content of prev scope
const scope = Scope.clone(this.getScope());
const scope = this.getScope().clone();
this.getStack().push({
client: this.getClient(),
scope,
Expand Down Expand Up @@ -578,7 +578,7 @@ export function ensureHubOnCarrier(carrier: Carrier, parent: Hub = getGlobalHub(
// If there's no hub on current domain, or it's an old API, assign a new one
if (!hasHubOnCarrier(carrier) || getHubFromCarrier(carrier).isOlderThan(API_VERSION)) {
const globalHubTopStack = parent.getStackTop();
setHubOnCarrier(carrier, new Hub(globalHubTopStack.client, Scope.clone(globalHubTopStack.scope)));
setHubOnCarrier(carrier, new Hub(globalHubTopStack.client, globalHubTopStack.scope.clone()));
}
}

Expand Down
42 changes: 24 additions & 18 deletions packages/core/src/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,27 +110,33 @@ export class Scope implements ScopeInterface {

/**
* Inherit values from the parent scope.
* @param scope to clone.
* @deprecated Use `scope.clone()` and `new Scope()` instead.
*/
public static clone(scope?: Scope): Scope {
return scope ? scope.clone() : new Scope();
}

/**
* Clone this scope instance.
*/
public clone(): Scope {
const newScope = new Scope();
if (scope) {
newScope._breadcrumbs = [...scope._breadcrumbs];
newScope._tags = { ...scope._tags };
newScope._extra = { ...scope._extra };
newScope._contexts = { ...scope._contexts };
newScope._user = scope._user;
newScope._level = scope._level;
newScope._span = scope._span;
newScope._session = scope._session;
newScope._transactionName = scope._transactionName;
newScope._fingerprint = scope._fingerprint;
newScope._eventProcessors = [...scope._eventProcessors];
newScope._requestSession = scope._requestSession;
newScope._attachments = [...scope._attachments];
newScope._sdkProcessingMetadata = { ...scope._sdkProcessingMetadata };
newScope._propagationContext = { ...scope._propagationContext };
}
newScope._breadcrumbs = [...this._breadcrumbs];
newScope._tags = { ...this._tags };
newScope._extra = { ...this._extra };
newScope._contexts = { ...this._contexts };
newScope._user = this._user;
newScope._level = this._level;
newScope._span = this._span;
newScope._session = this._session;
newScope._transactionName = this._transactionName;
newScope._fingerprint = this._fingerprint;
newScope._eventProcessors = [...this._eventProcessors];
newScope._requestSession = this._requestSession;
newScope._attachments = [...this._attachments];
newScope._sdkProcessingMetadata = { ...this._sdkProcessingMetadata };
newScope._propagationContext = { ...this._propagationContext };

return newScope;
}

Expand Down
15 changes: 11 additions & 4 deletions packages/core/src/utils/prepareEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,7 @@ export function prepareEvent(

// If we have scope given to us, use it as the base for further modifications.
// This allows us to prevent unnecessary copying of data if `captureContext` is not provided.
let finalScope = scope;
if (hint.captureContext) {
finalScope = Scope.clone(finalScope).update(hint.captureContext);
}
const finalScope = getFinalScope(scope, hint.captureContext);

if (hint.mechanism) {
addExceptionMechanism(prepared, hint.mechanism);
Expand Down Expand Up @@ -349,6 +346,16 @@ function normalizeEvent(event: Event | null, depth: number, maxBreadth: number):
return normalized;
}

function getFinalScope(scope: Scope | undefined, captureContext: CaptureContext | undefined): Scope | undefined {
if (!captureContext) {
return scope;
}

const finalScope = scope ? scope.clone() : new Scope();
finalScope.update(captureContext);
return finalScope;
}

/**
* Parse either an `EventHint` directly, or convert a `CaptureContext` to an `EventHint`.
* This is used to allow to update method signatures that used to accept a `CaptureContext` but should now accept an `EventHint`.
Expand Down
12 changes: 2 additions & 10 deletions packages/opentelemetry-node/test/spanprocessor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,7 @@ import type { Span as OtelSpan } from '@opentelemetry/sdk-trace-base';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { SemanticAttributes, SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import type { SpanStatusType } from '@sentry/core';
import {
Hub,
Scope,
Span as SentrySpan,
Transaction,
addTracingExtensions,
createTransport,
makeMain,
} from '@sentry/core';
import { Hub, Span as SentrySpan, Transaction, addTracingExtensions, createTransport, makeMain } from '@sentry/core';
import { NodeClient } from '@sentry/node';
import { resolvedSyncPromise } from '@sentry/utils';

Expand Down Expand Up @@ -973,7 +965,7 @@ describe('SentrySpanProcessor', () => {
hub = new Hub(client);
makeMain(hub);

const newHub = new Hub(client, Scope.clone(hub.getScope()));
const newHub = new Hub(client, hub.getScope().clone());
newHub.configureScope(scope => {
scope.setTag('foo', 'bar');
});
Expand Down
3 changes: 2 additions & 1 deletion packages/opentelemetry/src/contextManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Context, ContextManager } from '@opentelemetry/api';
import type { Carrier, Hub } from '@sentry/core';
import { ensureHubOnCarrier } from '@sentry/core';

import { ensureHubOnCarrier, getCurrentHub, getHubFromCarrier } from './custom/hub';
import { getCurrentHub, getHubFromCarrier } from './custom/hub';
import { setHubOnContext } from './utils/contextData';

function createNewHub(parent: Hub | undefined): Hub {
Expand Down
12 changes: 9 additions & 3 deletions packages/opentelemetry/src/custom/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { trace } from '@opentelemetry/api';
import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
import type { BaseClient, Scope } from '@sentry/core';
import { SDK_VERSION } from '@sentry/core';
import type { Client, Event, EventHint } from '@sentry/types';
import type { CaptureContext, Client, Event, EventHint } from '@sentry/types';

import type { OpenTelemetryClient as OpenTelemetryClientInterface } from '../types';
import { OpenTelemetryScope } from './scope';
Expand Down Expand Up @@ -65,14 +65,14 @@ export function wrapClientClass<

/**
* Extends the base `_prepareEvent` so that we can properly handle `captureContext`.
* This uses `Scope.clone()`, which we need to replace with `NodeExperimentalScope.clone()` for this client.
* This uses `Scope.clone()`, which we need to replace with `OpenTelemetryScope.clone()` for this client.
*/
protected _prepareEvent(event: Event, hint: EventHint, scope?: Scope): PromiseLike<Event | null> {
let actualScope = scope;

// Remove `captureContext` hint and instead clone already here
if (hint && hint.captureContext) {
actualScope = OpenTelemetryScope.clone(scope);
actualScope = getFinalScope(scope, hint.captureContext);
delete hint.captureContext;
}

Expand All @@ -83,3 +83,9 @@ export function wrapClientClass<
return OpenTelemetryClient as unknown as WrappedClassConstructor;
}
/* eslint-enable @typescript-eslint/no-explicit-any */

function getFinalScope(scope: Scope | undefined, captureContext: CaptureContext): Scope | undefined {
const finalScope = scope ? scope.clone() : new OpenTelemetryScope();
finalScope.update(captureContext);
return finalScope;
}
18 changes: 1 addition & 17 deletions packages/opentelemetry/src/custom/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,6 @@ export class OpenTelemetryHub extends Hub {
public constructor(client?: Client, scope: Scope = new OpenTelemetryScope()) {
super(client, scope);
}

/**
* @inheritDoc
*/
public pushScope(): Scope {
// We want to clone the content of prev scope
const scope = OpenTelemetryScope.clone(this.getScope());
this.getStack().push({
client: this.getClient(),
scope,
});
return scope;
}
}

/** Custom getClient method that uses the custom hub. */
Expand Down Expand Up @@ -110,10 +97,7 @@ export function ensureHubOnCarrier(carrier: Carrier, parent: Hub = getGlobalHub(
// If there's no hub on current domain, or it's an old API, assign a new one
if (!hasHubOnCarrier(carrier) || getHubFromCarrier(carrier).isOlderThan(API_VERSION)) {
const globalHubTopStack = parent.getStackTop();
setHubOnCarrier(
carrier,
new OpenTelemetryHub(globalHubTopStack.client, OpenTelemetryScope.clone(globalHubTopStack.scope)),
);
setHubOnCarrier(carrier, new OpenTelemetryHub(globalHubTopStack.client, globalHubTopStack.scope.clone()));
}
}

Expand Down
40 changes: 23 additions & 17 deletions packages/opentelemetry/src/custom/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,30 @@ export class OpenTelemetryScope extends Scope {
* @inheritDoc
*/
public static clone(scope?: Scope): Scope {
return scope ? scope.clone() : new OpenTelemetryScope();
}

/**
* Clone this scope instance.
*/
public clone(): OpenTelemetryScope {
const newScope = new OpenTelemetryScope();
if (scope) {
newScope._breadcrumbs = [...scope['_breadcrumbs']];
newScope._tags = { ...scope['_tags'] };
newScope._extra = { ...scope['_extra'] };
newScope._contexts = { ...scope['_contexts'] };
newScope._user = scope['_user'];
newScope._level = scope['_level'];
newScope._span = scope['_span'];
newScope._session = scope['_session'];
newScope._transactionName = scope['_transactionName'];
newScope._fingerprint = scope['_fingerprint'];
newScope._eventProcessors = [...scope['_eventProcessors']];
newScope._requestSession = scope['_requestSession'];
newScope._attachments = [...scope['_attachments']];
newScope._sdkProcessingMetadata = { ...scope['_sdkProcessingMetadata'] };
newScope._propagationContext = { ...scope['_propagationContext'] };
}
newScope._breadcrumbs = [...this['_breadcrumbs']];
newScope._tags = { ...this['_tags'] };
newScope._extra = { ...this['_extra'] };
newScope._contexts = { ...this['_contexts'] };
newScope._user = this['_user'];
newScope._level = this['_level'];
newScope._span = this['_span'];
newScope._session = this['_session'];
newScope._transactionName = this['_transactionName'];
newScope._fingerprint = this['_fingerprint'];
newScope._eventProcessors = [...this['_eventProcessors']];
newScope._requestSession = this['_requestSession'];
newScope._attachments = [...this['_attachments']];
newScope._sdkProcessingMetadata = { ...this['_sdkProcessingMetadata'] };
newScope._propagationContext = { ...this['_propagationContext'] };

return newScope;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/opentelemetry/src/spanExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ function maybeSend(spans: ReadableSpan[]): ReadableSpan[] {

// Now finish the transaction, which will send it together with all the spans
// We make sure to use the current span as the activeSpan for this transaction
const scope = getSpanScope(span);
const forkedScope = OpenTelemetryScope.clone(scope as OpenTelemetryScope | undefined) as OpenTelemetryScope;
const scope = getSpanScope(span) as OpenTelemetryScope | undefined;
const forkedScope = scope ? scope.clone() : new OpenTelemetryScope();
forkedScope.activeSpan = span as unknown as Span;

transaction.finishWithScope(convertOtelTimeToSeconds(span.endTime), forkedScope);
Expand Down
2 changes: 2 additions & 0 deletions packages/opentelemetry/test/custom/scope.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe('NodeExperimentalScope', () => {
scope['_attachments'] = [{ data: '123', filename: 'test.txt' }];
scope['_sdkProcessingMetadata'] = { sdk: 'bar' };

// eslint-disable-next-line deprecation/deprecation
const scope2 = OpenTelemetryScope.clone(scope);

expect(scope2).toBeInstanceOf(OpenTelemetryScope);
Expand Down Expand Up @@ -68,6 +69,7 @@ describe('NodeExperimentalScope', () => {
});

it('clone() works without existing scope', () => {
// eslint-disable-next-line deprecation/deprecation
const scope = OpenTelemetryScope.clone(undefined);

expect(scope).toBeInstanceOf(OpenTelemetryScope);
Expand Down
4 changes: 2 additions & 2 deletions packages/replay/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
"devDependencies": {
"@babel/core": "^7.17.5",
"@sentry-internal/replay-worker": "7.86.0",
"@sentry-internal/rrweb": "2.2.0",
"@sentry-internal/rrweb-snapshot": "2.2.0",
"@sentry-internal/rrweb": "2.5.0",
"@sentry-internal/rrweb-snapshot": "2.5.0",
"fflate": "^0.8.1",
"jsdom-worker": "^0.2.1"
},
Expand Down
42 changes: 42 additions & 0 deletions packages/replay/src/coreHandlers/handleBeforeSendEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { ErrorEvent, Event } from '@sentry/types';

import type { ReplayContainer } from '../types';
import { createBreadcrumb } from '../util/createBreadcrumb';
import { isErrorEvent } from '../util/eventUtils';
import { addBreadcrumbEvent } from './util/addBreadcrumbEvent';

type BeforeSendEventCallback = (event: Event) => void;

/**
* Returns a listener to be added to `client.on('afterSendErrorEvent, listener)`.
*/
export function handleBeforeSendEvent(replay: ReplayContainer): BeforeSendEventCallback {
return (event: Event) => {
if (!replay.isEnabled() || !isErrorEvent(event)) {
return;
}

handleHydrationError(replay, event);
};
}

function handleHydrationError(replay: ReplayContainer, event: ErrorEvent): void {
const exceptionValue = event.exception && event.exception.values && event.exception.values[0].value;
if (typeof exceptionValue !== 'string') {
return;
}

if (
// Only matches errors in production builds of react-dom
// Example https://reactjs.org/docs/error-decoder.html?invariant=423
exceptionValue.match(/reactjs\.org\/docs\/error-decoder\.html\?invariant=(418|419|422|423|425)/) ||
// Development builds of react-dom
// Example Text: content did not match. Server: "A" Client: "B"
exceptionValue.match(/(hydration|content does not match|did not match)/i)
) {
const breadcrumb = createBreadcrumb({
category: 'replay.hydrate-error',
});
addBreadcrumbEvent(replay, breadcrumb);
}
}
2 changes: 1 addition & 1 deletion packages/replay/src/replay.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable max-lines */ // TODO: We might want to split this file up
import { EventType, record } from '@sentry-internal/rrweb';
import { captureException, getClient, getCurrentHub } from '@sentry/core';
import type { ReplayRecordingMode, Transaction } from '@sentry/types';
import type { Event as SentryEvent, ReplayRecordingMode, Transaction } from '@sentry/types';
import { logger } from '@sentry/utils';

import {
Expand Down
2 changes: 2 additions & 0 deletions packages/replay/src/util/addGlobalListeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Client, DynamicSamplingContext } from '@sentry/types';
import { addClickKeypressInstrumentationHandler, addHistoryInstrumentationHandler } from '@sentry/utils';

import { handleAfterSendEvent } from '../coreHandlers/handleAfterSendEvent';
import { handleBeforeSendEvent } from '../coreHandlers/handleBeforeSendEvent';
import { handleDomListener } from '../coreHandlers/handleDom';
import { handleGlobalEventListener } from '../coreHandlers/handleGlobalEvent';
import { handleHistorySpanListener } from '../coreHandlers/handleHistory';
Expand Down Expand Up @@ -35,6 +36,7 @@ export function addGlobalListeners(replay: ReplayContainer): void {

// If a custom client has no hooks yet, we continue to use the "old" implementation
if (hasHooks(client)) {
client.on('beforeSendEvent', handleBeforeSendEvent(replay));
client.on('afterSendEvent', handleAfterSendEvent(replay));
client.on('createDsc', (dsc: DynamicSamplingContext) => {
const replayId = replay.getSessionId();
Expand Down
Loading

0 comments on commit ed50b0c

Please sign in to comment.