Skip to content

Commit 52810b3

Browse files
authored
ref: Small integration refactors (#9928)
Some small fixes to integrations & rewriting some "forgotten" ones to functional syntax.
1 parent d152364 commit 52810b3

File tree

4 files changed

+121
-142
lines changed

4 files changed

+121
-142
lines changed
Lines changed: 79 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { getCurrentScope } from '@sentry/core';
2-
import type { Client, EventEnvelope, EventProcessor, Hub, Integration, Transaction } from '@sentry/types';
1+
import { convertIntegrationFnToClass, getCurrentScope } from '@sentry/core';
2+
import type { EventEnvelope, IntegrationFn, Transaction } from '@sentry/types';
33
import type { Profile } from '@sentry/types/src/profiling';
44
import { logger } from '@sentry/utils';
55

@@ -16,108 +16,97 @@ import {
1616
takeProfileFromGlobalCache,
1717
} from './utils';
1818

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

31-
public readonly name: string;
21+
const browserProfilingIntegration: IntegrationFn = () => {
22+
return {
23+
name: INTEGRATION_NAME,
24+
setup(client) {
25+
const scope = getCurrentScope();
3226

33-
/** @deprecated This is never set. */
34-
public getCurrentHub?: () => Hub;
27+
const transaction = scope.getTransaction();
3528

36-
public constructor() {
37-
this.name = BrowserProfilingIntegration.id;
38-
}
39-
40-
/**
41-
* @inheritDoc
42-
*/
43-
public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void, _getCurrentHub: () => Hub): void {
44-
// noop
45-
}
46-
47-
/** @inheritdoc */
48-
public setup(client: Client): void {
49-
const scope = getCurrentScope();
50-
51-
const transaction = scope.getTransaction();
52-
53-
if (transaction && isAutomatedPageLoadTransaction(transaction)) {
54-
if (shouldProfileTransaction(transaction)) {
55-
startProfileForTransaction(transaction);
56-
}
57-
}
58-
59-
if (typeof client.on !== 'function') {
60-
logger.warn('[Profiling] Client does not support hooks, profiling will be disabled');
61-
return;
62-
}
63-
64-
client.on('startTransaction', (transaction: Transaction) => {
65-
if (shouldProfileTransaction(transaction)) {
66-
startProfileForTransaction(transaction);
67-
}
68-
});
69-
70-
client.on('beforeEnvelope', (envelope): void => {
71-
// if not profiles are in queue, there is nothing to add to the envelope.
72-
if (!getActiveProfilesCount()) {
73-
return;
29+
if (transaction && isAutomatedPageLoadTransaction(transaction)) {
30+
if (shouldProfileTransaction(transaction)) {
31+
startProfileForTransaction(transaction);
32+
}
7433
}
7534

76-
const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);
77-
if (!profiledTransactionEvents.length) {
35+
if (typeof client.on !== 'function') {
36+
logger.warn('[Profiling] Client does not support hooks, profiling will be disabled');
7837
return;
7938
}
8039

81-
const profilesToAddToEnvelope: Profile[] = [];
82-
83-
for (const profiledTransaction of profiledTransactionEvents) {
84-
const context = profiledTransaction && profiledTransaction.contexts;
85-
const profile_id = context && context['profile'] && context['profile']['profile_id'];
86-
const start_timestamp = context && context['profile'] && context['profile']['start_timestamp'];
87-
88-
if (typeof profile_id !== 'string') {
89-
DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context');
90-
continue;
40+
client.on('startTransaction', (transaction: Transaction) => {
41+
if (shouldProfileTransaction(transaction)) {
42+
startProfileForTransaction(transaction);
9143
}
44+
});
9245

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

98-
// Remove the profile from the transaction context before sending, relay will take care of the rest.
99-
if (context && context['profile']) {
100-
delete context.profile;
52+
const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);
53+
if (!profiledTransactionEvents.length) {
54+
return;
10155
}
10256

103-
const profile = takeProfileFromGlobalCache(profile_id);
104-
if (!profile) {
105-
DEBUG_BUILD && logger.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`);
106-
continue;
57+
const profilesToAddToEnvelope: Profile[] = [];
58+
59+
for (const profiledTransaction of profiledTransactionEvents) {
60+
const context = profiledTransaction && profiledTransaction.contexts;
61+
const profile_id = context && context['profile'] && context['profile']['profile_id'];
62+
const start_timestamp = context && context['profile'] && context['profile']['start_timestamp'];
63+
64+
if (typeof profile_id !== 'string') {
65+
DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context');
66+
continue;
67+
}
68+
69+
if (!profile_id) {
70+
DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context');
71+
continue;
72+
}
73+
74+
// Remove the profile from the transaction context before sending, relay will take care of the rest.
75+
if (context && context['profile']) {
76+
delete context.profile;
77+
}
78+
79+
const profile = takeProfileFromGlobalCache(profile_id);
80+
if (!profile) {
81+
DEBUG_BUILD && logger.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`);
82+
continue;
83+
}
84+
85+
const profileEvent = createProfilingEvent(
86+
profile_id,
87+
start_timestamp as number | undefined,
88+
profile,
89+
profiledTransaction as ProfiledEvent,
90+
);
91+
if (profileEvent) {
92+
profilesToAddToEnvelope.push(profileEvent);
93+
}
10794
}
10895

109-
const profileEvent = createProfilingEvent(
110-
profile_id,
111-
start_timestamp as number | undefined,
112-
profile,
113-
profiledTransaction as ProfiledEvent,
114-
);
115-
if (profileEvent) {
116-
profilesToAddToEnvelope.push(profileEvent);
117-
}
118-
}
96+
addProfilesToEnvelope(envelope as EventEnvelope, profilesToAddToEnvelope);
97+
});
98+
},
99+
};
100+
};
119101

120-
addProfilesToEnvelope(envelope as EventEnvelope, profilesToAddToEnvelope);
121-
});
122-
}
123-
}
102+
/**
103+
* Browser profiling integration. Stores any event that has contexts["profile"]["profile_id"]
104+
* This exists because we do not want to await async profiler.stop calls as transaction.finish is called
105+
* in a synchronous context. Instead, we handle sending the profile async from the promise callback and
106+
* rely on being able to pull the event from the cache when we need to construct the envelope. This makes the
107+
* integration less reliable as we might be dropping profiles when the cache is full.
108+
*
109+
* @experimental
110+
*/
111+
// eslint-disable-next-line deprecation/deprecation
112+
export const BrowserProfilingIntegration = convertIntegrationFnToClass(INTEGRATION_NAME, browserProfilingIntegration);

