{"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing/redis/index.ts"],"sourcesContent":["import type { Span } from '@opentelemetry/api';\nimport type { IntegrationFn } from '@sentry/core';\nimport {\n defineIntegration,\n SEMANTIC_ATTRIBUTE_CACHE_HIT,\n SEMANTIC_ATTRIBUTE_CACHE_ITEM_SIZE,\n SEMANTIC_ATTRIBUTE_CACHE_KEY,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n spanToJSON,\n truncate,\n} from '@sentry/core';\nimport { subscribeRedisDiagnosticChannels } from '@sentry-internal/server-utils';\nimport { generateInstrumentOnce } from '@sentry/node-core';\nimport { tracingChannel as otelTracingChannel } from '@sentry/opentelemetry/tracing-channel';\nimport type { IORedisCommandArgs } from '../../../utils/redisCache';\nimport {\n calculateCacheItemSize,\n GET_COMMANDS,\n getCacheKeySafely,\n getCacheOperation,\n isInCommands,\n shouldConsiderForCache,\n} from '../../../utils/redisCache';\nimport type { IORedisResponseCustomAttributeFunction } from './vendored/types';\nimport { IORedisInstrumentation } from './vendored/ioredis-instrumentation';\nimport { RedisInstrumentation } from './vendored/redis-instrumentation';\n\ninterface RedisOptions {\n /**\n * Define cache prefixes for cache keys that should be captured as a cache span.\n *\n * Setting this to, for example, `['user:']` will capture cache keys that start with `user:`.\n */\n cachePrefixes?: string[];\n /**\n * Maximum length of the cache key added to the span description. If the key exceeds this length, it will be truncated.\n *\n * Passing `0` will use the full cache key without truncation.\n *\n * By default, the full cache key is used.\n */\n maxCacheKeyLength?: number;\n}\n\nconst INTEGRATION_NAME = 'Redis';\n\n/* Only exported for testing purposes */\nexport let _redisOptions: RedisOptions = {};\n\n/* Only exported for testing purposes */\nexport const cacheResponseHook: IORedisResponseCustomAttributeFunction = (\n span: Span,\n redisCommand: string,\n cmdArgs: IORedisCommandArgs,\n response: unknown,\n) => {\n const safeKey = getCacheKeySafely(redisCommand, cmdArgs);\n const cacheOperation = getCacheOperation(redisCommand);\n\n if (\n !safeKey ||\n !cacheOperation ||\n !_redisOptions.cachePrefixes ||\n !shouldConsiderForCache(redisCommand, safeKey, _redisOptions.cachePrefixes)\n ) {\n // not relevant for cache\n return;\n }\n\n // otel/ioredis seems to be using the old standard, as there was a change to those params: https://github.com/open-telemetry/opentelemetry-specification/issues/3199\n // We are using params based on the docs: https://opentelemetry.io/docs/specs/semconv/attributes-registry/network/\n // Fall back to stable semconv attributes (server.address/server.port) when\n // old-semconv ones are absent, eg OTEL_SEMCONV_STABILITY_OPT_IN=database\n // set for node-redis v4/v5.\n const spanData = spanToJSON(span).data;\n const networkPeerAddress = spanData['net.peer.name'] ?? spanData['server.address'];\n const networkPeerPort = spanData['net.peer.port'] ?? spanData['server.port'];\n if (networkPeerPort && networkPeerAddress) {\n span.setAttributes({ 'network.peer.address': networkPeerAddress, 'network.peer.port': networkPeerPort });\n }\n\n const cacheItemSize = calculateCacheItemSize(response);\n\n if (cacheItemSize) {\n span.setAttribute(SEMANTIC_ATTRIBUTE_CACHE_ITEM_SIZE, cacheItemSize);\n }\n\n if (isInCommands(GET_COMMANDS, redisCommand) && cacheItemSize !== undefined) {\n span.setAttribute(SEMANTIC_ATTRIBUTE_CACHE_HIT, cacheItemSize > 0);\n }\n\n span.setAttributes({\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: cacheOperation,\n [SEMANTIC_ATTRIBUTE_CACHE_KEY]: safeKey,\n });\n\n // todo: change to string[] once EAP supports it\n const spanDescription = safeKey.join(', ');\n\n span.updateName(\n _redisOptions.maxCacheKeyLength ? truncate(spanDescription, _redisOptions.maxCacheKeyLength) : spanDescription,\n );\n};\n\nconst instrumentIORedis = generateInstrumentOnce(`${INTEGRATION_NAME}.IORedis`, () => {\n return new IORedisInstrumentation({\n responseHook: cacheResponseHook,\n });\n});\n\nconst instrumentRedisModule = generateInstrumentOnce(`${INTEGRATION_NAME}.Redis`, () => {\n return new RedisInstrumentation({\n responseHook: cacheResponseHook,\n });\n});\n\n/**\n * To be able to preload all Redis OTel instrumentations with just one ID\n * (\"Redis\"), all the instrumentations are generated in this one function\n */\nexport const instrumentRedis = Object.assign(\n (): void => {\n instrumentIORedis();\n instrumentRedisModule();\n // node-redis >= 5.12.0 and ioredis >= 5.11.0 publish via diagnostics_channel.\n // We pass `@sentry/opentelemetry/tracing-channel` as the factory so the span\n // becomes the active OTel context via `bindStore`. That factory needs the\n // Sentry OTel context manager to be registered, which `initOpenTelemetry()`\n // does after integration `setupOnce`, so defer to the next tick.\n void Promise.resolve().then(() => subscribeRedisDiagnosticChannels(otelTracingChannel, cacheResponseHook));\n\n // todo: implement them gradually\n // new LegacyRedisInstrumentation({}),\n },\n { id: INTEGRATION_NAME },\n);\n\nconst _redisIntegration = ((options: RedisOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n _redisOptions = options;\n instrumentRedis();\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for the [redis](https://www.npmjs.com/package/redis) and\n * [ioredis](https://www.npmjs.com/package/ioredis) libraries.\n *\n * For more information, see the [`redisIntegration` documentation](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/redis/).\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n *\n * Sentry.init({\n * integrations: [Sentry.redisIntegration()],\n * });\n * ```\n */\nexport const redisIntegration = defineIntegration(_redisIntegration);\n"],"names":["_redisOptions","getCacheKeySafely","getCacheOperation","shouldConsiderForCache","spanToJSON","calculateCacheItemSize","SEMANTIC_ATTRIBUTE_CACHE_ITEM_SIZE","isInCommands","GET_COMMANDS","SEMANTIC_ATTRIBUTE_CACHE_HIT","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_CACHE_KEY","truncate","generateInstrumentOnce","IORedisInstrumentation","RedisInstrumentation","subscribeRedisDiagnosticChannels","otelTracingChannel","defineIntegration"],"mappings":"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"}