diff --git a/dev-packages/e2e-tests/test-applications/effect-3-browser/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/effect-3-browser/tests/transactions.test.ts index 331cb8b599e9..b2dfd2de9b54 100644 --- a/dev-packages/e2e-tests/test-applications/effect-3-browser/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/effect-3-browser/tests/transactions.test.ts @@ -75,4 +75,6 @@ test('captures Effect spans with correct parent-child structure', async ({ page expect(parentSpan).toBeDefined(); expect(nestedSpan).toBeDefined(); expect(nestedSpan?.parent_span_id).toBe(parentSpan?.span_id); + expect(parentSpan?.attributes['code.function.name']?.value).toBe('custom-effect-span'); + expect(nestedSpan?.attributes['code.function.name']?.value).toBe('nested-span'); }); diff --git a/dev-packages/e2e-tests/test-applications/effect-3-node/tests/spans.test.ts b/dev-packages/e2e-tests/test-applications/effect-3-node/tests/spans.test.ts index 3b04189aea2f..6731b12f2ecf 100644 --- a/dev-packages/e2e-tests/test-applications/effect-3-node/tests/spans.test.ts +++ b/dev-packages/e2e-tests/test-applications/effect-3-node/tests/spans.test.ts @@ -52,6 +52,8 @@ test('Sends Effect spans with correct parent-child structure', async ({ baseURL expect(segment.name).toBe('http.server GET'); expect(segment.attributes['sentry.origin']?.value).toBe('auto.http.effect'); expect(segment.attributes['sentry.sdk.name']?.value).toBe('sentry.javascript.effect'); + // `http.server` spans carry the route in their name, so they get no `code.function.name`. + expect(segment.attributes['code.function.name']).toBeUndefined(); expect(children).toHaveLength(2); const parent = children.find(span => span.name === 'custom-effect-span')!; const nested = children.find(span => span.name === 'nested-span')!; @@ -60,6 +62,7 @@ test('Sends Effect spans with correct parent-child structure', async ({ baseURL for (const child of children) { expect(getSpanOp(child)).toBe('function'); expect(child.attributes['sentry.origin']?.value).toBe('auto.function.effect'); + expect(child.attributes['code.function.name']?.value).toBe(child.name); expect(child.trace_id).toBe(segment.trace_id); } }); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/transactions.test.ts index 971f5678c276..1d0335798d1c 100644 --- a/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/effect-4-browser/tests/transactions.test.ts @@ -75,4 +75,6 @@ test('captures Effect spans with correct parent-child structure', async ({ page expect(parentSpan).toBeDefined(); expect(nestedSpan).toBeDefined(); expect(nestedSpan?.parent_span_id).toBe(parentSpan?.span_id); + expect(parentSpan?.attributes['code.function.name']?.value).toBe('custom-effect-span'); + expect(nestedSpan?.attributes['code.function.name']?.value).toBe('nested-span'); }); diff --git a/dev-packages/e2e-tests/test-applications/effect-4-node/tests/spans.test.ts b/dev-packages/e2e-tests/test-applications/effect-4-node/tests/spans.test.ts index 7477d91a04be..f1b1ce5ae36e 100644 --- a/dev-packages/e2e-tests/test-applications/effect-4-node/tests/spans.test.ts +++ b/dev-packages/e2e-tests/test-applications/effect-4-node/tests/spans.test.ts @@ -52,6 +52,8 @@ test('Sends Effect spans with correct parent-child structure', async ({ baseURL expect(segment.name).toBe('http.server GET'); expect(segment.attributes['sentry.origin']?.value).toBe('auto.http.effect'); expect(segment.attributes['sentry.sdk.name']?.value).toBe('sentry.javascript.effect'); + // `http.server` spans carry the route in their name, so they get no `code.function.name`. + expect(segment.attributes['code.function.name']).toBeUndefined(); expect(children).toHaveLength(2); const parent = children.find(span => span.name === 'custom-effect-span')!; const nested = children.find(span => span.name === 'nested-span')!; @@ -60,6 +62,7 @@ test('Sends Effect spans with correct parent-child structure', async ({ baseURL for (const child of children) { expect(getSpanOp(child)).toBe('function'); expect(child.attributes['sentry.origin']?.value).toBe('auto.function.effect'); + expect(child.attributes['code.function.name']?.value).toBe(child.name); expect(child.trace_id).toBe(segment.trace_id); } }); diff --git a/packages/effect/src/tracer.ts b/packages/effect/src/tracer.ts index 31ac0be35c29..cc65c44d9101 100644 --- a/packages/effect/src/tracer.ts +++ b/packages/effect/src/tracer.ts @@ -1,4 +1,4 @@ -import { SENTRY_OP } from '@sentry/conventions/attributes'; +import { CODE_FUNCTION_NAME, SENTRY_OP } from '@sentry/conventions/attributes'; import { FUNCTION, HTTP_CLIENT, HTTP_SERVER } from '@sentry/conventions/op'; import type { Span, StartSpanOptions } from '@sentry/core'; import { isObjectLike, getActiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, withActiveSpan } from '@sentry/core'; @@ -190,12 +190,15 @@ function createSentrySpan( const parentSentrySpan = Option.isSome(parent) && isSentrySpan(parent.value) ? parent.value.sentrySpan : (getActiveSpan() ?? null); + const op = deriveOp(name); + const newSpan = startInactiveSpan({ name, startTime: nanosToHrTime(startTime), attributes: { - [SENTRY_OP]: deriveOp(name), + [SENTRY_OP]: op, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: deriveOrigin(name), + ...(op === FUNCTION && { [CODE_FUNCTION_NAME]: name }), }, ...(parentSentrySpan ? { parentSpan: parentSentrySpan } : {}), }); diff --git a/packages/effect/test/tracer.test.ts b/packages/effect/test/tracer.test.ts index a056227576e1..91aae81ca918 100644 --- a/packages/effect/test/tracer.test.ts +++ b/packages/effect/test/tracer.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from '@effect/vitest'; import * as sentryCore from '@sentry/core'; import * as sentryCoreBrowser from '@sentry/core/browser'; +import { CODE_FUNCTION_NAME } from '@sentry/conventions/attributes'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; import { Effect } from 'effect'; import { afterEach, vi } from 'vitest'; @@ -198,6 +199,7 @@ describe.each(VARIANTS)('SentryEffectTracer ($variant)', ({ tracer, spanApi }) = expect(attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]).toBe('auto.function.effect'); expect(attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_OP]).toBe('function'); + expect(attributes?.[CODE_FUNCTION_NAME]).toBe('my-operation'); }), ); @@ -207,6 +209,7 @@ describe.each(VARIANTS)('SentryEffectTracer ($variant)', ({ tracer, spanApi }) = expect(attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]).toBe('auto.http.effect'); expect(attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_OP]).toBe('http.server'); + expect(attributes?.[CODE_FUNCTION_NAME]).toBeUndefined(); }), ); @@ -216,6 +219,7 @@ describe.each(VARIANTS)('SentryEffectTracer ($variant)', ({ tracer, spanApi }) = expect(attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]).toBe('auto.http.effect'); expect(attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_OP]).toBe('http.client'); + expect(attributes?.[CODE_FUNCTION_NAME]).toBeUndefined(); }), );