diff --git a/dev-packages/e2e-tests/test-applications/deno-redis/tests/ioredis.test.ts b/dev-packages/e2e-tests/test-applications/deno-redis/tests/ioredis.test.ts index 63a40d68d960..49a53850634b 100644 --- a/dev-packages/e2e-tests/test-applications/deno-redis/tests/ioredis.test.ts +++ b/dev-packages/e2e-tests/test-applications/deno-redis/tests/ioredis.test.ts @@ -22,8 +22,8 @@ test('ioredis GET emits an http.server transaction containing a db.redis child s expect(redisSpan).toBeDefined(); // ioredis publishes lowercase command names; node-redis publishes uppercase. expect(redisSpan!.description).toBe('redis-get'); - expect(redisSpan!.data?.['db.system']).toBe('redis'); - expect(redisSpan!.data?.['db.statement']).toBe('get iocache:user:42'); + expect(redisSpan!.data?.['db.system.name']).toBe('redis'); + expect(redisSpan!.data?.['db.query.text']).toBe('get iocache:user:42'); }); test('ioredis SET then GET emit two db.redis child spans on the same transaction', async ({ baseURL }) => { diff --git a/dev-packages/e2e-tests/test-applications/deno-redis/tests/redis.test.ts b/dev-packages/e2e-tests/test-applications/deno-redis/tests/redis.test.ts index b6bfa9fc4bd7..a5fdcd1b74a1 100644 --- a/dev-packages/e2e-tests/test-applications/deno-redis/tests/redis.test.ts +++ b/dev-packages/e2e-tests/test-applications/deno-redis/tests/redis.test.ts @@ -21,10 +21,10 @@ test('GET command emits an http.server transaction containing a db.redis child s const redisSpan = transaction.spans!.find(span => span.op === 'db.redis'); expect(redisSpan).toBeDefined(); expect(redisSpan!.description).toBe('redis-GET'); - expect(redisSpan!.data?.['db.system']).toBe('redis'); + expect(redisSpan!.data?.['db.system.name']).toBe('redis'); // Statement omits the value; for GET the only allowed arg is the key. - expect(redisSpan!.data?.['db.statement']).toBe('GET cache:user:42'); - expect(redisSpan!.data?.['net.peer.port']).toBe(6379); + expect(redisSpan!.data?.['db.query.text']).toBe('GET cache:user:42'); + expect(redisSpan!.data?.['server.port']).toBe(6379); }); test('SET then GET emit two db.redis child spans on the same transaction', async ({ baseURL }) => { @@ -65,7 +65,7 @@ test('MULTI batch emits a PIPELINE/MULTI batch span', async ({ baseURL }) => { const batchSpan = transaction.spans!.find(span => span.description === 'MULTI' || span.description === 'PIPELINE'); expect(batchSpan).toBeDefined(); expect(batchSpan!.op).toBe('db.redis'); - expect(batchSpan!.data?.['db.system']).toBe('redis'); + expect(batchSpan!.data?.['db.system.name']).toBe('redis'); }); test('shut down redis client', async ({ baseURL }) => { diff --git a/packages/deno/test/deno-redis.test.ts b/packages/deno/test/deno-redis.test.ts index 8ff9099f879c..cd4b59e86ec2 100644 --- a/packages/deno/test/deno-redis.test.ts +++ b/packages/deno/test/deno-redis.test.ts @@ -93,10 +93,10 @@ Deno.test('denoRedisIntegration: node-redis:command channel produces a db.redis const redisSpan = parent.spans?.find(s => s.op === 'db.redis'); assertExists(redisSpan, `expected a db.redis child span, got ops: ${parent.spans?.map(s => s.op).join(', ')}`); assertEquals(redisSpan!.description, 'redis-GET'); - assertEquals(redisSpan!.data?.['db.system'], 'redis'); - assertEquals(redisSpan!.data?.['db.statement'], 'GET cache:key'); - assertEquals(redisSpan!.data?.['net.peer.name'], '127.0.0.1'); - assertEquals(redisSpan!.data?.['net.peer.port'], 6379); + assertEquals(redisSpan!.data?.['db.system.name'], 'redis'); + assertEquals(redisSpan!.data?.['db.query.text'], 'GET cache:key'); + assertEquals(redisSpan!.data?.['server.address'], '127.0.0.1'); + assertEquals(redisSpan!.data?.['server.port'], 6379); }); Deno.test('denoRedisIntegration: errors on the command channel set span status', async () => { @@ -165,8 +165,8 @@ Deno.test('denoRedisIntegration: ioredis:command channel produces a db.redis chi const redisSpan = parent.spans?.find(s => s.op === 'db.redis'); assertExists(redisSpan, `expected a db.redis child span, got ops: ${parent.spans?.map(s => s.op).join(', ')}`); assertEquals(redisSpan!.description, 'redis-get'); - assertEquals(redisSpan!.data?.['db.system'], 'redis'); - assertEquals(redisSpan!.data?.['db.statement'], 'get cache:key'); - assertEquals(redisSpan!.data?.['net.peer.name'], '127.0.0.1'); - assertEquals(redisSpan!.data?.['net.peer.port'], 6379); + assertEquals(redisSpan!.data?.['db.system.name'], 'redis'); + assertEquals(redisSpan!.data?.['db.query.text'], 'get cache:key'); + assertEquals(redisSpan!.data?.['server.address'], '127.0.0.1'); + assertEquals(redisSpan!.data?.['server.port'], 6379); }); diff --git a/packages/server-utils/src/redis/redis-dc-subscriber.ts b/packages/server-utils/src/redis/redis-dc-subscriber.ts index 3dbc79c67f90..465f02f93c4c 100644 --- a/packages/server-utils/src/redis/redis-dc-subscriber.ts +++ b/packages/server-utils/src/redis/redis-dc-subscriber.ts @@ -20,13 +20,16 @@ export const IOREDIS_DC_CHANNEL_CONNECT = 'ioredis:connect'; const ORIGIN = 'auto.db.redis.diagnostic_channel'; -// Inlined semconv attribute keys — these are plain strings, no need to depend -// on @opentelemetry/semantic-conventions for them. -const ATTR_DB_STATEMENT = 'db.statement'; -const ATTR_DB_SYSTEM = 'db.system'; -const ATTR_NET_PEER_NAME = 'net.peer.name'; -const ATTR_NET_PEER_PORT = 'net.peer.port'; -const DB_SYSTEM_VALUE_REDIS = 'redis'; +// Inlined stable semconv attribute keys — these are plain strings, no need to +// depend on @opentelemetry/semantic-conventions for them. We use the stable +// OTel names (matching `@sentry/core`'s postgresjs integration) rather than the +// deprecated `db.statement`/`db.system`/`net.peer.*` forms. +const ATTR_DB_QUERY_TEXT = 'db.query.text'; +const ATTR_DB_SYSTEM_NAME = 'db.system.name'; +const ATTR_DB_OPERATION_BATCH_SIZE = 'db.operation.batch.size'; +const ATTR_SERVER_ADDRESS = 'server.address'; +const ATTR_SERVER_PORT = 'server.port'; +const DB_SYSTEM_NAME_VALUE_REDIS = 'redis'; const NOOP = (): void => {}; @@ -37,7 +40,7 @@ const NOOP = (): void => {}; * `sanitizeArgs` in @redis/client) using the OTel `redis-common` rules. The * arg array is `[, , ..., '?', ...]` — `?` replaces any * value the library considers sensitive. Subscribers can emit `args` directly - * as `db.statement` without further serialization. + * as `db.query.text` without further serialization. */ export interface RedisCommandData { command: string; @@ -203,10 +206,10 @@ function setupCommandChannel( attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis', - [ATTR_DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS, - [ATTR_DB_STATEMENT]: statement, - ...(data.serverAddress != null ? { [ATTR_NET_PEER_NAME]: data.serverAddress } : {}), - ...(data.serverPort != null ? { [ATTR_NET_PEER_PORT]: data.serverPort } : {}), + [ATTR_DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS, + [ATTR_DB_QUERY_TEXT]: statement, + ...(data.serverAddress != null ? { [ATTR_SERVER_ADDRESS]: data.serverAddress } : {}), + ...(data.serverPort != null ? { [ATTR_SERVER_PORT]: data.serverPort } : {}), }, }, span => span, @@ -247,10 +250,12 @@ function setupBatchChannel( attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis', - [ATTR_DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS, - ...(data.batchSize != null ? { 'db.redis.batch_size': data.batchSize } : {}), - ...(data.serverAddress != null ? { [ATTR_NET_PEER_NAME]: data.serverAddress } : {}), - ...(data.serverPort != null ? { [ATTR_NET_PEER_PORT]: data.serverPort } : {}), + [ATTR_DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS, + // should only include batch size greater than 1, + // or else it isn't properly considered a "batch" + ...(Number(data.batchSize) > 1 ? { [ATTR_DB_OPERATION_BATCH_SIZE]: data.batchSize } : {}), + ...(data.serverAddress != null ? { [ATTR_SERVER_ADDRESS]: data.serverAddress } : {}), + ...(data.serverPort != null ? { [ATTR_SERVER_PORT]: data.serverPort } : {}), }, }, span => span, @@ -283,9 +288,9 @@ function setupConnectChannel(tracingChannel: RedisTracingChannelFactory, channel attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis.connect', - [ATTR_DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS, - ...(data.serverAddress != null ? { [ATTR_NET_PEER_NAME]: data.serverAddress } : {}), - ...(data.serverPort != null ? { [ATTR_NET_PEER_PORT]: data.serverPort } : {}), + [ATTR_DB_SYSTEM_NAME]: DB_SYSTEM_NAME_VALUE_REDIS, + ...(data.serverAddress != null ? { [ATTR_SERVER_ADDRESS]: data.serverAddress } : {}), + ...(data.serverPort != null ? { [ATTR_SERVER_PORT]: data.serverPort } : {}), }, }, span => span,