packages/bun/src/integrations/bunserver.ts

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
1-
import { Transaction, captureException, continueTrace, runWithAsyncContext, startSpan } from '@sentry/core';
2-
import type { Integration } from '@sentry/types';
1+
import {
2+
Transaction,
3+
captureException,
4+
continueTrace,
5+
convertIntegrationFnToClass,
6+
runWithAsyncContext,
7+
startSpan,
8+
} from '@sentry/core';
9+
import type { IntegrationFn } from '@sentry/types';
310
import { getSanitizedUrlString, parseUrl } from '@sentry/utils';
411

12+
const INTEGRATION_NAME = 'BunServer';
13+
14+
const bunServerIntegration: IntegrationFn = () => {
15+
return {
16+
name: INTEGRATION_NAME,
17+
setupOnce() {
18+
instrumentBunServe();
19+
},
20+
};
21+
};
22+
523
/**
624
* Instruments `Bun.serve` to automatically create transactions and capture errors.
725
*/
8-
export class BunServer implements Integration {
9-
/**
10-
* @inheritDoc
11-
*/
12-
public static id: string = 'BunServer';
13-
14-
/**
15-
* @inheritDoc
16-
*/
17-
public name: string = BunServer.id;
18-
19-
/**
20-
* @inheritDoc
21-
*/
22-
public setupOnce(): void {
23-
instrumentBunServe();
24-
}
25-
}
26+
// eslint-disable-next-line deprecation/deprecation
27+
export const BunServer = convertIntegrationFnToClass(INTEGRATION_NAME, bunServerIntegration);
2628

2729
/**
2830
* Instruments Bun.serve by patching it's options.
Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,23 @@
1-
import type { ClientOptions, Integration } from '@sentry/types';
1+
import type { ClientOptions, IntegrationFn } from '@sentry/types';
22
import type { BaseClient } from '../baseclient';
3+
import { convertIntegrationFnToClass } from '../integration';
34
import { SimpleMetricsAggregator } from './simpleaggregator';
45

6+
const INTEGRATION_NAME = 'MetricsAggregator';
7+
8+
const metricsAggregatorIntegration: IntegrationFn = () => {
9+
return {
10+
name: INTEGRATION_NAME,
11+
setup(client: BaseClient<ClientOptions>) {
12+
client.metricsAggregator = new SimpleMetricsAggregator(client);
13+
},
14+
};
15+
};
16+
517
/**
618
* Enables Sentry metrics monitoring.
719
*
820
* @experimental This API is experimental and might having breaking changes in the future.
921
*/
10-
export class MetricsAggregator implements Integration {
11-
/**
12-
* @inheritDoc
13-
*/
14-
public static id: string = 'MetricsAggregator';
15-
16-
/**
17-
* @inheritDoc
18-
*/
19-
public name: string;
20-
21-
public constructor() {
22-
this.name = MetricsAggregator.id;
23-
}
24-
25-
/**
26-
* @inheritDoc
27-
*/
28-
public setupOnce(): void {
29-
// Do nothing
30-
}
31-
32-
/**
33-
* @inheritDoc
34-
*/
35-
public setup(client: BaseClient<ClientOptions>): void {
36-
client.metricsAggregator = new SimpleMetricsAggregator(client);
37-
}
38-
}
22+
// eslint-disable-next-line deprecation/deprecation
23+
export const MetricsAggregator = convertIntegrationFnToClass(INTEGRATION_NAME, metricsAggregatorIntegration);

packages/integration-shims/src/Feedback.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Integration } from '@sentry/types';
2+
import { consoleSandbox } from '@sentry/utils';
23

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

23-
// eslint-disable-next-line no-console
24-
console.error('You are using new Feedback() even though this bundle does not include Feedback.');
24+
consoleSandbox(() => {
25+
// eslint-disable-next-line no-console
26+
console.error('You are using new Feedback() even though this bundle does not include Feedback.');
27+
});
2528
}
2629

2730
/** jsdoc */

0 commit comments

Comments
 (0)