Skip to content

Commit

Permalink
ref: Small integration refactors (#9928)
Browse files Browse the repository at this point in the history
Some small fixes to integrations & rewriting some "forgotten" ones to
functional syntax.
  • Loading branch information
mydea authored Dec 21, 2023
1 parent d152364 commit 52810b3
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 142 deletions.
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

0 comments on commit 52810b3

Please sign in to comment.