@sentry/node 10.50.0 → 10.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/index.js +4 -4
- package/build/cjs/integrations/http.js +18 -145
- package/build/cjs/integrations/http.js.map +1 -1
- package/build/cjs/integrations/tracing/index.js +21 -22
- package/build/cjs/integrations/tracing/index.js.map +1 -1
- package/build/cjs/integrations/tracing/langgraph/instrumentation.js +70 -25
- package/build/cjs/integrations/tracing/langgraph/instrumentation.js.map +1 -1
- package/build/cjs/integrations/tracing/prisma.js +6 -2
- package/build/cjs/integrations/tracing/prisma.js.map +1 -1
- package/build/cjs/integrations/tracing/{redis.js → redis/index.js} +18 -10
- package/build/cjs/integrations/tracing/redis/index.js.map +1 -0
- package/build/cjs/integrations/tracing/redis/redis-dc-subscriber.js +186 -0
- package/build/cjs/integrations/tracing/redis/redis-dc-subscriber.js.map +1 -0
- package/build/cjs/integrations/tracing/redis/vendored/ioredis-instrumentation.js +255 -0
- package/build/cjs/integrations/tracing/redis/vendored/ioredis-instrumentation.js.map +1 -0
- package/build/cjs/integrations/tracing/redis/vendored/redis-common.js +74 -0
- package/build/cjs/integrations/tracing/redis/vendored/redis-common.js.map +1 -0
- package/build/cjs/integrations/tracing/redis/vendored/redis-instrumentation.js +685 -0
- package/build/cjs/integrations/tracing/redis/vendored/redis-instrumentation.js.map +1 -0
- package/build/cjs/integrations/tracing/redis/vendored/semconv.js +47 -0
- package/build/cjs/integrations/tracing/redis/vendored/semconv.js.map +1 -0
- package/build/cjs/utils/redisCache.js.map +1 -1
- package/build/esm/index.js +1 -1
- package/build/esm/integrations/http.js +21 -146
- package/build/esm/integrations/http.js.map +1 -1
- package/build/esm/integrations/tracing/index.js +2 -3
- package/build/esm/integrations/tracing/index.js.map +1 -1
- package/build/esm/integrations/tracing/langgraph/instrumentation.js +71 -26
- package/build/esm/integrations/tracing/langgraph/instrumentation.js.map +1 -1
- package/build/esm/integrations/tracing/prisma.js +6 -2
- package/build/esm/integrations/tracing/prisma.js.map +1 -1
- package/build/esm/integrations/tracing/{redis.js → redis/index.js} +17 -9
- package/build/esm/integrations/tracing/redis/index.js.map +1 -0
- package/build/esm/integrations/tracing/redis/redis-dc-subscriber.js +184 -0
- package/build/esm/integrations/tracing/redis/redis-dc-subscriber.js.map +1 -0
- package/build/esm/integrations/tracing/redis/vendored/ioredis-instrumentation.js +253 -0
- package/build/esm/integrations/tracing/redis/vendored/ioredis-instrumentation.js.map +1 -0
- package/build/esm/integrations/tracing/redis/vendored/redis-common.js +72 -0
- package/build/esm/integrations/tracing/redis/vendored/redis-common.js.map +1 -0
- package/build/esm/integrations/tracing/redis/vendored/redis-instrumentation.js +683 -0
- package/build/esm/integrations/tracing/redis/vendored/redis-instrumentation.js.map +1 -0
- package/build/esm/integrations/tracing/redis/vendored/semconv.js +39 -0
- package/build/esm/integrations/tracing/redis/vendored/semconv.js.map +1 -0
- package/build/esm/package.json +1 -1
- package/build/esm/utils/redisCache.js.map +1 -1
- package/build/types/integrations/http.d.ts +8 -15
- package/build/types/integrations/http.d.ts.map +1 -1
- package/build/types/integrations/tracing/index.d.ts.map +1 -1
- package/build/types/integrations/tracing/langgraph/instrumentation.d.ts +1 -1
- package/build/types/integrations/tracing/langgraph/instrumentation.d.ts.map +1 -1
- package/build/types/integrations/tracing/prisma.d.ts.map +1 -1
- package/build/types/integrations/tracing/{redis.d.ts → redis/index.d.ts} +3 -3
- package/build/types/integrations/tracing/redis/index.d.ts.map +1 -0
- package/build/types/integrations/tracing/redis/redis-dc-subscriber.d.ts +17 -0
- package/build/types/integrations/tracing/redis/redis-dc-subscriber.d.ts.map +1 -0
- package/build/types/integrations/tracing/redis/vendored/ioredis-instrumentation.d.ts +15 -0
- package/build/types/integrations/tracing/redis/vendored/ioredis-instrumentation.d.ts.map +1 -0
- package/build/types/integrations/tracing/redis/vendored/redis-common.d.ts +6 -0
- package/build/types/integrations/tracing/redis/vendored/redis-common.d.ts.map +1 -0
- package/build/types/integrations/tracing/redis/vendored/redis-instrumentation.d.ts +16 -0
- package/build/types/integrations/tracing/redis/vendored/redis-instrumentation.d.ts.map +1 -0
- package/build/types/integrations/tracing/redis/vendored/semconv.d.ts +8 -0
- package/build/types/integrations/tracing/redis/vendored/semconv.d.ts.map +1 -0
- package/build/types/integrations/tracing/redis/vendored/types.d.ts +58 -0
- package/build/types/integrations/tracing/redis/vendored/types.d.ts.map +1 -0
- package/build/types/utils/redisCache.d.ts +1 -1
- package/build/types/utils/redisCache.d.ts.map +1 -1
- package/build/types-ts3.8/integrations/http.d.ts +8 -15
- package/build/types-ts3.8/integrations/tracing/langgraph/instrumentation.d.ts +1 -1
- package/build/types-ts3.8/integrations/tracing/{redis.d.ts → redis/index.d.ts} +3 -3
- package/build/types-ts3.8/integrations/tracing/redis/redis-dc-subscriber.d.ts +17 -0
- package/build/types-ts3.8/integrations/tracing/redis/vendored/ioredis-instrumentation.d.ts +15 -0
- package/build/types-ts3.8/integrations/tracing/redis/vendored/redis-common.d.ts +6 -0
- package/build/types-ts3.8/integrations/tracing/redis/vendored/redis-instrumentation.d.ts +16 -0
- package/build/types-ts3.8/integrations/tracing/redis/vendored/semconv.d.ts +8 -0
- package/build/types-ts3.8/integrations/tracing/redis/vendored/types.d.ts +58 -0
- package/build/types-ts3.8/utils/redisCache.d.ts +1 -1
- package/package.json +4 -6
- package/build/cjs/integrations/tracing/redis.js.map +0 -1
- package/build/esm/integrations/tracing/redis.js.map +0 -1
- package/build/types/integrations/tracing/redis.d.ts.map +0 -1
|
@@ -183,8 +183,12 @@ const prismaIntegration = core.defineIntegration((options) => {
|
|
|
183
183
|
span.setAttribute(core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.prisma');
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
// Make sure we use the query text as the span name, for ex. SELECT * FROM "User" WHERE "id" = $1
|
|
187
|
-
|
|
186
|
+
// Make sure we use the query text as the span name, for ex. SELECT * FROM "User" WHERE "id" = $1.
|
|
187
|
+
// v5/v6 emit `prisma:engine:db_query`; v7 inlined the engine and emits `prisma:client:db_query`.
|
|
188
|
+
if (
|
|
189
|
+
(spanJSON.description === 'prisma:engine:db_query' || spanJSON.description === 'prisma:client:db_query') &&
|
|
190
|
+
spanJSON.data['db.query.text']
|
|
191
|
+
) {
|
|
188
192
|
span.updateName(spanJSON.data['db.query.text'] );
|
|
189
193
|
}
|
|
190
194
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma.js","sources":["../../../../src/integrations/tracing/prisma.ts"],"sourcesContent":["import type { Link, Tracer } from '@opentelemetry/api';\nimport { context, SpanKind, trace, TraceFlags } from '@opentelemetry/api';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\nimport type { IdGenerator } from '@opentelemetry/sdk-trace-base';\nimport { PrismaInstrumentation } from '@prisma/instrumentation';\nimport { consoleSandbox, defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, spanToJSON } from '@sentry/core';\nimport { generateInstrumentOnce } from '@sentry/node-core';\nimport type { PrismaV5TracingHelper } from './prisma/vendor/v5-tracing-helper';\nimport type { PrismaV6TracingHelper } from './prisma/vendor/v6-tracing-helper';\n\nconst INTEGRATION_NAME = 'Prisma';\n\ntype CompatibilityLayerTraceHelper = PrismaV5TracingHelper & PrismaV6TracingHelper;\n\n// Vendored in from @prisma/instrumentation v5:\ntype V5EngineSpanEvent = {\n span: boolean;\n spans: V5EngineSpan[];\n};\n\ntype V5EngineSpanKind = 'client' | 'internal';\n\ntype V5EngineSpan = {\n span: boolean;\n name: string;\n trace_id: string;\n span_id: string;\n parent_span_id: string;\n start_time: [number, number];\n end_time: [number, number];\n attributes?: Record<string, string>;\n links?: { trace_id: string; span_id: string }[];\n kind: V5EngineSpanKind;\n};\n\nfunction isPrismaV6TracingHelper(helper: unknown): helper is PrismaV6TracingHelper {\n return !!helper && typeof helper === 'object' && 'dispatchEngineSpans' in helper;\n}\n\nfunction getPrismaTracingHelper(): unknown | undefined {\n const prismaInstrumentationObject = (globalThis as Record<string, unknown>).PRISMA_INSTRUMENTATION;\n const prismaTracingHelper =\n prismaInstrumentationObject &&\n typeof prismaInstrumentationObject === 'object' &&\n 'helper' in prismaInstrumentationObject\n ? prismaInstrumentationObject.helper\n : undefined;\n\n return prismaTracingHelper;\n}\n\ntype TracerWithIdGenerator = Tracer & {\n _idGenerator?: IdGenerator;\n};\n\ninterface PrismaOptions {\n /**\n * @deprecated This is no longer used, v5 works out of the box.\n */\n prismaInstrumentation?: Instrumentation;\n /**\n * Configuration passed through to the {@link PrismaInstrumentation} constructor.\n */\n instrumentationConfig?: ConstructorParameters<typeof PrismaInstrumentation>[0];\n}\n\nclass SentryPrismaInteropInstrumentation extends PrismaInstrumentation {\n public constructor(options?: PrismaOptions) {\n super(options?.instrumentationConfig);\n }\n\n public enable(): void {\n super.enable();\n\n // The PrismaIntegration (super class) defines a global variable `global[\"PRISMA_INSTRUMENTATION\"]` when `enable()` is called. This global variable holds a \"TracingHelper\" which Prisma uses internally to create tracing data. It's their way of not depending on OTEL with their main package. The sucky thing is, prisma broke the interface of the tracing helper with the v6 major update. This means that if you use Prisma 5 with the v6 instrumentation (or vice versa) Prisma just blows up, because tries to call methods on the helper that no longer exist.\n // Because we actually want to use the v6 instrumentation and not blow up in Prisma 5 user's faces, what we're doing here is backfilling the v5 method (`createEngineSpan`) with a noop so that no longer crashes when it attempts to call that function.\n const prismaTracingHelper = getPrismaTracingHelper();\n\n if (isPrismaV6TracingHelper(prismaTracingHelper)) {\n // Inspired & adjusted from https://github.com/prisma/prisma/tree/5.22.0/packages/instrumentation\n (prismaTracingHelper as CompatibilityLayerTraceHelper).createEngineSpan = (\n engineSpanEvent: V5EngineSpanEvent,\n ) => {\n const tracer = trace.getTracer('prismaV5Compatibility') as TracerWithIdGenerator;\n\n // Prisma v5 relies on being able to create spans with a specific span & trace ID\n // this is no longer possible in OTEL v2, there is no public API to do this anymore\n // So in order to kind of hack this possibility, we rely on the internal `_idGenerator` property\n // This is used to generate the random IDs, and we overwrite this temporarily to generate static IDs\n // This is flawed and may not work, e.g. if the code is bundled and the private property is renamed\n // in such cases, these spans will not be captured and some Prisma spans will be missing\n const initialIdGenerator = tracer._idGenerator;\n\n if (!initialIdGenerator) {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n '[Sentry] Could not find _idGenerator on tracer, skipping Prisma v5 compatibility - some Prisma spans may be missing!',\n );\n });\n\n return;\n }\n\n try {\n engineSpanEvent.spans.forEach(engineSpan => {\n const kind = engineSpanKindToOTELSpanKind(engineSpan.kind);\n\n const parentSpanId = engineSpan.parent_span_id;\n const spanId = engineSpan.span_id;\n const traceId = engineSpan.trace_id;\n\n const links: Link[] | undefined = engineSpan.links?.map(link => {\n return {\n context: {\n traceId: link.trace_id,\n spanId: link.span_id,\n traceFlags: TraceFlags.SAMPLED,\n },\n };\n });\n\n const ctx = trace.setSpanContext(context.active(), {\n traceId,\n spanId: parentSpanId,\n traceFlags: TraceFlags.SAMPLED,\n });\n\n context.with(ctx, () => {\n const temporaryIdGenerator: IdGenerator = {\n generateTraceId: () => {\n return traceId;\n },\n generateSpanId: () => {\n return spanId;\n },\n };\n\n tracer._idGenerator = temporaryIdGenerator;\n\n const span = tracer.startSpan(engineSpan.name, {\n kind,\n links,\n startTime: engineSpan.start_time,\n attributes: engineSpan.attributes,\n });\n\n span.end(engineSpan.end_time);\n\n tracer._idGenerator = initialIdGenerator;\n });\n });\n } finally {\n // Ensure we always restore this at the end, even if something errors\n tracer._idGenerator = initialIdGenerator;\n }\n };\n }\n }\n}\n\nfunction engineSpanKindToOTELSpanKind(engineSpanKind: V5EngineSpanKind): SpanKind {\n switch (engineSpanKind) {\n case 'client':\n return SpanKind.CLIENT;\n case 'internal':\n default: // Other span kinds aren't currently supported\n return SpanKind.INTERNAL;\n }\n}\n\nexport const instrumentPrisma = generateInstrumentOnce<PrismaOptions>(INTEGRATION_NAME, options => {\n return new SentryPrismaInteropInstrumentation(options);\n});\n\n/**\n * Adds Sentry tracing instrumentation for the [prisma](https://www.npmjs.com/package/prisma) library.\n * For more information, see the [`prismaIntegration` documentation](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/).\n *\n * NOTE: By default, this integration works with Prisma version 6.\n * To get performance instrumentation for other Prisma versions,\n * 1. Install the `@prisma/instrumentation` package with the desired version.\n * 1. Pass a `new PrismaInstrumentation()` instance as exported from `@prisma/instrumentation` to the `prismaInstrumentation` option of this integration:\n *\n * ```js\n * import { PrismaInstrumentation } from '@prisma/instrumentation'\n *\n * Sentry.init({\n * integrations: [\n * prismaIntegration({\n * // Override the default instrumentation that Sentry uses\n * prismaInstrumentation: new PrismaInstrumentation()\n * })\n * ]\n * })\n * ```\n *\n * The passed instrumentation instance will override the default instrumentation instance the integration would use, while the `prismaIntegration` will still ensure data compatibility for the various Prisma versions.\n * 1. Depending on your Prisma version (prior to version 6), add `previewFeatures = [\"tracing\"]` to the client generator block of your Prisma schema:\n *\n * ```\n * generator client {\n * provider = \"prisma-client-js\"\n * previewFeatures = [\"tracing\"]\n * }\n * ```\n */\nexport const prismaIntegration = defineIntegration((options?: PrismaOptions) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentPrisma(options);\n },\n setup(client) {\n // If no tracing helper exists, we skip any work here\n // this means that prisma is not being used\n if (!getPrismaTracingHelper()) {\n return;\n }\n\n client.on('spanStart', span => {\n const spanJSON = spanToJSON(span);\n if (spanJSON.description?.startsWith('prisma:')) {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.prisma');\n }\n\n // Make sure we use the query text as the span name, for ex. SELECT * FROM \"User\" WHERE \"id\" = $1\n if (spanJSON.description === 'prisma:engine:db_query' && spanJSON.data['db.query.text']) {\n span.updateName(spanJSON.data['db.query.text'] as string);\n }\n\n // In Prisma v5.22+, the `db.system` attribute is automatically set\n // On older versions, this is missing, so we add it here\n if (spanJSON.description === 'prisma:engine:db_query' && !spanJSON.data['db.system']) {\n span.setAttribute('db.system', 'prisma');\n }\n });\n },\n };\n});\n"],"names":["PrismaInstrumentation","trace","consoleSandbox","TraceFlags","context","SpanKind","generateInstrumentOnce","defineIntegration","spanToJSON","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN"],"mappings":";;;;;;;AAUA,MAAM,gBAAA,GAAmB,QAAQ;;AAyBjC,SAAS,uBAAuB,CAAC,MAAM,EAA4C;AACnF,EAAE,OAAO,CAAC,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,IAAY,qBAAA,IAAyB,MAAM;AAClF;;AAEA,SAAS,sBAAsB,GAAwB;AACvD,EAAE,MAAM,2BAAA,GAA8B,CAAC,UAAA,GAAuC,sBAAsB;AACpG,EAAE,MAAM,mBAAA;AACR,IAAI,2BAAA;AACJ,IAAI,OAAO,2BAAA,KAAgC,QAAA;AAC3C,IAAI,YAAY;AAChB,QAAQ,2BAA2B,CAAC;AACpC,QAAQ,SAAS;;AAEjB,EAAE,OAAO,mBAAmB;AAC5B;;AAiBA,MAAM,kCAAA,SAA2CA,qCAAA,CAAsB;AACvE,GAAS,WAAW,CAAC,OAAO,EAAkB;AAC9C,IAAI,KAAK,CAAC,OAAO,EAAE,qBAAqB,CAAC;AACzC,EAAE;;AAEF,GAAS,MAAM,GAAS;AACxB,IAAI,KAAK,CAAC,MAAM,EAAE;;AAElB;AACA;AACA,IAAI,MAAM,mBAAA,GAAsB,sBAAsB,EAAE;;AAExD,IAAI,IAAI,uBAAuB,CAAC,mBAAmB,CAAC,EAAE;AACtD;AACA,MAAM,CAAC,mBAAA,GAAsD,mBAAmB;AAChF,QAAQ,eAAe;AACvB,WAAW;AACX,QAAQ,MAAM,SAASC,SAAK,CAAC,SAAS,CAAC,uBAAuB,CAAA;;AAE9D;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ,MAAM,kBAAA,GAAqB,MAAM,CAAC,YAAY;;AAEtD,QAAQ,IAAI,CAAC,kBAAkB,EAAE;AACjC,UAAUC,mBAAc,CAAC,MAAM;AAC/B;AACA,YAAY,OAAO,CAAC,IAAI;AACxB,cAAc,sHAAsH;AACpI,aAAa;AACb,UAAU,CAAC,CAAC;;AAEZ,UAAU;AACV,QAAQ;;AAER,QAAQ,IAAI;AACZ,UAAU,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc;AACtD,YAAY,MAAM,OAAO,4BAA4B,CAAC,UAAU,CAAC,IAAI,CAAC;;AAEtE,YAAY,MAAM,YAAA,GAAe,UAAU,CAAC,cAAc;AAC1D,YAAY,MAAM,MAAA,GAAS,UAAU,CAAC,OAAO;AAC7C,YAAY,MAAM,OAAA,GAAU,UAAU,CAAC,QAAQ;;AAE/C,YAAY,MAAM,KAAK,GAAuB,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,IAAA,IAAQ;AAC5E,cAAc,OAAO;AACrB,gBAAgB,OAAO,EAAE;AACzB,kBAAkB,OAAO,EAAE,IAAI,CAAC,QAAQ;AACxC,kBAAkB,MAAM,EAAE,IAAI,CAAC,OAAO;AACtC,kBAAkB,UAAU,EAAEC,cAAU,CAAC,OAAO;AAChD,iBAAiB;AACjB,eAAe;AACf,YAAY,CAAC,CAAC;;AAEd,YAAY,MAAM,GAAA,GAAMF,SAAK,CAAC,cAAc,CAACG,WAAO,CAAC,MAAM,EAAE,EAAE;AAC/D,cAAc,OAAO;AACrB,cAAc,MAAM,EAAE,YAAY;AAClC,cAAc,UAAU,EAAED,cAAU,CAAC,OAAO;AAC5C,aAAa,CAAC;;AAEd,YAAYC,WAAO,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM;AACpC,cAAc,MAAM,oBAAoB,GAAgB;AACxD,gBAAgB,eAAe,EAAE,MAAM;AACvC,kBAAkB,OAAO,OAAO;AAChC,gBAAgB,CAAC;AACjB,gBAAgB,cAAc,EAAE,MAAM;AACtC,kBAAkB,OAAO,MAAM;AAC/B,gBAAgB,CAAC;AACjB,eAAe;;AAEf,cAAc,MAAM,CAAC,YAAA,GAAe,oBAAoB;;AAExD,cAAc,MAAM,IAAA,GAAO,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE;AAC7D,gBAAgB,IAAI;AACpB,gBAAgB,KAAK;AACrB,gBAAgB,SAAS,EAAE,UAAU,CAAC,UAAU;AAChD,gBAAgB,UAAU,EAAE,UAAU,CAAC,UAAU;AACjD,eAAe,CAAC;;AAEhB,cAAc,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;;AAE3C,cAAc,MAAM,CAAC,YAAA,GAAe,kBAAkB;AACtD,YAAY,CAAC,CAAC;AACd,UAAU,CAAC,CAAC;AACZ,QAAQ,UAAU;AAClB;AACA,UAAU,MAAM,CAAC,YAAA,GAAe,kBAAkB;AAClD,QAAQ;AACR,MAAM,CAAC;AACP,IAAI;AACJ,EAAE;AACF;;AAEA,SAAS,4BAA4B,CAAC,cAAc,EAA8B;AAClF,EAAE,QAAQ,cAAc;AACxB,IAAI,KAAK,QAAQ;AACjB,MAAM,OAAOC,YAAQ,CAAC,MAAM;AAC5B,IAAI,KAAK,UAAU;AACnB,IAAI;AACJ,MAAM,OAAOA,YAAQ,CAAC,QAAQ;AAC9B;AACA;;AAEO,MAAM,mBAAmBC,+BAAsB,CAAgB,gBAAgB,EAAE,WAAW;AACnG,EAAE,OAAO,IAAI,kCAAkC,CAAC,OAAO,CAAC;AACxD,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,oBAAoBC,sBAAiB,CAAC,CAAC,OAAO,KAAqB;AAChF,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB,MAAM,gBAAgB,CAAC,OAAO,CAAC;AAC/B,IAAI,CAAC;AACL,IAAI,KAAK,CAAC,MAAM,EAAE;AAClB;AACA;AACA,MAAM,IAAI,CAAC,sBAAsB,EAAE,EAAE;AACrC,QAAQ;AACR,MAAM;;AAEN,MAAM,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,QAAQ;AACrC,QAAQ,MAAM,QAAA,GAAWC,eAAU,CAAC,IAAI,CAAC;AACzC,QAAQ,IAAI,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE;AACzD,UAAU,IAAI,CAAC,YAAY,CAACC,qCAAgC,EAAE,qBAAqB,CAAC;AACpF,QAAQ;;AAER;AACA,QAAQ,IAAI,QAAQ,CAAC,gBAAgB,wBAAA,IAA4B,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE;AACjG,UAAU,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAA,EAAY;AACnE,QAAQ;;AAER;AACA;AACA,QAAQ,IAAI,QAAQ,CAAC,gBAAgB,wBAAA,IAA4B,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE;AAC9F,UAAU,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC;AAClD,QAAQ;AACR,MAAM,CAAC,CAAC;AACR,IAAI,CAAC;AACL,GAAG;AACH,CAAC;;;;;"}
|
|
1
|
+
{"version":3,"file":"prisma.js","sources":["../../../../src/integrations/tracing/prisma.ts"],"sourcesContent":["import type { Link, Tracer } from '@opentelemetry/api';\nimport { context, SpanKind, trace, TraceFlags } from '@opentelemetry/api';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\nimport type { IdGenerator } from '@opentelemetry/sdk-trace-base';\nimport { PrismaInstrumentation } from '@prisma/instrumentation';\nimport { consoleSandbox, defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, spanToJSON } from '@sentry/core';\nimport { generateInstrumentOnce } from '@sentry/node-core';\nimport type { PrismaV5TracingHelper } from './prisma/vendor/v5-tracing-helper';\nimport type { PrismaV6TracingHelper } from './prisma/vendor/v6-tracing-helper';\n\nconst INTEGRATION_NAME = 'Prisma';\n\ntype CompatibilityLayerTraceHelper = PrismaV5TracingHelper & PrismaV6TracingHelper;\n\n// Vendored in from @prisma/instrumentation v5:\ntype V5EngineSpanEvent = {\n span: boolean;\n spans: V5EngineSpan[];\n};\n\ntype V5EngineSpanKind = 'client' | 'internal';\n\ntype V5EngineSpan = {\n span: boolean;\n name: string;\n trace_id: string;\n span_id: string;\n parent_span_id: string;\n start_time: [number, number];\n end_time: [number, number];\n attributes?: Record<string, string>;\n links?: { trace_id: string; span_id: string }[];\n kind: V5EngineSpanKind;\n};\n\nfunction isPrismaV6TracingHelper(helper: unknown): helper is PrismaV6TracingHelper {\n return !!helper && typeof helper === 'object' && 'dispatchEngineSpans' in helper;\n}\n\nfunction getPrismaTracingHelper(): unknown | undefined {\n const prismaInstrumentationObject = (globalThis as Record<string, unknown>).PRISMA_INSTRUMENTATION;\n const prismaTracingHelper =\n prismaInstrumentationObject &&\n typeof prismaInstrumentationObject === 'object' &&\n 'helper' in prismaInstrumentationObject\n ? prismaInstrumentationObject.helper\n : undefined;\n\n return prismaTracingHelper;\n}\n\ntype TracerWithIdGenerator = Tracer & {\n _idGenerator?: IdGenerator;\n};\n\ninterface PrismaOptions {\n /**\n * @deprecated This is no longer used, v5 works out of the box.\n */\n prismaInstrumentation?: Instrumentation;\n /**\n * Configuration passed through to the {@link PrismaInstrumentation} constructor.\n */\n instrumentationConfig?: ConstructorParameters<typeof PrismaInstrumentation>[0];\n}\n\nclass SentryPrismaInteropInstrumentation extends PrismaInstrumentation {\n public constructor(options?: PrismaOptions) {\n super(options?.instrumentationConfig);\n }\n\n public enable(): void {\n super.enable();\n\n // The PrismaIntegration (super class) defines a global variable `global[\"PRISMA_INSTRUMENTATION\"]` when `enable()` is called. This global variable holds a \"TracingHelper\" which Prisma uses internally to create tracing data. It's their way of not depending on OTEL with their main package. The sucky thing is, prisma broke the interface of the tracing helper with the v6 major update. This means that if you use Prisma 5 with the v6 instrumentation (or vice versa) Prisma just blows up, because tries to call methods on the helper that no longer exist.\n // Because we actually want to use the v6 instrumentation and not blow up in Prisma 5 user's faces, what we're doing here is backfilling the v5 method (`createEngineSpan`) with a noop so that no longer crashes when it attempts to call that function.\n const prismaTracingHelper = getPrismaTracingHelper();\n\n if (isPrismaV6TracingHelper(prismaTracingHelper)) {\n // Inspired & adjusted from https://github.com/prisma/prisma/tree/5.22.0/packages/instrumentation\n (prismaTracingHelper as CompatibilityLayerTraceHelper).createEngineSpan = (\n engineSpanEvent: V5EngineSpanEvent,\n ) => {\n const tracer = trace.getTracer('prismaV5Compatibility') as TracerWithIdGenerator;\n\n // Prisma v5 relies on being able to create spans with a specific span & trace ID\n // this is no longer possible in OTEL v2, there is no public API to do this anymore\n // So in order to kind of hack this possibility, we rely on the internal `_idGenerator` property\n // This is used to generate the random IDs, and we overwrite this temporarily to generate static IDs\n // This is flawed and may not work, e.g. if the code is bundled and the private property is renamed\n // in such cases, these spans will not be captured and some Prisma spans will be missing\n const initialIdGenerator = tracer._idGenerator;\n\n if (!initialIdGenerator) {\n consoleSandbox(() => {\n // eslint-disable-next-line no-console\n console.warn(\n '[Sentry] Could not find _idGenerator on tracer, skipping Prisma v5 compatibility - some Prisma spans may be missing!',\n );\n });\n\n return;\n }\n\n try {\n engineSpanEvent.spans.forEach(engineSpan => {\n const kind = engineSpanKindToOTELSpanKind(engineSpan.kind);\n\n const parentSpanId = engineSpan.parent_span_id;\n const spanId = engineSpan.span_id;\n const traceId = engineSpan.trace_id;\n\n const links: Link[] | undefined = engineSpan.links?.map(link => {\n return {\n context: {\n traceId: link.trace_id,\n spanId: link.span_id,\n traceFlags: TraceFlags.SAMPLED,\n },\n };\n });\n\n const ctx = trace.setSpanContext(context.active(), {\n traceId,\n spanId: parentSpanId,\n traceFlags: TraceFlags.SAMPLED,\n });\n\n context.with(ctx, () => {\n const temporaryIdGenerator: IdGenerator = {\n generateTraceId: () => {\n return traceId;\n },\n generateSpanId: () => {\n return spanId;\n },\n };\n\n tracer._idGenerator = temporaryIdGenerator;\n\n const span = tracer.startSpan(engineSpan.name, {\n kind,\n links,\n startTime: engineSpan.start_time,\n attributes: engineSpan.attributes,\n });\n\n span.end(engineSpan.end_time);\n\n tracer._idGenerator = initialIdGenerator;\n });\n });\n } finally {\n // Ensure we always restore this at the end, even if something errors\n tracer._idGenerator = initialIdGenerator;\n }\n };\n }\n }\n}\n\nfunction engineSpanKindToOTELSpanKind(engineSpanKind: V5EngineSpanKind): SpanKind {\n switch (engineSpanKind) {\n case 'client':\n return SpanKind.CLIENT;\n case 'internal':\n default: // Other span kinds aren't currently supported\n return SpanKind.INTERNAL;\n }\n}\n\nexport const instrumentPrisma = generateInstrumentOnce<PrismaOptions>(INTEGRATION_NAME, options => {\n return new SentryPrismaInteropInstrumentation(options);\n});\n\n/**\n * Adds Sentry tracing instrumentation for the [prisma](https://www.npmjs.com/package/prisma) library.\n * For more information, see the [`prismaIntegration` documentation](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/).\n *\n * NOTE: By default, this integration works with Prisma version 6.\n * To get performance instrumentation for other Prisma versions,\n * 1. Install the `@prisma/instrumentation` package with the desired version.\n * 1. Pass a `new PrismaInstrumentation()` instance as exported from `@prisma/instrumentation` to the `prismaInstrumentation` option of this integration:\n *\n * ```js\n * import { PrismaInstrumentation } from '@prisma/instrumentation'\n *\n * Sentry.init({\n * integrations: [\n * prismaIntegration({\n * // Override the default instrumentation that Sentry uses\n * prismaInstrumentation: new PrismaInstrumentation()\n * })\n * ]\n * })\n * ```\n *\n * The passed instrumentation instance will override the default instrumentation instance the integration would use, while the `prismaIntegration` will still ensure data compatibility for the various Prisma versions.\n * 1. Depending on your Prisma version (prior to version 6), add `previewFeatures = [\"tracing\"]` to the client generator block of your Prisma schema:\n *\n * ```\n * generator client {\n * provider = \"prisma-client-js\"\n * previewFeatures = [\"tracing\"]\n * }\n * ```\n */\nexport const prismaIntegration = defineIntegration((options?: PrismaOptions) => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentPrisma(options);\n },\n setup(client) {\n // If no tracing helper exists, we skip any work here\n // this means that prisma is not being used\n if (!getPrismaTracingHelper()) {\n return;\n }\n\n client.on('spanStart', span => {\n const spanJSON = spanToJSON(span);\n if (spanJSON.description?.startsWith('prisma:')) {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.prisma');\n }\n\n // Make sure we use the query text as the span name, for ex. SELECT * FROM \"User\" WHERE \"id\" = $1.\n // v5/v6 emit `prisma:engine:db_query`; v7 inlined the engine and emits `prisma:client:db_query`.\n if (\n (spanJSON.description === 'prisma:engine:db_query' || spanJSON.description === 'prisma:client:db_query') &&\n spanJSON.data['db.query.text']\n ) {\n span.updateName(spanJSON.data['db.query.text'] as string);\n }\n\n // In Prisma v5.22+, the `db.system` attribute is automatically set\n // On older versions, this is missing, so we add it here\n if (spanJSON.description === 'prisma:engine:db_query' && !spanJSON.data['db.system']) {\n span.setAttribute('db.system', 'prisma');\n }\n });\n },\n };\n});\n"],"names":["PrismaInstrumentation","trace","consoleSandbox","TraceFlags","context","SpanKind","generateInstrumentOnce","defineIntegration","spanToJSON","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN"],"mappings":"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"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
-
const instrumentationIoredis = require('@opentelemetry/instrumentation-ioredis');
|
|
4
|
-
const instrumentationRedis = require('@opentelemetry/instrumentation-redis');
|
|
5
3
|
const core = require('@sentry/core');
|
|
6
4
|
const nodeCore = require('@sentry/node-core');
|
|
7
|
-
const redisCache = require('
|
|
5
|
+
const redisCache = require('../../../utils/redisCache.js');
|
|
6
|
+
const ioredisInstrumentation = require('./vendored/ioredis-instrumentation.js');
|
|
7
|
+
const redisInstrumentation = require('./vendored/redis-instrumentation.js');
|
|
8
|
+
const redisDcSubscriber = require('./redis-dc-subscriber.js');
|
|
8
9
|
|
|
9
10
|
const INTEGRATION_NAME = 'Redis';
|
|
10
11
|
|
|
@@ -18,8 +19,6 @@ const cacheResponseHook = (
|
|
|
18
19
|
cmdArgs,
|
|
19
20
|
response,
|
|
20
21
|
) => {
|
|
21
|
-
span.setAttribute(core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.redis');
|
|
22
|
-
|
|
23
22
|
const safeKey = redisCache.getCacheKeySafely(redisCommand, cmdArgs);
|
|
24
23
|
const cacheOperation = redisCache.getCacheOperation(redisCommand);
|
|
25
24
|
|
|
@@ -35,8 +34,12 @@ const cacheResponseHook = (
|
|
|
35
34
|
|
|
36
35
|
// 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
|
|
37
36
|
// We are using params based on the docs: https://opentelemetry.io/docs/specs/semconv/attributes-registry/network/
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
// Fall back to stable semconv attributes (server.address/server.port) when
|
|
38
|
+
// old-semconv ones are absent, eg OTEL_SEMCONV_STABILITY_OPT_IN=database
|
|
39
|
+
// set for node-redis v4/v5.
|
|
40
|
+
const spanData = core.spanToJSON(span).data;
|
|
41
|
+
const networkPeerAddress = spanData['net.peer.name'] ?? spanData['server.address'];
|
|
42
|
+
const networkPeerPort = spanData['net.peer.port'] ?? spanData['server.port'];
|
|
40
43
|
if (networkPeerPort && networkPeerAddress) {
|
|
41
44
|
span.setAttributes({ 'network.peer.address': networkPeerAddress, 'network.peer.port': networkPeerPort });
|
|
42
45
|
}
|
|
@@ -65,13 +68,13 @@ const cacheResponseHook = (
|
|
|
65
68
|
};
|
|
66
69
|
|
|
67
70
|
const instrumentIORedis = nodeCore.generateInstrumentOnce(`${INTEGRATION_NAME}.IORedis`, () => {
|
|
68
|
-
return new
|
|
71
|
+
return new ioredisInstrumentation.IORedisInstrumentation({
|
|
69
72
|
responseHook: cacheResponseHook,
|
|
70
73
|
});
|
|
71
74
|
});
|
|
72
75
|
|
|
73
76
|
const instrumentRedisModule = nodeCore.generateInstrumentOnce(`${INTEGRATION_NAME}.Redis`, () => {
|
|
74
|
-
return new
|
|
77
|
+
return new redisInstrumentation.RedisInstrumentation({
|
|
75
78
|
responseHook: cacheResponseHook,
|
|
76
79
|
});
|
|
77
80
|
});
|
|
@@ -81,6 +84,11 @@ const instrumentRedis = Object.assign(
|
|
|
81
84
|
() => {
|
|
82
85
|
instrumentIORedis();
|
|
83
86
|
instrumentRedisModule();
|
|
87
|
+
// node-redis >= 5.12.0 publishes via diagnostics_channel. The subscriber uses
|
|
88
|
+
// `@sentry/opentelemetry/tracing-channel`, which needs the Sentry OTel context manager
|
|
89
|
+
// to be registered before it can `bindStore`. `initOpenTelemetry()` runs after integration
|
|
90
|
+
// `setupOnce`, so defer to the next tick.
|
|
91
|
+
void Promise.resolve().then(() => redisDcSubscriber.subscribeRedisDiagnosticChannels(cacheResponseHook));
|
|
84
92
|
|
|
85
93
|
// todo: implement them gradually
|
|
86
94
|
// new LegacyRedisInstrumentation({}),
|
|
@@ -118,4 +126,4 @@ const redisIntegration = core.defineIntegration(_redisIntegration);
|
|
|
118
126
|
exports.cacheResponseHook = cacheResponseHook;
|
|
119
127
|
exports.instrumentRedis = instrumentRedis;
|
|
120
128
|
exports.redisIntegration = redisIntegration;
|
|
121
|
-
//# sourceMappingURL=
|
|
129
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"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 { generateInstrumentOnce } from '@sentry/node-core';\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';\nimport { subscribeRedisDiagnosticChannels } from './redis-dc-subscriber';\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/** To be able to preload all Redis OTel instrumentations with just one ID (\"Redis\"), all the instrumentations are generated in this one function */\nexport const instrumentRedis = Object.assign(\n (): void => {\n instrumentIORedis();\n instrumentRedisModule();\n // node-redis >= 5.12.0 publishes via diagnostics_channel. The subscriber uses\n // `@sentry/opentelemetry/tracing-channel`, which needs the Sentry OTel context manager\n // to be registered before it can `bindStore`. `initOpenTelemetry()` runs after integration\n // `setupOnce`, so defer to the next tick.\n void Promise.resolve().then(() => subscribeRedisDiagnosticChannels(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","defineIntegration"],"mappings":"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"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const core = require('@sentry/core');
|
|
4
|
+
const tracingChannel = require('@sentry/opentelemetry/tracing-channel');
|
|
5
|
+
const redisCommon = require('./vendored/redis-common.js');
|
|
6
|
+
const semconv = require('./vendored/semconv.js');
|
|
7
|
+
|
|
8
|
+
// Channel names as published by node-redis >= 5.12.0.
|
|
9
|
+
// Hardcoded so we don't import `redis` at module-load time.
|
|
10
|
+
const CHANNEL_COMMAND = 'node-redis:command';
|
|
11
|
+
const CHANNEL_BATCH = 'node-redis:batch';
|
|
12
|
+
const CHANNEL_CONNECT = 'node-redis:connect';
|
|
13
|
+
|
|
14
|
+
const ORIGIN = 'auto.db.redis.diagnostic_channel';
|
|
15
|
+
|
|
16
|
+
const NOOP = () => {};
|
|
17
|
+
|
|
18
|
+
let subscribed = false;
|
|
19
|
+
let currentResponseHook;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe Sentry handlers to node-redis diagnostics_channel events (>= 5.12.0).
|
|
23
|
+
*
|
|
24
|
+
* Uses `@sentry/opentelemetry/tracing-channel` so OTel AsyncLocalStorage context propagates
|
|
25
|
+
* automatically via `bindStore` — without it, spans created in `start` would not become
|
|
26
|
+
* the active context for subsequent operations.
|
|
27
|
+
*
|
|
28
|
+
* Safe on every runtime that exposes `node:diagnostics_channel` (Node, Bun, Deno, Workers).
|
|
29
|
+
* In node-redis < 5.12.0 the channels are never published to, so subscribers are inert and
|
|
30
|
+
* there is no double-instrumentation against the IITM-based patcher (gated to < 5.12.0).
|
|
31
|
+
*/
|
|
32
|
+
function subscribeRedisDiagnosticChannels(responseHook) {
|
|
33
|
+
currentResponseHook = responseHook;
|
|
34
|
+
if (subscribed) return;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
setupCommandChannel();
|
|
38
|
+
setupBatchChannel();
|
|
39
|
+
setupConnectChannel();
|
|
40
|
+
subscribed = true;
|
|
41
|
+
} catch {
|
|
42
|
+
// tracingChannel from @sentry/opentelemetry requires `node:diagnostics_channel`.
|
|
43
|
+
// On runtimes where it isn't available, fail closed.
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function setupCommandChannel() {
|
|
48
|
+
const channel = tracingChannel.tracingChannel(CHANNEL_COMMAND, data => {
|
|
49
|
+
// node-redis >= 5.12.0 includes the command name as args[0] in the DC payload.
|
|
50
|
+
// Strip it so serialization and cache key extraction see only the actual arguments.
|
|
51
|
+
const actualArgs = data.args.slice(1);
|
|
52
|
+
const statement = safeSerialize(data.command, actualArgs);
|
|
53
|
+
return core.startSpanManual(
|
|
54
|
+
{
|
|
55
|
+
name: `redis-${data.command}`,
|
|
56
|
+
attributes: {
|
|
57
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,
|
|
58
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',
|
|
59
|
+
[semconv.ATTR_DB_SYSTEM]: semconv.DB_SYSTEM_VALUE_REDIS,
|
|
60
|
+
...(statement != null ? { [semconv.ATTR_DB_STATEMENT]: statement } : {}),
|
|
61
|
+
...(data.serverAddress != null ? { [semconv.ATTR_NET_PEER_NAME]: data.serverAddress } : {}),
|
|
62
|
+
...(data.serverPort != null ? { [semconv.ATTR_NET_PEER_PORT]: data.serverPort } : {}),
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
span => span,
|
|
66
|
+
) ;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
channel.subscribe({
|
|
70
|
+
start: NOOP,
|
|
71
|
+
asyncStart: NOOP,
|
|
72
|
+
end: NOOP,
|
|
73
|
+
asyncEnd: data => {
|
|
74
|
+
const span = data._sentrySpan;
|
|
75
|
+
// only end if error handler isn't going to
|
|
76
|
+
if (!span || data.error) return;
|
|
77
|
+
// Same slice: strip command name from args before passing to the response hook.
|
|
78
|
+
runResponseHook(span, data.command, data.args.slice(1), data.result);
|
|
79
|
+
span.end();
|
|
80
|
+
},
|
|
81
|
+
error: data => {
|
|
82
|
+
const span = data._sentrySpan;
|
|
83
|
+
if (!span) return;
|
|
84
|
+
if (data.error) {
|
|
85
|
+
span.setStatus({ code: core.SPAN_STATUS_ERROR, message: data.error.message });
|
|
86
|
+
}
|
|
87
|
+
span.end();
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function setupBatchChannel() {
|
|
93
|
+
const channel = tracingChannel.tracingChannel(CHANNEL_BATCH, data => {
|
|
94
|
+
const operationName = data.batchMode === 'PIPELINE' ? 'PIPELINE' : 'MULTI';
|
|
95
|
+
|
|
96
|
+
return core.startSpanManual(
|
|
97
|
+
{
|
|
98
|
+
name: operationName,
|
|
99
|
+
attributes: {
|
|
100
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,
|
|
101
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',
|
|
102
|
+
[semconv.ATTR_DB_SYSTEM]: semconv.DB_SYSTEM_VALUE_REDIS,
|
|
103
|
+
...(data.batchSize != null ? { 'db.redis.batch_size': data.batchSize } : {}),
|
|
104
|
+
...(data.serverAddress != null ? { [semconv.ATTR_NET_PEER_NAME]: data.serverAddress } : {}),
|
|
105
|
+
...(data.serverPort != null ? { [semconv.ATTR_NET_PEER_PORT]: data.serverPort } : {}),
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
span => span,
|
|
109
|
+
) ;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
channel.subscribe({
|
|
113
|
+
start: NOOP,
|
|
114
|
+
asyncStart: NOOP,
|
|
115
|
+
end: NOOP,
|
|
116
|
+
asyncEnd: data => {
|
|
117
|
+
// only end if the error handler isn't going to
|
|
118
|
+
if (!data.error) data._sentrySpan?.end();
|
|
119
|
+
},
|
|
120
|
+
error: data => {
|
|
121
|
+
const span = data._sentrySpan;
|
|
122
|
+
if (!span) return;
|
|
123
|
+
if (data.error) {
|
|
124
|
+
span.setStatus({ code: core.SPAN_STATUS_ERROR, message: data.error.message });
|
|
125
|
+
}
|
|
126
|
+
span.end();
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function setupConnectChannel() {
|
|
132
|
+
const channel = tracingChannel.tracingChannel(CHANNEL_CONNECT, data => {
|
|
133
|
+
return core.startSpanManual(
|
|
134
|
+
{
|
|
135
|
+
name: 'redis-connect',
|
|
136
|
+
attributes: {
|
|
137
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,
|
|
138
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis.connect',
|
|
139
|
+
[semconv.ATTR_DB_SYSTEM]: semconv.DB_SYSTEM_VALUE_REDIS,
|
|
140
|
+
...(data.serverAddress != null ? { [semconv.ATTR_NET_PEER_NAME]: data.serverAddress } : {}),
|
|
141
|
+
...(data.serverPort != null ? { [semconv.ATTR_NET_PEER_PORT]: data.serverPort } : {}),
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
span => span,
|
|
145
|
+
) ;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
channel.subscribe({
|
|
149
|
+
start: NOOP,
|
|
150
|
+
asyncStart: NOOP,
|
|
151
|
+
end: NOOP,
|
|
152
|
+
asyncEnd: data => {
|
|
153
|
+
// only end if the error handler isn't going to
|
|
154
|
+
if (!data.error) data._sentrySpan?.end();
|
|
155
|
+
},
|
|
156
|
+
error: data => {
|
|
157
|
+
const span = data._sentrySpan;
|
|
158
|
+
if (!span) return;
|
|
159
|
+
if (data.error) {
|
|
160
|
+
span.setStatus({ code: core.SPAN_STATUS_ERROR, message: data.error.message });
|
|
161
|
+
}
|
|
162
|
+
span.end();
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function runResponseHook(span, command, args, result) {
|
|
168
|
+
const hook = currentResponseHook;
|
|
169
|
+
if (!hook) return;
|
|
170
|
+
try {
|
|
171
|
+
hook(span, command, args , result);
|
|
172
|
+
} catch {
|
|
173
|
+
// never let user hooks break instrumentation
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function safeSerialize(command, args) {
|
|
178
|
+
try {
|
|
179
|
+
return redisCommon.defaultDbStatementSerializer(command, args);
|
|
180
|
+
} catch {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
exports.subscribeRedisDiagnosticChannels = subscribeRedisDiagnosticChannels;
|
|
186
|
+
//# sourceMappingURL=redis-dc-subscriber.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-dc-subscriber.js","sources":["../../../../../src/integrations/tracing/redis/redis-dc-subscriber.ts"],"sourcesContent":["import type { Span } from '@opentelemetry/api';\nimport {\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startSpanManual,\n} from '@sentry/core';\nimport { tracingChannel, type TracingChannelContextWithSpan } from '@sentry/opentelemetry/tracing-channel';\nimport { defaultDbStatementSerializer } from './vendored/redis-common';\nimport {\n ATTR_DB_STATEMENT,\n ATTR_DB_SYSTEM,\n ATTR_NET_PEER_NAME,\n ATTR_NET_PEER_PORT,\n DB_SYSTEM_VALUE_REDIS,\n} from './vendored/semconv';\nimport type { IORedisInstrumentationConfig } from './vendored/types';\n\n// Channel names as published by node-redis >= 5.12.0.\n// Hardcoded so we don't import `redis` at module-load time.\nconst CHANNEL_COMMAND = 'node-redis:command';\nconst CHANNEL_BATCH = 'node-redis:batch';\nconst CHANNEL_CONNECT = 'node-redis:connect';\n\nconst ORIGIN = 'auto.db.redis.diagnostic_channel';\n\ninterface CommandData {\n command: string;\n args: Array<string | Buffer>;\n database?: number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown;\n error?: Error;\n}\n\ninterface BatchData {\n batchMode?: 'MULTI' | 'PIPELINE';\n batchSize?: number;\n database?: number;\n clientId?: string | number;\n serverAddress?: string;\n serverPort?: number;\n result?: unknown[];\n error?: Error;\n}\n\ninterface ConnectData {\n serverAddress?: string;\n serverPort?: number;\n url?: string;\n error?: Error;\n}\n\nconst NOOP = (): void => {};\n\nlet subscribed = false;\nlet currentResponseHook: IORedisInstrumentationConfig['responseHook'] | undefined;\n\n/**\n * Subscribe Sentry handlers to node-redis diagnostics_channel events (>= 5.12.0).\n *\n * Uses `@sentry/opentelemetry/tracing-channel` so OTel AsyncLocalStorage context propagates\n * automatically via `bindStore` — without it, spans created in `start` would not become\n * the active context for subsequent operations.\n *\n * Safe on every runtime that exposes `node:diagnostics_channel` (Node, Bun, Deno, Workers).\n * In node-redis < 5.12.0 the channels are never published to, so subscribers are inert and\n * there is no double-instrumentation against the IITM-based patcher (gated to < 5.12.0).\n */\nexport function subscribeRedisDiagnosticChannels(responseHook?: IORedisInstrumentationConfig['responseHook']): void {\n currentResponseHook = responseHook;\n if (subscribed) return;\n\n try {\n setupCommandChannel();\n setupBatchChannel();\n setupConnectChannel();\n subscribed = true;\n } catch {\n // tracingChannel from @sentry/opentelemetry requires `node:diagnostics_channel`.\n // On runtimes where it isn't available, fail closed.\n }\n}\n\nfunction setupCommandChannel(): void {\n const channel = tracingChannel<CommandData>(CHANNEL_COMMAND, data => {\n // node-redis >= 5.12.0 includes the command name as args[0] in the DC payload.\n // Strip it so serialization and cache key extraction see only the actual arguments.\n const actualArgs = data.args.slice(1);\n const statement = safeSerialize(data.command, actualArgs);\n return startSpanManual(\n {\n name: `redis-${data.command}`,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [ATTR_DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS,\n ...(statement != null ? { [ATTR_DB_STATEMENT]: statement } : {}),\n ...(data.serverAddress != null ? { [ATTR_NET_PEER_NAME]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [ATTR_NET_PEER_PORT]: data.serverPort } : {}),\n },\n },\n span => span,\n ) as Span;\n });\n\n channel.subscribe({\n start: NOOP,\n asyncStart: NOOP,\n end: NOOP,\n asyncEnd: data => {\n const span = data._sentrySpan;\n // only end if error handler isn't going to\n if (!span || data.error) return;\n // Same slice: strip command name from args before passing to the response hook.\n runResponseHook(span, data.command, data.args.slice(1), data.result);\n span.end();\n },\n error: data => {\n const span = data._sentrySpan;\n if (!span) return;\n if (data.error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: data.error.message });\n }\n span.end();\n },\n });\n}\n\nfunction setupBatchChannel(): void {\n const channel = tracingChannel<BatchData>(CHANNEL_BATCH, data => {\n const operationName = data.batchMode === 'PIPELINE' ? 'PIPELINE' : 'MULTI';\n\n return startSpanManual(\n {\n name: operationName,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis',\n [ATTR_DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS,\n ...(data.batchSize != null ? { 'db.redis.batch_size': data.batchSize } : {}),\n ...(data.serverAddress != null ? { [ATTR_NET_PEER_NAME]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [ATTR_NET_PEER_PORT]: data.serverPort } : {}),\n },\n },\n span => span,\n ) as Span;\n });\n\n channel.subscribe({\n start: NOOP,\n asyncStart: NOOP,\n end: NOOP,\n asyncEnd: data => {\n // only end if the error handler isn't going to\n if (!data.error) data._sentrySpan?.end();\n },\n error: data => {\n const span = data._sentrySpan;\n if (!span) return;\n if (data.error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: data.error.message });\n }\n span.end();\n },\n });\n}\n\nfunction setupConnectChannel(): void {\n const channel = tracingChannel<ConnectData>(CHANNEL_CONNECT, data => {\n return startSpanManual(\n {\n name: 'redis-connect',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'db.redis.connect',\n [ATTR_DB_SYSTEM]: DB_SYSTEM_VALUE_REDIS,\n ...(data.serverAddress != null ? { [ATTR_NET_PEER_NAME]: data.serverAddress } : {}),\n ...(data.serverPort != null ? { [ATTR_NET_PEER_PORT]: data.serverPort } : {}),\n },\n },\n span => span,\n ) as Span;\n });\n\n channel.subscribe({\n start: NOOP,\n asyncStart: NOOP,\n end: NOOP,\n asyncEnd: data => {\n // only end if the error handler isn't going to\n if (!data.error) data._sentrySpan?.end();\n },\n error: data => {\n const span = data._sentrySpan;\n if (!span) return;\n if (data.error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: data.error.message });\n }\n span.end();\n },\n });\n}\n\nfunction runResponseHook(span: Span, command: string, args: Array<string | Buffer>, result: unknown): void {\n const hook = currentResponseHook;\n if (!hook) return;\n try {\n hook(span, command, args as unknown as Parameters<typeof hook>[2], result);\n } catch {\n // never let user hooks break instrumentation\n }\n}\n\nfunction safeSerialize(command: string, args: Array<string | Buffer>): string | undefined {\n try {\n return defaultDbStatementSerializer(command, args);\n } catch {\n return undefined;\n }\n}\n\n// Test-only helper.\nexport function _resetRedisDiagnosticChannelsForTesting(): void {\n subscribed = false;\n currentResponseHook = undefined;\n}\n\n// Suppress unused-import lint when only used in types.\nexport type { TracingChannelContextWithSpan };\n"],"names":["tracingChannel","startSpanManual","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","ATTR_DB_SYSTEM","DB_SYSTEM_VALUE_REDIS","ATTR_DB_STATEMENT","ATTR_NET_PEER_NAME","ATTR_NET_PEER_PORT","SPAN_STATUS_ERROR","defaultDbStatementSerializer"],"mappings":"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"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const api = require('@opentelemetry/api');
|
|
4
|
+
const core = require('@sentry/core');
|
|
5
|
+
const instrumentation = require('@opentelemetry/instrumentation');
|
|
6
|
+
const semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
7
|
+
const redisCommon = require('./redis-common.js');
|
|
8
|
+
const semconv = require('./semconv.js');
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Copyright The OpenTelemetry Authors
|
|
12
|
+
*
|
|
13
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
* you may not use this file except in compliance with the License.
|
|
15
|
+
* You may obtain a copy of the License at
|
|
16
|
+
*
|
|
17
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
*
|
|
19
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
* See the License for the specific language governing permissions and
|
|
23
|
+
* limitations under the License.
|
|
24
|
+
*
|
|
25
|
+
* NOTICE from the Sentry authors:
|
|
26
|
+
* - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/instrumentation-ioredis-v0.62.0/packages/instrumentation-ioredis
|
|
27
|
+
* - Upstream version: @opentelemetry/instrumentation-ioredis@0.62.0
|
|
28
|
+
* - Minor TypeScript adjustments for this repository's compiler settings
|
|
29
|
+
*/
|
|
30
|
+
/* eslint-disable -- vendored @opentelemetry/instrumentation-ioredis */
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
const PACKAGE_NAME = '@opentelemetry/instrumentation-ioredis';
|
|
34
|
+
const PACKAGE_VERSION = '0.62.0';
|
|
35
|
+
|
|
36
|
+
// ---- utils ----
|
|
37
|
+
|
|
38
|
+
function endSpan(span, err) {
|
|
39
|
+
if (err) {
|
|
40
|
+
span.recordException(err);
|
|
41
|
+
span.setStatus({
|
|
42
|
+
code: api.SpanStatusCode.ERROR,
|
|
43
|
+
message: err.message,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
span.end();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---- IORedisInstrumentation ----
|
|
50
|
+
|
|
51
|
+
const DEFAULT_CONFIG = {
|
|
52
|
+
requireParentSpan: true,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
class IORedisInstrumentation extends instrumentation.InstrumentationBase {
|
|
56
|
+
|
|
57
|
+
constructor(config = {}) {
|
|
58
|
+
super(PACKAGE_NAME, PACKAGE_VERSION, { ...DEFAULT_CONFIG, ...config });
|
|
59
|
+
this._setSemconvStabilityFromEnv();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_setSemconvStabilityFromEnv() {
|
|
63
|
+
this._netSemconvStability = instrumentation.semconvStabilityFromStr('http', process.env['OTEL_SEMCONV_STABILITY_OPT_IN']);
|
|
64
|
+
this._dbSemconvStability = instrumentation.semconvStabilityFromStr('database', process.env['OTEL_SEMCONV_STABILITY_OPT_IN']);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
setConfig(config = {}) {
|
|
68
|
+
super.setConfig({ ...DEFAULT_CONFIG, ...config });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
init() {
|
|
72
|
+
return [
|
|
73
|
+
new instrumentation.InstrumentationNodeModuleDefinition(
|
|
74
|
+
'ioredis',
|
|
75
|
+
['>=2.0.0 <6'],
|
|
76
|
+
(module, moduleVersion) => {
|
|
77
|
+
const moduleExports =
|
|
78
|
+
module[Symbol.toStringTag] === 'Module'
|
|
79
|
+
? module.default // ESM
|
|
80
|
+
: module; // CommonJS
|
|
81
|
+
if (instrumentation.isWrapped(moduleExports.prototype.sendCommand)) {
|
|
82
|
+
this._unwrap(moduleExports.prototype, 'sendCommand');
|
|
83
|
+
}
|
|
84
|
+
this._wrap(moduleExports.prototype, 'sendCommand', this._patchSendCommand(moduleVersion));
|
|
85
|
+
if (instrumentation.isWrapped(moduleExports.prototype.connect)) {
|
|
86
|
+
this._unwrap(moduleExports.prototype, 'connect');
|
|
87
|
+
}
|
|
88
|
+
this._wrap(moduleExports.prototype, 'connect', this._patchConnection());
|
|
89
|
+
return module;
|
|
90
|
+
},
|
|
91
|
+
(module) => {
|
|
92
|
+
if (module === undefined) return;
|
|
93
|
+
const moduleExports =
|
|
94
|
+
module[Symbol.toStringTag] === 'Module'
|
|
95
|
+
? module.default // ESM
|
|
96
|
+
: module; // CommonJS
|
|
97
|
+
this._unwrap(moduleExports.prototype, 'sendCommand');
|
|
98
|
+
this._unwrap(moduleExports.prototype, 'connect');
|
|
99
|
+
},
|
|
100
|
+
),
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
_patchSendCommand(moduleVersion) {
|
|
105
|
+
return (original) => {
|
|
106
|
+
return this._traceSendCommand(original, moduleVersion);
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
_patchConnection() {
|
|
111
|
+
return (original) => {
|
|
112
|
+
return this._traceConnection(original);
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
_traceSendCommand(original, moduleVersion) {
|
|
117
|
+
const instrumentation$1 = this;
|
|
118
|
+
return function ( cmd) {
|
|
119
|
+
if (arguments.length < 1 || typeof cmd !== 'object') {
|
|
120
|
+
return original.apply(this, arguments);
|
|
121
|
+
}
|
|
122
|
+
const config = instrumentation$1.getConfig();
|
|
123
|
+
const dbStatementSerializer = config.dbStatementSerializer || redisCommon.defaultDbStatementSerializer;
|
|
124
|
+
const hasNoParentSpan = api.trace.getSpan(api.context.active()) === undefined;
|
|
125
|
+
if (config.requireParentSpan === true && hasNoParentSpan) {
|
|
126
|
+
return original.apply(this, arguments);
|
|
127
|
+
}
|
|
128
|
+
const attributes = {};
|
|
129
|
+
const { host, port } = this.options;
|
|
130
|
+
const dbQueryText = dbStatementSerializer(cmd.name, cmd.args);
|
|
131
|
+
if (instrumentation$1._dbSemconvStability & instrumentation.SemconvStability.OLD) {
|
|
132
|
+
attributes[semconv.ATTR_DB_SYSTEM] = semconv.DB_SYSTEM_VALUE_REDIS;
|
|
133
|
+
attributes[semconv.ATTR_DB_STATEMENT] = dbQueryText;
|
|
134
|
+
attributes[semconv.ATTR_DB_CONNECTION_STRING] = `redis://${host}:${port}`;
|
|
135
|
+
}
|
|
136
|
+
if (instrumentation$1._dbSemconvStability & instrumentation.SemconvStability.STABLE) {
|
|
137
|
+
attributes[semanticConventions.ATTR_DB_SYSTEM_NAME] = semconv.DB_SYSTEM_NAME_VALUE_REDIS;
|
|
138
|
+
attributes[semanticConventions.ATTR_DB_QUERY_TEXT] = dbQueryText;
|
|
139
|
+
}
|
|
140
|
+
if (instrumentation$1._netSemconvStability & instrumentation.SemconvStability.OLD) {
|
|
141
|
+
attributes[semconv.ATTR_NET_PEER_NAME] = host;
|
|
142
|
+
attributes[semconv.ATTR_NET_PEER_PORT] = port;
|
|
143
|
+
}
|
|
144
|
+
if (instrumentation$1._netSemconvStability & instrumentation.SemconvStability.STABLE) {
|
|
145
|
+
attributes[semanticConventions.ATTR_SERVER_ADDRESS] = host;
|
|
146
|
+
attributes[semanticConventions.ATTR_SERVER_PORT] = port;
|
|
147
|
+
}
|
|
148
|
+
attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = 'auto.db.otel.redis';
|
|
149
|
+
const span = instrumentation$1.tracer.startSpan(cmd.name, {
|
|
150
|
+
kind: api.SpanKind.CLIENT,
|
|
151
|
+
attributes,
|
|
152
|
+
});
|
|
153
|
+
const { requestHook } = config;
|
|
154
|
+
if (requestHook) {
|
|
155
|
+
instrumentation.safeExecuteInTheMiddle(
|
|
156
|
+
() =>
|
|
157
|
+
requestHook(span, {
|
|
158
|
+
moduleVersion,
|
|
159
|
+
cmdName: cmd.name,
|
|
160
|
+
cmdArgs: cmd.args,
|
|
161
|
+
}),
|
|
162
|
+
(e) => {
|
|
163
|
+
if (e) {
|
|
164
|
+
api.diag.error('ioredis instrumentation: request hook failed', e);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
true,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const result = original.apply(this, arguments);
|
|
172
|
+
const origResolve = cmd.resolve;
|
|
173
|
+
cmd.resolve = function (result) {
|
|
174
|
+
instrumentation.safeExecuteInTheMiddle(
|
|
175
|
+
() => config.responseHook?.(span, cmd.name, cmd.args, result),
|
|
176
|
+
(e) => {
|
|
177
|
+
if (e) {
|
|
178
|
+
api.diag.error('ioredis instrumentation: response hook failed', e);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
true,
|
|
182
|
+
);
|
|
183
|
+
endSpan(span, null);
|
|
184
|
+
origResolve(result);
|
|
185
|
+
};
|
|
186
|
+
const origReject = cmd.reject;
|
|
187
|
+
cmd.reject = function (err) {
|
|
188
|
+
endSpan(span, err);
|
|
189
|
+
origReject(err);
|
|
190
|
+
};
|
|
191
|
+
return result;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
endSpan(span, error );
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
_traceConnection(original) {
|
|
200
|
+
const instrumentation$1 = this;
|
|
201
|
+
return function () {
|
|
202
|
+
const hasNoParentSpan = api.trace.getSpan(api.context.active()) === undefined;
|
|
203
|
+
if (instrumentation$1.getConfig().requireParentSpan === true && hasNoParentSpan) {
|
|
204
|
+
return original.apply(this, arguments);
|
|
205
|
+
}
|
|
206
|
+
const attributes = {};
|
|
207
|
+
const { host, port } = this.options;
|
|
208
|
+
if (instrumentation$1._dbSemconvStability & instrumentation.SemconvStability.OLD) {
|
|
209
|
+
attributes[semconv.ATTR_DB_SYSTEM] = semconv.DB_SYSTEM_VALUE_REDIS;
|
|
210
|
+
attributes[semconv.ATTR_DB_STATEMENT] = 'connect';
|
|
211
|
+
attributes[semconv.ATTR_DB_CONNECTION_STRING] = `redis://${host}:${port}`;
|
|
212
|
+
}
|
|
213
|
+
if (instrumentation$1._dbSemconvStability & instrumentation.SemconvStability.STABLE) {
|
|
214
|
+
attributes[semanticConventions.ATTR_DB_SYSTEM_NAME] = semconv.DB_SYSTEM_NAME_VALUE_REDIS;
|
|
215
|
+
attributes[semanticConventions.ATTR_DB_QUERY_TEXT] = 'connect';
|
|
216
|
+
}
|
|
217
|
+
if (instrumentation$1._netSemconvStability & instrumentation.SemconvStability.OLD) {
|
|
218
|
+
attributes[semconv.ATTR_NET_PEER_NAME] = host;
|
|
219
|
+
attributes[semconv.ATTR_NET_PEER_PORT] = port;
|
|
220
|
+
}
|
|
221
|
+
if (instrumentation$1._netSemconvStability & instrumentation.SemconvStability.STABLE) {
|
|
222
|
+
attributes[semanticConventions.ATTR_SERVER_ADDRESS] = host;
|
|
223
|
+
attributes[semanticConventions.ATTR_SERVER_PORT] = port;
|
|
224
|
+
}
|
|
225
|
+
attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = 'auto.db.otel.redis';
|
|
226
|
+
const span = instrumentation$1.tracer.startSpan('connect', {
|
|
227
|
+
kind: api.SpanKind.CLIENT,
|
|
228
|
+
attributes,
|
|
229
|
+
});
|
|
230
|
+
try {
|
|
231
|
+
const result = original.apply(this, arguments);
|
|
232
|
+
if (typeof result?.then === 'function') {
|
|
233
|
+
return result.then(
|
|
234
|
+
(value) => {
|
|
235
|
+
endSpan(span, null);
|
|
236
|
+
return value;
|
|
237
|
+
},
|
|
238
|
+
(error) => {
|
|
239
|
+
endSpan(span, error);
|
|
240
|
+
return Promise.reject(error);
|
|
241
|
+
},
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
endSpan(span, null);
|
|
245
|
+
return result;
|
|
246
|
+
} catch (error) {
|
|
247
|
+
endSpan(span, error );
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
exports.IORedisInstrumentation = IORedisInstrumentation;
|
|
255
|
+
//# sourceMappingURL=ioredis-instrumentation.js.map
|