diff --git a/dev-packages/e2e-tests/test-applications/astro-7/tests/db.test.ts b/dev-packages/e2e-tests/test-applications/astro-7/tests/db.test.ts index 0d574839e992..a5fdaccc40ac 100644 --- a/dev-packages/e2e-tests/test-applications/astro-7/tests/db.test.ts +++ b/dev-packages/e2e-tests/test-applications/astro-7/tests/db.test.ts @@ -17,24 +17,26 @@ test('Instruments ioredis automatically', async ({ baseURL }) => { expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'set test-key [1 other arguments]', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'set', 'db.query.text': 'set test-key [1 other arguments]', }), }), ); expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'get test-key', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'get', 'db.query.text': 'get test-key', }), }), diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-v2/tests/db.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app-v2/tests/db.test.ts index a5959a69b4ca..74cf5ae27e5f 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-v2/tests/db.test.ts +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-v2/tests/db.test.ts @@ -21,24 +21,26 @@ test.describe('orchestrion DB instrumentation', () => { expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'set test-key [1 other arguments]', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'set', 'db.query.text': 'set test-key [1 other arguments]', }), }), ); expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'get test-key', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'get', 'db.query.text': 'get test-key', }), }), diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16/tests/db-page.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-16/tests/db-page.test.ts index 5805693f0217..564192e1d0a9 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-16/tests/db-page.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-16/tests/db-page.test.ts @@ -30,22 +30,28 @@ test('Instruments DB calls made during server-side rendering of a page', async ( ); expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'set page-key [1 other arguments]', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'set', 'db.query.text': 'set page-key [1 other arguments]', }), }), ); expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'get page-key', status: 'ok', + data: expect.objectContaining({ + 'db.system.name': 'redis', + 'db.operation.name': 'get', + 'db.query.text': 'get page-key', + }), }), ); }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/db-drivers.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/db-drivers.test.ts index edc49d309d1d..a4ef30dc0367 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/db-drivers.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/db-drivers.test.ts @@ -25,24 +25,26 @@ test('Instruments ioredis automatically', async ({ baseURL }) => { expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'set test-key [1 other arguments]', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'set', 'db.query.text': 'set test-key [1 other arguments]', }), }), ); expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'get test-key', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'get', 'db.query.text': 'get test-key', }), }), diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/db.server.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/db.server.test.ts index 3f1a0b059934..cbc6c0686d19 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/db.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-framework-instrumentation/tests/performance/db.server.test.ts @@ -29,24 +29,26 @@ test.describe('server - orchestrion build-time db instrumentation', () => { expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'set test-key [1 other arguments]', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'set', 'db.query.text': 'set test-key [1 other arguments]', }), }), ); expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'get test-key', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'get', 'db.query.text': 'get test-key', }), }), @@ -59,7 +61,7 @@ test.describe('server - orchestrion build-time db instrumentation', () => { // Every db span nests under the native instrumentation-API http.server transaction. const rootSpanId = transactionEvent.contexts?.trace?.span_id; const spanIds = new Set([rootSpanId, ...spans.map(span => span.span_id)]); - const dbSpans = spans.filter(span => span.op === 'db'); + const dbSpans = spans.filter(span => span.origin === 'auto.db.redis'); expect(dbSpans.every(span => typeof span.parent_span_id === 'string' && spanIds.has(span.parent_span_id))).toBe( true, ); diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/db.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/db.test.ts index 6e7270571658..ed2a1add22c1 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/tests/db.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/db.test.ts @@ -15,24 +15,26 @@ test('Instruments ioredis automatically via build-time orchestrion', async ({ ba expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'set test-key [1 other arguments]', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'set', 'db.query.text': 'set test-key [1 other arguments]', }), }), ); expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'get test-key', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'get', 'db.query.text': 'get test-key', }), }), diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/db.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/db.test.ts index b030b04f4be3..061e5c163960 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/db.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/tests/db.test.ts @@ -17,24 +17,26 @@ test('Instruments ioredis automatically', async ({ baseURL }) => { expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'set test-key [1 other arguments]', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'set', 'db.query.text': 'set test-key [1 other arguments]', }), }), ); expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'get test-key', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'get', 'db.query.text': 'get test-key', }), }), diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/db-drivers.test.ts b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/db-drivers.test.ts index 1d25ce7f8b2a..86eadcc1616a 100644 --- a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/db-drivers.test.ts +++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/db-drivers.test.ts @@ -24,24 +24,26 @@ test('Instruments ioredis automatically', async ({ baseURL }) => { expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'set test-key [1 other arguments]', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'set', 'db.query.text': 'set test-key [1 other arguments]', }), }), ); expect(spans).toContainEqual( expect.objectContaining({ - op: 'db', + op: 'db.query', origin: 'auto.db.redis', description: 'get test-key', status: 'ok', data: expect.objectContaining({ 'db.system.name': 'redis', + 'db.operation.name': 'get', 'db.query.text': 'get test-key', }), }), diff --git a/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts b/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts index 29c313b1363e..2c5262bc2195 100644 --- a/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts @@ -11,13 +11,7 @@ const expectedProducerSpan = (routingKey: string) => op: 'queue.publish', data: expect.objectContaining({ 'messaging.system': 'rabbitmq', - // Legacy messaging attributes emitted by both the OTel and orchestrion integrations. - 'messaging.destination': '', - 'messaging.destination_kind': 'topic', - 'messaging.rabbitmq.routing_key': routingKey, - 'messaging.url': 'amqp://sentry:***@localhost:5672/', - 'messaging.protocol': 'AMQP', - 'messaging.protocol_version': '0.9.1', + 'messaging.operation.name': 'send', 'messaging.operation.type': 'send', 'messaging.destination.name': '', 'messaging.rabbitmq.destination.routing_key': routingKey, @@ -37,14 +31,10 @@ const EXPECTED_MESSAGE_SPAN_CONSUMER = expect.objectContaining({ op: 'queue.process', data: expect.objectContaining({ 'messaging.system': 'rabbitmq', - // Legacy messaging attributes emitted by both the OTel and orchestrion integrations. The consumer - // reads the default exchange ('') off the delivered message and the queue name as the routing key. - 'messaging.destination': '', - 'messaging.destination_kind': 'topic', - 'messaging.rabbitmq.routing_key': 'queue1', - 'messaging.operation': 'process', + // The consumer reads the default exchange ('') off the delivered message and the queue name as the routing key. 'messaging.destination.name': '', 'messaging.rabbitmq.destination.routing_key': 'queue1', + 'messaging.operation.name': 'process', 'messaging.operation.type': 'process', 'sentry.kind': 'consumer', 'sentry.op': 'queue.process', diff --git a/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts b/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts index 3b46aa7673fe..712d7b3852e3 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts @@ -7,6 +7,7 @@ describeWithDockerCompose('redis cache auto instrumentation', { workingDirectory }); const redisOrigin = 'auto.db.redis'; + const redisSpanOp = 'db.query'; describe('ioredis non-cache keys', () => { const EXPECTED_TRANSACTION = { @@ -14,10 +15,10 @@ describeWithDockerCompose('redis cache auto instrumentation', { workingDirectory spans: expect.arrayContaining([ expect.objectContaining({ description: 'set test-key [1 other arguments]', - op: 'db', + op: redisSpanOp, origin: redisOrigin, data: expect.objectContaining({ - 'sentry.op': 'db', + 'sentry.op': redisSpanOp, 'db.system.name': 'redis', 'server.address': 'localhost', 'server.port': 6383, @@ -26,10 +27,10 @@ describeWithDockerCompose('redis cache auto instrumentation', { workingDirectory }), expect.objectContaining({ description: 'get test-key', - op: 'db', + op: redisSpanOp, origin: redisOrigin, data: expect.objectContaining({ - 'sentry.op': 'db', + 'sentry.op': redisSpanOp, 'db.system.name': 'redis', 'server.address': 'localhost', 'server.port': 6383, diff --git a/dev-packages/node-integration-tests/suites/tracing/redis/test.ts b/dev-packages/node-integration-tests/suites/tracing/redis/test.ts index 4d7e44f37787..90a625eb8adf 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis/test.ts @@ -10,48 +10,48 @@ describeWithDockerCompose('redis auto instrumentation', { workingDirectory: [__d // subscriber instead of the OTel monkey-patch, so the span origin differs. All // other attributes are identical. const origin = 'auto.db.redis'; + const redisSpanOp = 'db.query'; + const redisData = { + 'db.system.name': 'redis', + 'server.address': 'localhost', + 'server.port': 6380, + }; const EXPECTED_TRANSACTION = { transaction: 'Test Span', spans: expect.arrayContaining([ expect.objectContaining({ description: 'set test-key [1 other arguments]', - op: 'db', + op: redisSpanOp, origin, data: expect.objectContaining({ - 'sentry.op': 'db', + 'sentry.op': redisSpanOp, 'sentry.origin': origin, - 'db.system.name': 'redis', - 'server.address': 'localhost', - 'server.port': 6380, + ...redisData, 'db.query.text': 'set test-key [1 other arguments]', }), }), expect.objectContaining({ description: 'get test-key', - op: 'db', + op: redisSpanOp, origin, data: expect.objectContaining({ - 'sentry.op': 'db', + 'sentry.op': redisSpanOp, 'sentry.origin': origin, - 'db.system.name': 'redis', - 'server.address': 'localhost', - 'server.port': 6380, + ...redisData, 'db.query.text': 'get test-key', }), }), // a failing command produces a span with an error status expect.objectContaining({ description: 'incr test-key', - op: 'db', + op: redisSpanOp, status: 'internal_error', origin, data: expect.objectContaining({ - 'sentry.op': 'db', + 'sentry.op': redisSpanOp, 'sentry.origin': origin, - 'db.system.name': 'redis', - 'server.address': 'localhost', - 'server.port': 6380, + ...redisData, 'db.query.text': 'incr test-key', }), }), diff --git a/docs/migration/v11-end-state.md b/docs/migration/v11-end-state.md index 532b286ea63d..1290170c676a 100644 --- a/docs/migration/v11-end-state.md +++ b/docs/migration/v11-end-state.md @@ -614,8 +614,9 @@ Attribute availability remains runtime-dependent. For example, browser and Worke #### Messaging and database attributes -- Legacy messaging (`messaging.*`) span attributes on the AMQP instrumentation were replaced by their current semantic-convention equivalents. +- Legacy messaging (`messaging.*`) span attributes on the AMQP instrumentation were replaced by their current semantic-convention equivalents: `messaging.destination.name`, `messaging.rabbitmq.destination.routing_key`, `messaging.message.id`, `messaging.message.conversation_id`, `messaging.operation.name`, `network.protocol.name`, `network.protocol.version`, and `url.full`. `messaging.destination_kind` is no longer emitted. - The database span attributes `db.system`, `db.name`, `db.operation`, `db.statement` and `db.mongodb.collection` were renamed to `db.system.name`, `db.namespace`, `db.operation.name`, `db.query.text` and `db.collection.name`. +- The Redis and ioredis instrumentations no longer emit `db.connection_string`. The connection is described by `server.address` and `server.port` instead. #### GenAI attributes diff --git a/packages/server-utils/src/integrations/amqplib.ts b/packages/server-utils/src/integrations/amqplib.ts index 8fe16d212d47..3eb9b15f9c74 100644 --- a/packages/server-utils/src/integrations/amqplib.ts +++ b/packages/server-utils/src/integrations/amqplib.ts @@ -12,10 +12,11 @@ import { timestampInSeconds, } from '@sentry/core'; import { - MESSAGING_SYSTEM, + MESSAGING_DESTINATION_NAME, MESSAGING_MESSAGE_ID, + MESSAGING_OPERATION_NAME, MESSAGING_OPERATION_TYPE, - MESSAGING_DESTINATION_NAME, + MESSAGING_SYSTEM, NETWORK_PROTOCOL_NAME, NETWORK_PROTOCOL_VERSION, SENTRY_KIND, @@ -37,25 +38,9 @@ const INTEGRATION_NAME = 'Amqplib' as const; const PUBLISHER_ORIGIN = 'auto.amqplib.publisher'; const CONSUMER_ORIGIN = 'auto.amqplib.consumer'; -// Legacy messaging semantic-conventions, inlined to keep this integration free of `@opentelemetry/*` -// deps. These mirror what the vendored OTel amqplib instrumentation has always emitted. We keep -// emitting them alongside the current `@sentry/conventions` attributes for backwards compatibility. -// TODO(v11): remove these legacy attributes. -const ATTR_MESSAGING_OPERATION = 'messaging.operation'; -const ATTR_MESSAGING_DESTINATION = 'messaging.destination'; -const ATTR_MESSAGING_DESTINATION_KIND = 'messaging.destination_kind'; -const ATTR_MESSAGING_RABBITMQ_ROUTING_KEY = 'messaging.rabbitmq.routing_key'; -const ATTR_MESSAGING_PROTOCOL = 'messaging.protocol'; -const ATTR_MESSAGING_PROTOCOL_VERSION_LEGACY = 'messaging.protocol_version'; -const ATTR_MESSAGING_URL = 'messaging.url'; -const ATTR_MESSAGING_MESSAGE_ID = 'messaging.message_id'; -const ATTR_MESSAGING_CONVERSATION_ID_LEGACY = 'messaging.conversation_id'; - -// TODO(v11): replace with the corresponding attribute from `@sentry/conventions` once it is added there. const ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY = 'messaging.rabbitmq.destination.routing_key'; const ATTR_MESSAGING_CONVERSATION_ID = 'messaging.message.conversation_id'; -const MESSAGING_DESTINATION_KIND_VALUE_TOPIC = 'topic'; const MESSAGING_OPERATION_VALUE_PROCESS = 'process'; const MESSAGING_OPERATION_VALUE_SEND = 'send'; @@ -465,15 +450,11 @@ function startPublishSpan(data: AmqpChannelContext): Span { [SENTRY_OP]: QUEUE_PUBLISH, [SENTRY_KIND]: 'producer', ...getStoredConnectionAttributes(data.self), - [ATTR_MESSAGING_DESTINATION]: exchange, // TODO(v11) remove this attribute [MESSAGING_DESTINATION_NAME]: exchange, - [ATTR_MESSAGING_DESTINATION_KIND]: MESSAGING_DESTINATION_KIND_VALUE_TOPIC, // TODO(v11) remove this attribute - [ATTR_MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey, // TODO(v11) remove this attribute [ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY]: routingKey, + [MESSAGING_OPERATION_NAME]: MESSAGING_OPERATION_VALUE_SEND, [MESSAGING_OPERATION_TYPE]: MESSAGING_OPERATION_VALUE_SEND, - [ATTR_MESSAGING_MESSAGE_ID]: options?.messageId as string | undefined, // todo(v11) remove this attribute [MESSAGING_MESSAGE_ID]: options?.messageId as string | undefined, - [ATTR_MESSAGING_CONVERSATION_ID_LEGACY]: options?.correlationId as string | undefined, // todo(v11) remove this attribute [ATTR_MESSAGING_CONVERSATION_ID]: options?.correlationId as string | undefined, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: PUBLISHER_ORIGIN, }, @@ -504,16 +485,11 @@ function startConsumeSpan(queue: string, msg: ConsumeMessage, channel: ChannelLi [SENTRY_KIND]: 'consumer', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', ...getStoredConnectionAttributes(channel), - [ATTR_MESSAGING_DESTINATION]: msg.fields?.exchange, // TODO(v11) remove this attribute [MESSAGING_DESTINATION_NAME]: msg.fields?.exchange, - [ATTR_MESSAGING_DESTINATION_KIND]: MESSAGING_DESTINATION_KIND_VALUE_TOPIC, // TODO(v11) remove this attribute - [ATTR_MESSAGING_RABBITMQ_ROUTING_KEY]: msg.fields?.routingKey, // TODO(v11) remove this attribute [ATTR_MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY]: msg.fields?.routingKey, - [ATTR_MESSAGING_OPERATION]: MESSAGING_OPERATION_VALUE_PROCESS, // TODO(v11) remove this attribute + [MESSAGING_OPERATION_NAME]: MESSAGING_OPERATION_VALUE_PROCESS, [MESSAGING_OPERATION_TYPE]: MESSAGING_OPERATION_VALUE_PROCESS, - [ATTR_MESSAGING_MESSAGE_ID]: msg.properties?.messageId as string | undefined, // todo(v11) remove this attribute [MESSAGING_MESSAGE_ID]: msg.properties?.messageId as string | undefined, - [ATTR_MESSAGING_CONVERSATION_ID_LEGACY]: msg.properties?.correlationId as string | undefined, // todo(v11) remove this attribute [ATTR_MESSAGING_CONVERSATION_ID]: msg.properties?.correlationId as string | undefined, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: CONSUMER_ORIGIN, }, @@ -549,7 +525,6 @@ function getConnectionAttributesFromServer(conn: ConnectionLike): SpanAttributes function getConnectionAttributesFromUrl(url: unknown): SpanAttributes { const attributes: SpanAttributes = { // The only protocol supported by the instrumented library. - [ATTR_MESSAGING_PROTOCOL_VERSION_LEGACY]: '0.9.1', // TODO(v11): remove this attribute [NETWORK_PROTOCOL_VERSION]: '0.9.1', }; @@ -560,14 +535,12 @@ function getConnectionAttributesFromUrl(url: unknown): SpanAttributes { const hostname = getHostname(connectOptions.hostname); const port = getPort(connectOptions.port, protocol); - attributes[ATTR_MESSAGING_PROTOCOL] = protocol; // TODO(v11) remove this attribute attributes[NETWORK_PROTOCOL_NAME] = protocol; attributes[SERVER_ADDRESS] = hostname; attributes[SERVER_PORT] = port; } else if (typeof resolvedUrl === 'string') { const censoredUrl = censorPassword(resolvedUrl); - attributes[ATTR_MESSAGING_URL] = censoredUrl; // todo(v11) remove this attribute // oxlint-disable-next-line sdk/no-unfiltered-url-attributes -- AMQP connection URL, not an HTTP request URL attributes[URL_FULL] = censoredUrl; @@ -577,7 +550,6 @@ function getConnectionAttributesFromUrl(url: unknown): SpanAttributes { const hostname = getHostname(urlParts.hostname); const port = getPort(urlParts.port ? parseInt(urlParts.port, 10) : undefined, protocol); - attributes[ATTR_MESSAGING_PROTOCOL] = protocol; // TODO(v11) remove this attribute attributes[NETWORK_PROTOCOL_NAME] = protocol; attributes[SERVER_ADDRESS] = hostname; diff --git a/packages/server-utils/src/integrations/redis/index.ts b/packages/server-utils/src/integrations/redis/index.ts index 90c7af6ace87..bb237cf5c7c2 100644 --- a/packages/server-utils/src/integrations/redis/index.ts +++ b/packages/server-utils/src/integrations/redis/index.ts @@ -1,6 +1,7 @@ import * as diagnosticsChannel from 'node:diagnostics_channel'; import { DB_OPERATION_BATCH_SIZE, + DB_OPERATION_NAME, DB_QUERY_TEXT, DB_SYSTEM_NAME, SENTRY_KIND, @@ -35,8 +36,6 @@ const INTEGRATION_NAME = 'Redis' as const; const ORIGIN = 'auto.db.redis'; -// todo(v11): drop this — it is already covered by host and port. -const ATTR_DB_CONNECTION_STRING = 'db.connection_string'; const DB_SYSTEM_VALUE_REDIS = 'redis'; export interface RedisIntegrationOptions extends RedisCacheOptions {} @@ -91,27 +90,11 @@ function stripCommandOptions(args: unknown[]): unknown[] { return args; } -function removeCredentialsFromConnectionString(url: string | undefined): string | undefined { - if (typeof url !== 'string' || !url) { - return undefined; - } - try { - const parsed = new URL(url); - parsed.searchParams.delete('user_pwd'); - parsed.username = ''; - parsed.password = ''; - return parsed.href; - } catch { - return undefined; - } -} - function nodeRedisAttributes(options: NodeRedisClientOptions | undefined): SpanAttributes { return { [DB_SYSTEM_NAME]: DB_SYSTEM_VALUE_REDIS, - [SERVER_ADDRESS]: options?.socket?.host, - [SERVER_PORT]: options?.socket?.port, - [ATTR_DB_CONNECTION_STRING]: removeCredentialsFromConnectionString(options?.url), + ...(options?.socket?.host != null ? { [SERVER_ADDRESS]: options.socket.host } : {}), + ...(options?.socket?.port != null ? { [SERVER_PORT]: options.socket.port } : {}), [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, }; } @@ -124,6 +107,7 @@ function startCommandSpan(commandName: string, commandArgs: Array { return { - [DB_SYSTEM_NAME]: 'redis', - [ATTR_DB_CONNECTION_STRING]: `redis://${host}:${port}`, - [SERVER_ADDRESS]: host, - [SERVER_PORT]: port, + [DB_SYSTEM_NAME]: DB_SYSTEM_VALUE_REDIS, + ...(host != null ? { [SERVER_ADDRESS]: host } : {}), + ...(port != null ? { [SERVER_PORT]: port } : {}), [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, }; } @@ -71,8 +78,13 @@ export function startIORedisCommandSpan(data: IORedisCommandContext): Span | und const statement = defaultDbStatementSerializer(command.name, command.args ?? []); return startInactiveSpan({ name: statement, - op: 'db', - attributes: { ...connectionAttributes(host, port), [DB_QUERY_TEXT]: statement }, + attributes: { + [SENTRY_KIND]: 'client', + ...connectionAttributes(host, port), + [SENTRY_OP]: DB_QUERY, + [DB_OPERATION_NAME]: command.name, + [DB_QUERY_TEXT]: statement, + }, }); } @@ -110,8 +122,11 @@ export function instrumentIoredis(options: RedisCacheOptions): void { const { host, port } = getConnectionOptions(data.self); return startInactiveSpan({ name: 'connect', - op: 'db', - attributes: { ...connectionAttributes(host, port), [DB_QUERY_TEXT]: 'connect' }, + attributes: { + [SENTRY_KIND]: 'client', + ...connectionAttributes(host, port), + [SENTRY_OP]: DB, + }, }); }, { requiresParentSpan: true }, diff --git a/packages/server-utils/test/integrations/redis/ioredis-channel-subscriber.test.ts b/packages/server-utils/test/integrations/redis/ioredis-channel-subscriber.test.ts index 94d2baec17ec..b0e21487e95f 100644 --- a/packages/server-utils/test/integrations/redis/ioredis-channel-subscriber.test.ts +++ b/packages/server-utils/test/integrations/redis/ioredis-channel-subscriber.test.ts @@ -20,19 +20,19 @@ describe('startIORedisCommandSpan', () => { vi.restoreAllMocks(); }); - it('builds a db span with the orchestrion origin and stable db/net attributes', () => { + it('builds a db query span with Sentry convention attributes', () => { startIORedisCommandSpan(ctx({ name: 'set', args: ['test-key', 'test-value'] })); expect(startInactiveSpanSpy).toHaveBeenCalledWith( expect.objectContaining({ name: 'set test-key [1 other arguments]', - op: 'db', attributes: expect.objectContaining({ + 'sentry.op': 'db.query', 'db.system.name': 'redis', - 'db.connection_string': 'redis://localhost:6379', + 'db.operation.name': 'set', + 'db.query.text': 'set test-key [1 other arguments]', 'server.address': 'localhost', 'server.port': 6379, - 'db.query.text': 'set test-key [1 other arguments]', 'sentry.origin': 'auto.db.redis', }), }),