Skip to content

ref: Small integration refactors #9928

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
Dec 21, 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
169 changes: 79 additions & 90 deletions packages/browser/src/profiling/integration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getCurrentScope } from '@sentry/core';
import type { Client, EventEnvelope, EventProcessor, Hub, Integration, Transaction } from '@sentry/types';
import { convertIntegrationFnToClass, getCurrentScope } from '@sentry/core';
import type { EventEnvelope, IntegrationFn, Transaction } from '@sentry/types';
import type { Profile } from '@sentry/types/src/profiling';
import { logger } from '@sentry/utils';

Expand All @@ -16,108 +16,97 @@ import {
takeProfileFromGlobalCache,
} from './utils';

/**
* Browser profiling integration. Stores any event that has contexts["profile"]["profile_id"]
* This exists because we do not want to await async profiler.stop calls as transaction.finish is called
* in a synchronous context. Instead, we handle sending the profile async from the promise callback and
* rely on being able to pull the event from the cache when we need to construct the envelope. This makes the
* integration less reliable as we might be dropping profiles when the cache is full.
*
* @experimental
*/
export class BrowserProfilingIntegration implements Integration {
public static id: string = 'BrowserProfilingIntegration';
const INTEGRATION_NAME = 'BrowserProfiling';

public readonly name: string;
const browserProfilingIntegration: IntegrationFn = () => {
return {
name: INTEGRATION_NAME,
setup(client) {
const scope = getCurrentScope();

/** @deprecated This is never set. */
public getCurrentHub?: () => Hub;
const transaction = scope.getTransaction();

public constructor() {
this.name = BrowserProfilingIntegration.id;
}

/**
* @inheritDoc
*/
public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void, _getCurrentHub: () => Hub): void {
// noop
}

/** @inheritdoc */
public setup(client: Client): void {
const scope = getCurrentScope();

const transaction = scope.getTransaction();

if (transaction && isAutomatedPageLoadTransaction(transaction)) {
if (shouldProfileTransaction(transaction)) {
startProfileForTransaction(transaction);
}
}

if (typeof client.on !== 'function') {
logger.warn('[Profiling] Client does not support hooks, profiling will be disabled');
return;
}

client.on('startTransaction', (transaction: Transaction) => {
if (shouldProfileTransaction(transaction)) {
startProfileForTransaction(transaction);
}
});

client.on('beforeEnvelope', (envelope): void => {
// if not profiles are in queue, there is nothing to add to the envelope.
if (!getActiveProfilesCount()) {
return;
if (transaction && isAutomatedPageLoadTransaction(transaction)) {
if (shouldProfileTransaction(transaction)) {
startProfileForTransaction(transaction);
}
}

const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);
if (!profiledTransactionEvents.length) {
if (typeof client.on !== 'function') {
logger.warn('[Profiling] Client does not support hooks, profiling will be disabled');
return;
}

const profilesToAddToEnvelope: Profile[] = [];

for (const profiledTransaction of profiledTransactionEvents) {
const context = profiledTransaction && profiledTransaction.contexts;
const profile_id = context && context['profile'] && context['profile']['profile_id'];
const start_timestamp = context && context['profile'] && context['profile']['start_timestamp'];

if (typeof profile_id !== 'string') {
DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context');
continue;
client.on('startTransaction', (transaction: Transaction) => {
if (shouldProfileTransaction(transaction)) {
startProfileForTransaction(transaction);
}
});

if (!profile_id) {
DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context');
continue;
client.on('beforeEnvelope', (envelope): void => {
// if not profiles are in queue, there is nothing to add to the envelope.
if (!getActiveProfilesCount()) {
return;
}

// Remove the profile from the transaction context before sending, relay will take care of the rest.
if (context && context['profile']) {
delete context.profile;
const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);
if (!profiledTransactionEvents.length) {
return;
}

const profile = takeProfileFromGlobalCache(profile_id);
if (!profile) {
DEBUG_BUILD && logger.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`);
continue;
const profilesToAddToEnvelope: Profile[] = [];

for (const profiledTransaction of profiledTransactionEvents) {
const context = profiledTransaction && profiledTransaction.contexts;
const profile_id = context && context['profile'] && context['profile']['profile_id'];
const start_timestamp = context && context['profile'] && context['profile']['start_timestamp'];

if (typeof profile_id !== 'string') {
DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context');
continue;
}

if (!profile_id) {
DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context');
continue;
}

// Remove the profile from the transaction context before sending, relay will take care of the rest.
if (context && context['profile']) {
delete context.profile;
}

const profile = takeProfileFromGlobalCache(profile_id);
if (!profile) {
DEBUG_BUILD && logger.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`);
continue;
}

const profileEvent = createProfilingEvent(
profile_id,
start_timestamp as number | undefined,
profile,
profiledTransaction as ProfiledEvent,
);
if (profileEvent) {
profilesToAddToEnvelope.push(profileEvent);
}
}

const profileEvent = createProfilingEvent(
profile_id,
start_timestamp as number | undefined,
profile,
profiledTransaction as ProfiledEvent,
);
if (profileEvent) {
profilesToAddToEnvelope.push(profileEvent);
}
}
addProfilesToEnvelope(envelope as EventEnvelope, profilesToAddToEnvelope);
});
},
};
};

addProfilesToEnvelope(envelope as EventEnvelope, profilesToAddToEnvelope);
});
}
}
/**
* Browser profiling integration. Stores any event that has contexts["profile"]["profile_id"]
* This exists because we do not want to await async profiler.stop calls as transaction.finish is called
* in a synchronous context. Instead, we handle sending the profile async from the promise callback and
* rely on being able to pull the event from the cache when we need to construct the envelope. This makes the
* integration less reliable as we might be dropping profiles when the cache is full.
*
* @experimental
*/
// eslint-disable-next-line deprecation/deprecation
export const BrowserProfilingIntegration = convertIntegrationFnToClass(INTEGRATION_NAME, browserProfilingIntegration);
42 changes: 22 additions & 20 deletions packages/bun/src/integrations/bunserver.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import { Transaction, captureException, continueTrace, runWithAsyncContext, startSpan } from '@sentry/core';
import type { Integration } from '@sentry/types';
import {
Transaction,
captureException,
continueTrace,
convertIntegrationFnToClass,
runWithAsyncContext,
startSpan,
} from '@sentry/core';
import type { IntegrationFn } from '@sentry/types';
import { getSanitizedUrlString, parseUrl } from '@sentry/utils';

