Skip to content

Commit 97b077a

Browse files
authored
feat(opentelemetry): Add addOpenTelemetryInstrumentation (#11667)
Adds `addOpenTelemetryInstrumentation`, a helper that can be used to dynamically register OpenTelemetry instrumentation. This helps unblock work on #11548 The helper itself is quite small: ```ts export function addOpenTelemetryInstrumentation( instrumentation: InstrumentationOption | InstrumentationOption[], ): void { registerInstrumentations({ instrumentations: Array.isArray(instrumentation) ? instrumentation : [instrumentation], }); } ``` and is designed to accept either a standalone instrumentation or an array of instrumentations. This gives users a ton of flexibility into usage!
1 parent bc43dbf commit 97b077a

File tree

25 files changed

+152
-158
lines changed

25 files changed

+152
-158
lines changed

packages/astro/src/index.server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export {
8787
hapiIntegration,
8888
setupHapiErrorHandler,
8989
spotlightIntegration,
90+
addOpenTelemetryInstrumentation,
9091
} from '@sentry/node';
9192

9293
// We can still leave this for the carrier init and type exports

packages/aws-serverless/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export {
9696
spanToJSON,
9797
spanToTraceHeader,
9898
trpcMiddleware,
99+
addOpenTelemetryInstrumentation,
99100
} from '@sentry/node';
100101

101102
export {

packages/bun/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export {
117117
spanToJSON,
118118
spanToTraceHeader,
119119
trpcMiddleware,
120+
addOpenTelemetryInstrumentation,
120121
} from '@sentry/node';
121122

122123
export {

packages/google-cloud-serverless/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export {
9696
spanToJSON,
9797
spanToTraceHeader,
9898
trpcMiddleware,
99+
addOpenTelemetryInstrumentation,
99100
} from '@sentry/node';
100101

101102
export {

packages/node/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export type { NodeOptions } from './types';
3939
export { addRequestDataToEvent, DEFAULT_USER_INCLUDES, extractRequestData } from '@sentry/utils';
4040

4141
export {
42+
addOpenTelemetryInstrumentation,
4243
// These are custom variants that need to be used instead of the core one
4344
// As they have slightly different implementations
4445
continueTrace,

packages/node/src/integrations/http.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { ClientRequest, IncomingMessage, ServerResponse } from 'http';
22
import type { Span } from '@opentelemetry/api';
33
import { SpanKind } from '@opentelemetry/api';
4-
import { registerInstrumentations } from '@opentelemetry/instrumentation';
54
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
5+
import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry';
66

77
import {
88
addBreadcrumb,
@@ -52,7 +52,7 @@ const _httpIntegration = ((options: HttpOptions = {}) => {
5252
return {
5353
name: 'Http',
5454
setupOnce() {
55-
const instrumentations = [
55+
addOpenTelemetryInstrumentation(
5656
new HttpInstrumentation({
5757
ignoreOutgoingRequestHook: request => {
5858
const url = getRequestUrl(request);
@@ -141,11 +141,7 @@ const _httpIntegration = ((options: HttpOptions = {}) => {
141141
}
142142
},
143143
}),
144-
];
145-
146-
registerInstrumentations({
147-
instrumentations,
148-
});
144+
);
149145
},
150146
};
151147
}) satisfies IntegrationFn;

packages/node/src/integrations/node-fetch.ts

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import type { Span } from '@opentelemetry/api';
22
import { SpanKind } from '@opentelemetry/api';
3-
import type { Instrumentation } from '@opentelemetry/instrumentation';
4-
import { registerInstrumentations } from '@opentelemetry/instrumentation';
53
import { addBreadcrumb, defineIntegration } from '@sentry/core';
4+
import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry';
65
import { getRequestSpanData, getSpanKind } from '@sentry/opentelemetry';
76
import type { IntegrationFn } from '@sentry/types';
87
import { logger } from '@sentry/utils';
98
import { DEBUG_BUILD } from '../debug-build';
109
import { NODE_MAJOR } from '../nodeVersion';
1110

11+
import type { FetchInstrumentation } from 'opentelemetry-instrumentation-fetch-node';
12+
1213
import { addOriginToSpan } from '../utils/addOriginToSpan';
1314

1415
interface NodeFetchOptions {
@@ -29,7 +30,7 @@ const _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {
2930
const _breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs;
3031
const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;
3132

32-
async function getInstrumentation(): Promise<[Instrumentation] | void> {
33+
async function getInstrumentation(): Promise<FetchInstrumentation | void> {
3334
// Only add NodeFetch if Node >= 18, as previous versions do not support it
3435
if (NODE_MAJOR < 18) {
3536
DEBUG_BUILD && logger.log('NodeFetch is not supported on Node < 18, skipping instrumentation...');
@@ -38,22 +39,20 @@ const _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {
3839

3940
try {
4041
const pkg = await import('opentelemetry-instrumentation-fetch-node');
41-
return [
42-
new pkg.FetchInstrumentation({
43-
ignoreRequestHook: (request: { origin?: string }) => {
44-
const url = request.origin;
45-
return _ignoreOutgoingRequests && url && _ignoreOutgoingRequests(url);
46-
},
47-
onRequest: ({ span }: { span: Span }) => {
48-
_updateSpan(span);
42+
return new pkg.FetchInstrumentation({
43+
ignoreRequestHook: (request: { origin?: string }) => {
44+
const url = request.origin;
45+
return _ignoreOutgoingRequests && url && _ignoreOutgoingRequests(url);
46+
},
47+
onRequest: ({ span }: { span: Span }) => {
48+
_updateSpan(span);
4949

50-
if (_breadcrumbs) {
51-
_addRequestBreadcrumb(span);
52-
}
53-
},
54-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
55-
} as any),
56-
];
50+
if (_breadcrumbs) {
51+
_addRequestBreadcrumb(span);
52+
}
53+
},
54+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
55+
} as any);
5756
} catch (error) {
5857
// Could not load instrumentation
5958
DEBUG_BUILD && logger.log('Could not load NodeFetch instrumentation.');
@@ -64,11 +63,9 @@ const _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {
6463
name: 'NodeFetch',
6564
setupOnce() {
6665
// eslint-disable-next-line @typescript-eslint/no-floating-promises
67-
getInstrumentation().then(instrumentations => {
68-
if (instrumentations) {
69-
registerInstrumentations({
70-
instrumentations,
71-
});
66+
getInstrumentation().then(instrumentation => {
67+
if (instrumentation) {
68+
addOpenTelemetryInstrumentation(instrumentation);
7269
}
7370
});
7471
},

packages/node/src/integrations/tracing/connect.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
import { registerInstrumentations } from '@opentelemetry/instrumentation';
21
import { ConnectInstrumentation } from '@opentelemetry/instrumentation-connect';
32
import { captureException, defineIntegration } from '@sentry/core';
3+
import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry';
44
import type { IntegrationFn } from '@sentry/types';
55

66
type ConnectApp = {
7+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
78
use: (middleware: any) => void;
89
};
910

1011
const _connectIntegration = (() => {
1112
return {
1213
name: 'Connect',
1314
setupOnce() {
14-
registerInstrumentations({
15-
instrumentations: [new ConnectInstrumentation({})],
16-
});
15+
addOpenTelemetryInstrumentation(new ConnectInstrumentation({}));
1716
},
1817
};
1918
}) satisfies IntegrationFn;
2019

2120
export const connectIntegration = defineIntegration(_connectIntegration);
2221

22+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2323
function connectErrorMiddleware(err: any, req: any, res: any, next: any): void {
2424
captureException(err);
2525
next(err);

packages/node/src/integrations/tracing/express.ts

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type * as http from 'http';
2-
import { registerInstrumentations } from '@opentelemetry/instrumentation';
32
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
43
import { defineIntegration, getDefaultIsolationScope } from '@sentry/core';
54
import { captureException, getClient, getIsolationScope } from '@sentry/core';
5+
import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry';
66
import type { IntegrationFn } from '@sentry/types';
77

88
import { logger } from '@sentry/utils';
@@ -14,29 +14,27 @@ const _expressIntegration = (() => {
1414
return {
1515
name: 'Express',
1616
setupOnce() {
17-
registerInstrumentations({
18-
instrumentations: [
19-
new ExpressInstrumentation({
20-
requestHook(span) {
21-
addOriginToSpan(span, 'auto.http.otel.express');
22-
},
23-
spanNameHook(info, defaultName) {
24-
if (getIsolationScope() === getDefaultIsolationScope()) {
25-
DEBUG_BUILD &&
26-
logger.warn('Isolation scope is still default isolation scope - skipping setting transactionName');
27-
return defaultName;
28-
}
29-
if (info.layerType === 'request_handler') {
30-
// type cast b/c Otel unfortunately types info.request as any :(
31-
const req = info.request as { method?: string };
32-
const method = req.method ? req.method.toUpperCase() : 'GET';
33-
getIsolationScope().setTransactionName(`${method} ${info.route}`);
34-
}
17+
addOpenTelemetryInstrumentation(
18+
new ExpressInstrumentation({
19+
requestHook(span) {
20+
addOriginToSpan(span, 'auto.http.otel.express');
21+
},
22+
spanNameHook(info, defaultName) {
23+
if (getIsolationScope() === getDefaultIsolationScope()) {
24+
DEBUG_BUILD &&
25+
logger.warn('Isolation scope is still default isolation scope - skipping setting transactionName');
3526
return defaultName;
36-
},
37-
}),
38-
],
39-
});
27+
}
28+
if (info.layerType === 'request_handler') {
29+
// type cast b/c Otel unfortunately types info.request as any :(
30+
const req = info.request as { method?: string };
31+
const method = req.method ? req.method.toUpperCase() : 'GET';
32+
getIsolationScope().setTransactionName(`${method} ${info.route}`);
33+
}
34+
return defaultName;
35+
},
36+
}),
37+
);
4038
},
4139
};
4240
}) satisfies IntegrationFn;

packages/node/src/integrations/tracing/fastify.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { registerInstrumentations } from '@opentelemetry/instrumentation';
21
import { FastifyInstrumentation } from '@opentelemetry/instrumentation-fastify';
32
import { captureException, defineIntegration, getIsolationScope } from '@sentry/core';
3+
import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry';
44
import type { IntegrationFn } from '@sentry/types';
55

66
import { addOriginToSpan } from '../../utils/addOriginToSpan';
@@ -9,15 +9,13 @@ const _fastifyIntegration = (() => {
99
return {
1010
name: 'Fastify',
1111
setupOnce() {
12-
registerInstrumentations({
13-
instrumentations: [
14-
new FastifyInstrumentation({
15-
requestHook(span) {
16-
addOriginToSpan(span, 'auto.http.otel.fastify');
17-
},
18-
}),
19-
],
20-
});
12+
addOpenTelemetryInstrumentation(
13+
new FastifyInstrumentation({
14+
requestHook(span) {
15+
addOriginToSpan(span, 'auto.http.otel.fastify');
16+
},
17+
}),
18+
);
2119
},
2220
};
2321
}) satisfies IntegrationFn;

0 commit comments

Comments
 (0)