diff --git a/packages/node-experimental/src/integrations/express.ts b/packages/node-experimental/src/integrations/express.ts index 9c30d70f4be8..95b9527c8498 100644 --- a/packages/node-experimental/src/integrations/express.ts +++ b/packages/node-experimental/src/integrations/express.ts @@ -1,8 +1,8 @@ import type { Instrumentation } from '@opentelemetry/instrumentation'; import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express'; -import { addOtelSpanData } from '@sentry/opentelemetry-node'; import type { Integration } from '@sentry/types'; +import { addOriginToOtelSpan } from '../utils/addOriginToSpan'; import { NodePerformanceIntegration } from './NodePerformanceIntegration'; /** @@ -31,9 +31,7 @@ export class Express extends NodePerformanceIntegration implements Integra return [ new ExpressInstrumentation({ requestHook(span) { - addOtelSpanData(span.spanContext().spanId, { - origin: 'auto.http.otel.express', - }); + addOriginToOtelSpan(span, 'auto.http.otel.express'); }, }), ]; diff --git a/packages/node-experimental/src/integrations/fastify.ts b/packages/node-experimental/src/integrations/fastify.ts index e1098d8d89b9..b84301967616 100644 --- a/packages/node-experimental/src/integrations/fastify.ts +++ b/packages/node-experimental/src/integrations/fastify.ts @@ -1,8 +1,8 @@ import type { Instrumentation } from '@opentelemetry/instrumentation'; import { FastifyInstrumentation } from '@opentelemetry/instrumentation-fastify'; -import { addOtelSpanData } from '@sentry/opentelemetry-node'; import type { Integration } from '@sentry/types'; +import { addOriginToOtelSpan } from '../utils/addOriginToSpan'; import { NodePerformanceIntegration } from './NodePerformanceIntegration'; /** @@ -31,9 +31,7 @@ export class Fastify extends NodePerformanceIntegration implements Integra return [ new FastifyInstrumentation({ requestHook(span) { - addOtelSpanData(span.spanContext().spanId, { - origin: 'auto.http.otel.fastify', - }); + addOriginToOtelSpan(span, 'auto.http.otel.fastify'); }, }), ]; diff --git a/packages/node-experimental/src/integrations/graphql.ts b/packages/node-experimental/src/integrations/graphql.ts index 1515f69b6516..87749a0f54a2 100644 --- a/packages/node-experimental/src/integrations/graphql.ts +++ b/packages/node-experimental/src/integrations/graphql.ts @@ -1,8 +1,8 @@ import type { Instrumentation } from '@opentelemetry/instrumentation'; import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql'; -import { addOtelSpanData } from '@sentry/opentelemetry-node'; import type { Integration } from '@sentry/types'; +import { addOriginToOtelSpan } from '../utils/addOriginToSpan'; import { NodePerformanceIntegration } from './NodePerformanceIntegration'; /** @@ -32,9 +32,7 @@ export class GraphQL extends NodePerformanceIntegration implements Integra new GraphQLInstrumentation({ ignoreTrivialResolveSpans: true, responseHook(span) { - addOtelSpanData(span.spanContext().spanId, { - origin: 'auto.graphql.otel-graphql', - }); + addOriginToOtelSpan(span, 'auto.graphql.otel.graphql'); }, }), ]; diff --git a/packages/node-experimental/src/integrations/http.ts b/packages/node-experimental/src/integrations/http.ts index 6c31f9bce497..5f45334da290 100644 --- a/packages/node-experimental/src/integrations/http.ts +++ b/packages/node-experimental/src/integrations/http.ts @@ -4,10 +4,9 @@ import { registerInstrumentations } from '@opentelemetry/instrumentation'; import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; import type { Span as OtelSpan } from '@opentelemetry/sdk-trace-node'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; -import { hasTracingEnabled } from '@sentry/core'; +import { hasTracingEnabled, Transaction } from '@sentry/core'; import { getCurrentHub } from '@sentry/node'; -import type { AdditionalOtelSpanData } from '@sentry/opentelemetry-node'; -import { addOtelSpanData } from '@sentry/opentelemetry-node'; +import { _INTERNAL_getSentrySpan } from '@sentry/opentelemetry-node'; import type { EventProcessor, Hub, Integration } from '@sentry/types'; import type { ClientRequest, IncomingMessage, ServerResponse } from 'http'; @@ -146,34 +145,37 @@ export class Http implements Integration { const data = getRequestSpanData(span, request, response); const { attributes } = span; - const additionalData: AdditionalOtelSpanData = { - tags: {}, - data: { + const sentrySpan = _INTERNAL_getSentrySpan(span.spanContext().spanId); + if (sentrySpan) { + sentrySpan.origin = 'auto.http.otel.http'; + + const additionalData: Record = { url: data.url, - }, - contexts: {}, - metadata: {}, - origin: 'auto.http.otel.http', - }; - - if (span.kind === SpanKind.SERVER) { - additionalData.metadata = { request }; - } + }; - if (attributes[SemanticAttributes.HTTP_STATUS_CODE]) { - const statusCode = attributes[SemanticAttributes.HTTP_STATUS_CODE] as string; - additionalData.tags['http.status_code'] = statusCode; - additionalData.data['http.response.status_code'] = statusCode; - } + if (sentrySpan instanceof Transaction && span.kind === SpanKind.SERVER) { + sentrySpan.setMetadata({ request }); + } - if (data['http.query']) { - additionalData.data['http.query'] = data['http.query'].slice(1); - } - if (data['http.fragment']) { - additionalData.data['http.fragment'] = data['http.fragment'].slice(1); - } + if (attributes[SemanticAttributes.HTTP_STATUS_CODE]) { + const statusCode = attributes[SemanticAttributes.HTTP_STATUS_CODE] as string; + additionalData['http.response.status_code'] = statusCode; - addOtelSpanData(span.spanContext().spanId, additionalData); + sentrySpan.setTag('http.status_code', statusCode); + } + + if (data['http.query']) { + additionalData['http.query'] = data['http.query'].slice(1); + } + if (data['http.fragment']) { + additionalData['http.fragment'] = data['http.fragment'].slice(1); + } + + Object.keys(additionalData).forEach(prop => { + const value = additionalData[prop]; + sentrySpan.setData(prop, value); + }); + } if (this._breadcrumbs) { getCurrentHub().addBreadcrumb( diff --git a/packages/node-experimental/src/integrations/mongo.ts b/packages/node-experimental/src/integrations/mongo.ts index 2b8752d770ad..aea5d0a7d3fb 100644 --- a/packages/node-experimental/src/integrations/mongo.ts +++ b/packages/node-experimental/src/integrations/mongo.ts @@ -1,8 +1,8 @@ import type { Instrumentation } from '@opentelemetry/instrumentation'; import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb'; -import { addOtelSpanData } from '@sentry/opentelemetry-node'; import type { Integration } from '@sentry/types'; +import { addOriginToOtelSpan } from '../utils/addOriginToSpan'; import { NodePerformanceIntegration } from './NodePerformanceIntegration'; /** @@ -31,9 +31,7 @@ export class Mongo extends NodePerformanceIntegration implements Integrati return [ new MongoDBInstrumentation({ responseHook(span) { - addOtelSpanData(span.spanContext().spanId, { - origin: 'auto.db.otel-mongo', - }); + addOriginToOtelSpan(span, 'auto.db.otel.mongo'); }, }), ]; diff --git a/packages/node-experimental/src/integrations/mongoose.ts b/packages/node-experimental/src/integrations/mongoose.ts index 42d863966ea3..8f6eb65adb8b 100644 --- a/packages/node-experimental/src/integrations/mongoose.ts +++ b/packages/node-experimental/src/integrations/mongoose.ts @@ -1,8 +1,8 @@ import type { Instrumentation } from '@opentelemetry/instrumentation'; import { MongooseInstrumentation } from '@opentelemetry/instrumentation-mongoose'; -import { addOtelSpanData } from '@sentry/opentelemetry-node'; import type { Integration } from '@sentry/types'; +import { addOriginToOtelSpan } from '../utils/addOriginToSpan'; import { NodePerformanceIntegration } from './NodePerformanceIntegration'; /** @@ -31,9 +31,7 @@ export class Mongoose extends NodePerformanceIntegration implements Integr return [ new MongooseInstrumentation({ responseHook(span) { - addOtelSpanData(span.spanContext().spanId, { - origin: 'auto.db.otel-mongoose', - }); + addOriginToOtelSpan(span, 'auto.db.otel.mongoose'); }, }), ]; diff --git a/packages/node-experimental/src/integrations/mysql2.ts b/packages/node-experimental/src/integrations/mysql2.ts index f7d8f2c96fc9..b78b56bdd0ab 100644 --- a/packages/node-experimental/src/integrations/mysql2.ts +++ b/packages/node-experimental/src/integrations/mysql2.ts @@ -1,8 +1,8 @@ import type { Instrumentation } from '@opentelemetry/instrumentation'; import { MySQL2Instrumentation } from '@opentelemetry/instrumentation-mysql2'; -import { addOtelSpanData } from '@sentry/opentelemetry-node'; import type { Integration } from '@sentry/types'; +import { addOriginToOtelSpan } from '../utils/addOriginToSpan'; import { NodePerformanceIntegration } from './NodePerformanceIntegration'; /** @@ -31,9 +31,7 @@ export class Mysql2 extends NodePerformanceIntegration implements Integrat return [ new MySQL2Instrumentation({ responseHook(span) { - addOtelSpanData(span.spanContext().spanId, { - origin: 'auto.db.otel-mysql2', - }); + addOriginToOtelSpan(span, 'auto.db.otel.mysql2'); }, }), ]; diff --git a/packages/node-experimental/src/integrations/postgres.ts b/packages/node-experimental/src/integrations/postgres.ts index f6d1414a7fc0..0df7ae31d8ae 100644 --- a/packages/node-experimental/src/integrations/postgres.ts +++ b/packages/node-experimental/src/integrations/postgres.ts @@ -1,8 +1,8 @@ import type { Instrumentation } from '@opentelemetry/instrumentation'; import { PgInstrumentation } from '@opentelemetry/instrumentation-pg'; -import { addOtelSpanData } from '@sentry/opentelemetry-node'; import type { Integration } from '@sentry/types'; +import { addOriginToOtelSpan } from '../utils/addOriginToSpan'; import { NodePerformanceIntegration } from './NodePerformanceIntegration'; /** @@ -31,9 +31,7 @@ export class Postgres extends NodePerformanceIntegration implements Integr return [ new PgInstrumentation({ requestHook(span) { - addOtelSpanData(span.spanContext().spanId, { - origin: 'auto.db.otel-postgres', - }); + addOriginToOtelSpan(span, 'auto.db.otel.postgres'); }, }), ]; diff --git a/packages/node-experimental/src/utils/addOriginToSpan.ts b/packages/node-experimental/src/utils/addOriginToSpan.ts new file mode 100644 index 000000000000..c327043f0816 --- /dev/null +++ b/packages/node-experimental/src/utils/addOriginToSpan.ts @@ -0,0 +1,13 @@ +import type { Span as OtelSpan } from '@opentelemetry/api'; +import { _INTERNAL_getSentrySpan } from '@sentry/opentelemetry-node'; +import type { SpanOrigin } from '@sentry/types'; + +/** Adds an origin to an OTEL Span. */ +export function addOriginToOtelSpan(otelSpan: OtelSpan, origin: SpanOrigin): void { + const sentrySpan = _INTERNAL_getSentrySpan(otelSpan.spanContext().spanId); + if (!sentrySpan) { + return; + } + + sentrySpan.origin = origin; +} diff --git a/packages/opentelemetry-node/src/index.ts b/packages/opentelemetry-node/src/index.ts index 4f68820d23a2..24c477a968fd 100644 --- a/packages/opentelemetry-node/src/index.ts +++ b/packages/opentelemetry-node/src/index.ts @@ -1,3 +1,19 @@ +import { getSentrySpan } from './spanprocessor'; + export { SentrySpanProcessor } from './spanprocessor'; export { SentryPropagator } from './propagator'; -export * from './utils/spanData'; + +/* eslint-disable deprecation/deprecation */ +export { addOtelSpanData, getOtelSpanData, clearOtelSpanData } from './utils/spanData'; +export type { AdditionalOtelSpanData } from './utils/spanData'; +/* eslint-enable deprecation/deprecation */ + +/** + * This is only exported for internal use. + * Semver etc. does not apply here, this is subject to change at any time! + * This is explicitly _NOT_ public because we may have to change the underlying way we store/handle spans, + * which may make this API unusable without further notice. + * + * @private + */ +export { getSentrySpan as _INTERNAL_getSentrySpan }; diff --git a/packages/opentelemetry-node/src/spanprocessor.ts b/packages/opentelemetry-node/src/spanprocessor.ts index 0e208b050357..40757160663f 100644 --- a/packages/opentelemetry-node/src/spanprocessor.ts +++ b/packages/opentelemetry-node/src/spanprocessor.ts @@ -10,19 +10,19 @@ import { SENTRY_DYNAMIC_SAMPLING_CONTEXT_KEY, SENTRY_TRACE_PARENT_CONTEXT_KEY } import { isSentryRequestSpan } from './utils/isSentryRequest'; import { mapOtelStatus } from './utils/mapOtelStatus'; import { parseSpanDescription } from './utils/parseOtelSpanDescription'; -import { clearOtelSpanData, getOtelSpanData } from './utils/spanData'; -export const SENTRY_SPAN_PROCESSOR_MAP: Map = new Map< - SentrySpan['spanId'], - SentrySpan ->(); +export const SENTRY_SPAN_PROCESSOR_MAP: Map = new Map(); // make sure to remove references in maps, to ensure this can be GCed function clearSpan(otelSpanId: string): void { - clearOtelSpanData(otelSpanId); SENTRY_SPAN_PROCESSOR_MAP.delete(otelSpanId); } +/** Get a Sentry span for an otel span ID. */ +export function getSentrySpan(otelSpanId: string): SentrySpan | undefined { + return SENTRY_SPAN_PROCESSOR_MAP.get(otelSpanId); +} + /** * Converts OpenTelemetry Spans to Sentry Spans and sends them to Sentry via * the Sentry SDK. @@ -225,18 +225,10 @@ function updateSpanWithOtelData(sentrySpan: SentrySpan, otelSpan: OtelSpan): voi const { op, description, data } = parseSpanDescription(otelSpan); - const { data: additionalData, tags, origin } = getOtelSpanData(otelSpan.spanContext().spanId); - sentrySpan.setStatus(mapOtelStatus(otelSpan)); sentrySpan.setData('otel.kind', SpanKind[kind]); - if (tags) { - Object.keys(tags).forEach(prop => { - sentrySpan.setTag(prop, tags[prop]); - }); - } - - const allData = { ...attributes, ...data, ...additionalData }; + const allData = { ...attributes, ...data }; Object.keys(allData).forEach(prop => { const value = allData[prop]; @@ -245,52 +237,27 @@ function updateSpanWithOtelData(sentrySpan: SentrySpan, otelSpan: OtelSpan): voi sentrySpan.op = op; sentrySpan.description = description; - - if (origin) { - sentrySpan.origin = origin; - } } function updateTransactionWithOtelData(transaction: Transaction, otelSpan: OtelSpan): void { const { op, description, source, data } = parseSpanDescription(otelSpan); - const { data: additionalData, tags, contexts, metadata, origin } = getOtelSpanData(otelSpan.spanContext().spanId); transaction.setContext('otel', { attributes: otelSpan.attributes, resource: otelSpan.resource.attributes, }); - if (tags) { - Object.keys(tags).forEach(prop => { - transaction.setTag(prop, tags[prop]); - }); - } - - if (metadata) { - transaction.setMetadata(metadata); - } - - const allData = { ...data, ...additionalData }; + const allData = data || {}; Object.keys(allData).forEach(prop => { const value = allData[prop]; transaction.setData(prop, value); }); - if (contexts) { - Object.keys(contexts).forEach(prop => { - transaction.setContext(prop, contexts[prop]); - }); - } - transaction.setStatus(mapOtelStatus(otelSpan)); transaction.op = op; transaction.setName(description, source); - - if (origin) { - transaction.origin = origin; - } } function convertOtelTimeToSeconds([seconds, nano]: [number, number]): number { diff --git a/packages/opentelemetry-node/src/utils/spanData.ts b/packages/opentelemetry-node/src/utils/spanData.ts index 74d20b964985..d0e582d5763a 100644 --- a/packages/opentelemetry-node/src/utils/spanData.ts +++ b/packages/opentelemetry-node/src/utils/spanData.ts @@ -1,9 +1,14 @@ +/* eslint-disable deprecation/deprecation */ +import { Transaction } from '@sentry/core'; import type { Context, SpanOrigin } from '@sentry/types'; +import { getSentrySpan } from '../spanprocessor'; + type SentryTags = Record; type SentryData = Record; type Contexts = Record; +/** @deprecated This will be removed in v8. */ export interface AdditionalOtelSpanData { data: SentryData; tags: SentryTags; @@ -14,21 +19,59 @@ export interface AdditionalOtelSpanData { const OTEL_SPAN_DATA_MAP: Map = new Map(); -/** Add data that should be added to the sentry span resulting from the given otel span ID. */ -export function addOtelSpanData(otelSpanId: string, data: Partial): void { - OTEL_SPAN_DATA_MAP.set(otelSpanId, { data: {}, tags: {}, contexts: {}, metadata: {}, ...data }); -} +/** + * Add data that should be added to the sentry span resulting from the given otel span ID. + * @deprecated This will be removed in v8. This was never meant to be public API. + */ +export function addOtelSpanData( + otelSpanId: string, + { data, tags, contexts, metadata, origin }: Partial, +): void { + const sentrySpan = getSentrySpan(otelSpanId); + if (!sentrySpan) { + return; + } -/** Get additional data for a Sentry span. */ -export function getOtelSpanData(otelSpanId: string): AdditionalOtelSpanData { - if (OTEL_SPAN_DATA_MAP.has(otelSpanId)) { - return OTEL_SPAN_DATA_MAP.get(otelSpanId) as AdditionalOtelSpanData; + if (data) { + Object.keys(data).forEach(prop => { + const value = data[prop]; + sentrySpan.setData(prop, value); + }); } + if (tags) { + Object.keys(tags).forEach(prop => { + sentrySpan.setTag(prop, tags[prop]); + }); + } + + if (origin) { + sentrySpan.origin = origin; + } + + if (sentrySpan instanceof Transaction) { + if (metadata) { + sentrySpan.setMetadata(metadata); + } + + if (contexts) { + Object.keys(contexts).forEach(prop => { + sentrySpan.setContext(prop, contexts[prop]); + }); + } + } +} + +/** + * @deprecated This will do nothing and will be removed in v8. + */ +export function getOtelSpanData(_otelSpanId: string): AdditionalOtelSpanData { return { data: {}, tags: {}, contexts: {}, metadata: {} }; } -/** Add data that should be added to the sentry span resulting from the given otel span ID. */ +/** + * @deprecated This will do nothing and will be removed in v8. + */ export function clearOtelSpanData(otelSpanId: string): void { OTEL_SPAN_DATA_MAP.delete(otelSpanId); }