const INTEGRATION_NAME = 'BunServer';

const bunServerIntegration: IntegrationFn = () => {
return {
name: INTEGRATION_NAME,
setupOnce() {
instrumentBunServe();
},
};
};

/**
* Instruments `Bun.serve` to automatically create transactions and capture errors.
*/
export class BunServer implements Integration {
/**
* @inheritDoc
*/
public static id: string = 'BunServer';

/**
* @inheritDoc
*/
public name: string = BunServer.id;

/**
* @inheritDoc
*/
public setupOnce(): void {
instrumentBunServe();
}
}
// eslint-disable-next-line deprecation/deprecation
export const BunServer = convertIntegrationFnToClass(INTEGRATION_NAME, bunServerIntegration);

/**
* Instruments Bun.serve by patching it's options.
Expand Down
45 changes: 15 additions & 30 deletions packages/core/src/metrics/integration.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,23 @@
import type { ClientOptions, Integration } from '@sentry/types';
import type { ClientOptions, IntegrationFn } from '@sentry/types';
import type { BaseClient } from '../baseclient';
import { convertIntegrationFnToClass } from '../integration';
import { SimpleMetricsAggregator } from './simpleaggregator';

const INTEGRATION_NAME = 'MetricsAggregator';

const metricsAggregatorIntegration: IntegrationFn = () => {
return {
name: INTEGRATION_NAME,
setup(client: BaseClient<ClientOptions>) {
client.metricsAggregator = new SimpleMetricsAggregator(client);
},
};
};

/**
* Enables Sentry metrics monitoring.
*
* @experimental This API is experimental and might having breaking changes in the future.
*/
export class MetricsAggregator implements Integration {
/**
* @inheritDoc
*/
public static id: string = 'MetricsAggregator';

/**
* @inheritDoc
*/
public name: string;

public constructor() {
this.name = MetricsAggregator.id;
}

/**
* @inheritDoc
*/
public setupOnce(): void {
// Do nothing
}

/**
* @inheritDoc
*/
public setup(client: BaseClient<ClientOptions>): void {
client.metricsAggregator = new SimpleMetricsAggregator(client);
}
}
// eslint-disable-next-line deprecation/deprecation
export const MetricsAggregator = convertIntegrationFnToClass(INTEGRATION_NAME, metricsAggregatorIntegration);
7 changes: 5 additions & 2 deletions packages/integration-shims/src/Feedback.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Integration } from '@sentry/types';
import { consoleSandbox } from '@sentry/utils';

/**
* This is a shim for the Feedback integration.
Expand All @@ -20,8 +21,10 @@ class FeedbackShim implements Integration {
public constructor(_options: any) {
this.name = FeedbackShim.id;

// eslint-disable-next-line no-console
console.error('You are using new Feedback() even though this bundle does not include Feedback.');
consoleSandbox(() => {
// eslint-disable-next-line no-console
console.error('You are using new Feedback() even though this bundle does not include Feedback.');
});
}

/** jsdoc */
Expand Down