diff --git a/packages/cloudflare/src/opentelemetry/tracer.ts b/packages/cloudflare/src/opentelemetry/tracer.ts index a180346f7cce..bb83a8550588 100644 --- a/packages/cloudflare/src/opentelemetry/tracer.ts +++ b/packages/cloudflare/src/opentelemetry/tracer.ts @@ -27,8 +27,8 @@ class SentryCloudflareTraceProvider implements TracerProvider { class SentryCloudflareTracer implements Tracer { public startSpan(name: string, options?: SpanOptions): Span { return startInactiveSpan({ - name, ...options, + name, attributes: { ...options?.attributes, 'sentry.cloudflare_tracer': true, @@ -56,8 +56,8 @@ class SentryCloudflareTracer implements Tracer { const opts = (typeof options === 'object' && options !== null ? options : {}) as SpanOptions; const spanOpts = { - name, ...opts, + name, attributes: { ...opts.attributes, 'sentry.cloudflare_tracer': true, diff --git a/packages/cloudflare/test/opentelemetry.test.ts b/packages/cloudflare/test/opentelemetry.test.ts index f918afff90cc..d7c28ca424cd 100644 --- a/packages/cloudflare/test/opentelemetry.test.ts +++ b/packages/cloudflare/test/opentelemetry.test.ts @@ -142,4 +142,59 @@ describe('opentelemetry compatibility', () => { }), ]); }); + + test('name parameter should take precedence over options.name in startSpan', async () => { + const transactionEvents: TransactionEvent[] = []; + + const client = init({ + dsn: 'https://username@domain/123', + tracesSampleRate: 1, + beforeSendTransaction: event => { + transactionEvents.push(event); + return null; + }, + }); + + const tracer = trace.getTracer('test'); + + // Pass options with a different name property - the first parameter should take precedence + // This is important for integrations like Prisma that add prefixes to span names + const span = tracer.startSpan('prisma:client:operation', { name: 'operation' } as any); + span.end(); + + await client!.flush(); + + expect(transactionEvents).toHaveLength(1); + const [transactionEvent] = transactionEvents; + + expect(transactionEvent?.transaction).toBe('prisma:client:operation'); + }); + + test('name parameter should take precedence over options.name in startActiveSpan', async () => { + const transactionEvents: TransactionEvent[] = []; + + const client = init({ + dsn: 'https://username@domain/123', + tracesSampleRate: 1, + beforeSendTransaction: event => { + transactionEvents.push(event); + return null; + }, + }); + + const tracer = trace.getTracer('test'); + + // Pass options with a different name property - the first parameter should take precedence + // This is important for integrations like Prisma that add prefixes to span names + tracer.startActiveSpan('prisma:client:operation', { name: 'operation' } as any, span => { + span.end(); + }); + + await client!.flush(); + + expect(transactionEvents).toHaveLength(1); + const [transactionEvent] = transactionEvents; + + expect(transactionEvent?.transaction).toBe('prisma:client:operation'); + }); }); diff --git a/packages/deno/src/opentelemetry/tracer.ts b/packages/deno/src/opentelemetry/tracer.ts index 801badefa19f..3176616bc04c 100644 --- a/packages/deno/src/opentelemetry/tracer.ts +++ b/packages/deno/src/opentelemetry/tracer.ts @@ -35,8 +35,8 @@ class SentryDenoTracer implements Tracer { const op = this._mapSpanKindToOp(options?.kind); return startInactiveSpan({ - name, ...options, + name, attributes: { ...options?.attributes, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', @@ -69,8 +69,8 @@ class SentryDenoTracer implements Tracer { const op = this._mapSpanKindToOp(opts.kind); const spanOpts = { - name, ...opts, + name, attributes: { ...opts.attributes, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'manual', diff --git a/packages/deno/test/opentelemetry.test.ts b/packages/deno/test/opentelemetry.test.ts index 2c37ddc48843..30723e033dd4 100644 --- a/packages/deno/test/opentelemetry.test.ts +++ b/packages/deno/test/opentelemetry.test.ts @@ -178,6 +178,66 @@ Deno.test('should be compatible with native Deno OpenTelemetry', async () => { await client.flush(); }); +// Test that name parameter takes precedence over options.name for both startSpan and startActiveSpan +Deno.test('name parameter should take precedence over options.name in startSpan', async () => { + resetSdk(); + const transactionEvents: any[] = []; + + const client = init({ + dsn: 'https://username@domain/123', + tracesSampleRate: 1, + beforeSendTransaction: event => { + transactionEvents.push(event); + return null; + }, + }) as DenoClient; + + const tracer = trace.getTracer('test'); + + // Pass options with a different name property - the first parameter should take precedence + // This is important for integrations like Prisma that add prefixes to span names + const span = tracer.startSpan('prisma:client:operation', { name: 'operation' } as any); + span.end(); + + await client.flush(); + + assertEquals(transactionEvents.length, 1); + const [transactionEvent] = transactionEvents; + + // The span name should be 'prisma:client:operation', not 'operation' + assertEquals(transactionEvent?.transaction, 'prisma:client:operation'); +}); + +Deno.test('name parameter should take precedence over options.name in startActiveSpan', async () => { + resetSdk(); + const transactionEvents: any[] = []; + + const client = init({ + dsn: 'https://username@domain/123', + tracesSampleRate: 1, + beforeSendTransaction: event => { + transactionEvents.push(event); + return null; + }, + }) as DenoClient; + + const tracer = trace.getTracer('test'); + + // Pass options with a different name property - the first parameter should take precedence + // This is important for integrations like Prisma that add prefixes to span names + tracer.startActiveSpan('prisma:client:operation', { name: 'operation' } as any, span => { + span.end(); + }); + + await client.flush(); + + assertEquals(transactionEvents.length, 1); + const [transactionEvent] = transactionEvents; + + // The span name should be 'prisma:client:operation', not 'operation' + assertEquals(transactionEvent?.transaction, 'prisma:client:operation'); +}); + Deno.test('should verify native Deno OpenTelemetry works when enabled', async () => { resetSdk();