From 0942036786651852723349a2d6292689ed949ebf Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Mon, 27 Jul 2026 15:42:38 +0200 Subject: [PATCH 1/2] feat(angular)!: Use `ui.mount` and `function` span ops for tracing decorators `TraceDirective`/`TraceClass` emit `ui.mount`; `TraceMethod` emits `function` with the method name in `code.function.name`. The method-key suffix was user-controlled and unbounded. Ref: JS-3105 Co-Authored-By: Claude Opus 5 (1M context) --- .../angular-17/tests/performance.test.ts | 30 +++++++++++-------- .../angular-18/tests/performance.test.ts | 26 +++++++++------- .../angular-19/tests/performance.test.ts | 30 +++++++++++-------- .../angular-20/tests/performance.test.ts | 30 +++++++++++-------- .../angular-21/tests/performance.test.ts | 30 +++++++++++-------- .../angular-22/tests/performance.test.ts | 30 +++++++++++-------- packages/angular/src/constants.ts | 5 ++-- packages/angular/src/tracing.ts | 8 +++-- 8 files changed, 113 insertions(+), 76 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/angular-17/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/angular-17/tests/performance.test.ts index e2238d42a17a..6644bbada6c1 100644 --- a/dev-packages/e2e-tests/test-applications/angular-17/tests/performance.test.ts +++ b/dev-packages/e2e-tests/test-applications/angular-17/tests/performance.test.ts @@ -261,22 +261,22 @@ test.describe('TraceDirective', () => { expect.arrayContaining([ expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', // custom component name passed to trace directive - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), }), expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', // fallback selector name - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -305,11 +305,11 @@ test.describe('TraceClass Decorator', () => { expect(classDecoratorSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_class_decorator', }, description: '', - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_class_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -329,17 +329,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngOnInit'); + const ngInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngOnInit', + ); expect(ngInitSpan).toBeDefined(); expect(ngInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngOnInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngOnInit', }, description: '', - op: 'ui.angular.ngOnInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -357,17 +360,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngAfterViewInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngAfterViewInit'); + const ngAfterViewInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngAfterViewInit', + ); expect(ngAfterViewInitSpan).toBeDefined(); expect(ngAfterViewInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngAfterViewInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngAfterViewInit', }, description: '', - op: 'ui.angular.ngAfterViewInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), diff --git a/dev-packages/e2e-tests/test-applications/angular-18/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/angular-18/tests/performance.test.ts index 66146b4b49ae..34d1f6e9bf31 100644 --- a/dev-packages/e2e-tests/test-applications/angular-18/tests/performance.test.ts +++ b/dev-packages/e2e-tests/test-applications/angular-18/tests/performance.test.ts @@ -260,11 +260,11 @@ test.describe('TraceDirective', () => { expect(traceDirectiveSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -292,11 +292,11 @@ test.describe('TraceClass Decorator', () => { expect(classDecoratorSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_class_decorator', }, description: '', - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_class_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -316,17 +316,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngOnInit'); + const ngInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngOnInit', + ); expect(ngInitSpan).toBeDefined(); expect(ngInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngOnInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngOnInit', }, description: '', - op: 'ui.angular.ngOnInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -344,17 +347,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngAfterViewInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngAfterViewInit'); + const ngAfterViewInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngAfterViewInit', + ); expect(ngAfterViewInitSpan).toBeDefined(); expect(ngAfterViewInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngAfterViewInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngAfterViewInit', }, description: '', - op: 'ui.angular.ngAfterViewInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), diff --git a/dev-packages/e2e-tests/test-applications/angular-19/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/angular-19/tests/performance.test.ts index 826959def7b2..d3ed7c0c0505 100644 --- a/dev-packages/e2e-tests/test-applications/angular-19/tests/performance.test.ts +++ b/dev-packages/e2e-tests/test-applications/angular-19/tests/performance.test.ts @@ -261,22 +261,22 @@ test.describe('TraceDirective', () => { expect.arrayContaining([ expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', // custom component name passed to trace directive - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), }), expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', // fallback selector name - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -305,11 +305,11 @@ test.describe('TraceClass Decorator', () => { expect(classDecoratorSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_class_decorator', }, description: '', - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_class_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -329,17 +329,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngOnInit'); + const ngInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngOnInit', + ); expect(ngInitSpan).toBeDefined(); expect(ngInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngOnInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngOnInit', }, description: '', - op: 'ui.angular.ngOnInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -357,17 +360,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngAfterViewInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngAfterViewInit'); + const ngAfterViewInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngAfterViewInit', + ); expect(ngAfterViewInitSpan).toBeDefined(); expect(ngAfterViewInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngAfterViewInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngAfterViewInit', }, description: '', - op: 'ui.angular.ngAfterViewInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), diff --git a/dev-packages/e2e-tests/test-applications/angular-20/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/angular-20/tests/performance.test.ts index 18dbd47eb540..6f674172b9a0 100644 --- a/dev-packages/e2e-tests/test-applications/angular-20/tests/performance.test.ts +++ b/dev-packages/e2e-tests/test-applications/angular-20/tests/performance.test.ts @@ -261,22 +261,22 @@ test.describe('TraceDirective', () => { expect.arrayContaining([ expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', // custom component name passed to trace directive - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), }), expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', // fallback selector name - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -305,11 +305,11 @@ test.describe('TraceClass Decorator', () => { expect(classDecoratorSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_class_decorator', }, description: '', - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_class_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -329,17 +329,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngOnInit'); + const ngInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngOnInit', + ); expect(ngInitSpan).toBeDefined(); expect(ngInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngOnInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngOnInit', }, description: '', - op: 'ui.angular.ngOnInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -357,17 +360,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngAfterViewInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngAfterViewInit'); + const ngAfterViewInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngAfterViewInit', + ); expect(ngAfterViewInitSpan).toBeDefined(); expect(ngAfterViewInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngAfterViewInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngAfterViewInit', }, description: '', - op: 'ui.angular.ngAfterViewInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), diff --git a/dev-packages/e2e-tests/test-applications/angular-21/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/angular-21/tests/performance.test.ts index d2617be9c653..8345acb204b3 100644 --- a/dev-packages/e2e-tests/test-applications/angular-21/tests/performance.test.ts +++ b/dev-packages/e2e-tests/test-applications/angular-21/tests/performance.test.ts @@ -261,22 +261,22 @@ test.describe('TraceDirective', () => { expect.arrayContaining([ expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', // custom component name passed to trace directive - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), }), expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', // fallback selector name - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -305,11 +305,11 @@ test.describe('TraceClass Decorator', () => { expect(classDecoratorSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_class_decorator', }, description: '', - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_class_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -329,17 +329,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngOnInit'); + const ngInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngOnInit', + ); expect(ngInitSpan).toBeDefined(); expect(ngInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngOnInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngOnInit', }, description: '', - op: 'ui.angular.ngOnInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -357,17 +360,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngAfterViewInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngAfterViewInit'); + const ngAfterViewInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngAfterViewInit', + ); expect(ngAfterViewInitSpan).toBeDefined(); expect(ngAfterViewInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngAfterViewInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngAfterViewInit', }, description: '', - op: 'ui.angular.ngAfterViewInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), diff --git a/dev-packages/e2e-tests/test-applications/angular-22/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/angular-22/tests/performance.test.ts index 4eca907379e4..895db1db2cee 100644 --- a/dev-packages/e2e-tests/test-applications/angular-22/tests/performance.test.ts +++ b/dev-packages/e2e-tests/test-applications/angular-22/tests/performance.test.ts @@ -261,22 +261,22 @@ test.describe('TraceDirective', () => { expect.arrayContaining([ expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', // custom component name passed to trace directive - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), }), expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', }, description: '', // fallback selector name - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_directive', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -305,11 +305,11 @@ test.describe('TraceClass Decorator', () => { expect(classDecoratorSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.init', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.mount', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_class_decorator', }, description: '', - op: 'ui.angular.init', + op: 'ui.mount', origin: 'auto.ui.angular.trace_class_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -329,17 +329,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngOnInit'); + const ngInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngOnInit', + ); expect(ngInitSpan).toBeDefined(); expect(ngInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngOnInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngOnInit', }, description: '', - op: 'ui.angular.ngOnInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -357,17 +360,20 @@ test.describe('TraceMethod Decorator', () => { // immediately navigate to a different route const [_, navigationTxn] = await Promise.all([page.locator('#componentTracking').click(), navigationTxnPromise]); - const ngAfterViewInitSpan = navigationTxn.spans?.find(span => span.op === 'ui.angular.ngAfterViewInit'); + const ngAfterViewInitSpan = navigationTxn.spans?.find( + span => span.op === 'function' && span.data?.['code.function.name'] === 'ngAfterViewInit', + ); expect(ngAfterViewInitSpan).toBeDefined(); expect(ngAfterViewInitSpan).toEqual( expect.objectContaining({ data: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.angular.ngAfterViewInit', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + 'code.function.name': 'ngAfterViewInit', }, description: '', - op: 'ui.angular.ngAfterViewInit', + op: 'function', origin: 'auto.ui.angular.trace_method_decorator', start_timestamp: expect.any(Number), timestamp: expect.any(Number), diff --git a/packages/angular/src/constants.ts b/packages/angular/src/constants.ts index a0786e7b516c..8a7ed50fa80c 100644 --- a/packages/angular/src/constants.ts +++ b/packages/angular/src/constants.ts @@ -1,5 +1,4 @@ export const ANGULAR_ROUTING_OP = 'ui.angular.routing'; -export const ANGULAR_INIT_OP = 'ui.angular.init'; - -export const ANGULAR_OP = 'ui.angular'; +// TODO(v11): Replace with the `ui.mount` constant from `@sentry/conventions/op` once it is registered there. +export const ANGULAR_INIT_OP = 'ui.mount'; diff --git a/packages/angular/src/tracing.ts b/packages/angular/src/tracing.ts index b6af07fd03d0..f55cdbf5141f 100644 --- a/packages/angular/src/tracing.ts +++ b/packages/angular/src/tracing.ts @@ -26,10 +26,11 @@ import { debug, parseStringToURLObject, stripUrlQueryAndFragment, timestampInSec import type { Observable } from 'rxjs'; import { Subscription } from 'rxjs'; import { filter, tap } from 'rxjs/operators'; -import { ANGULAR_INIT_OP, ANGULAR_OP, ANGULAR_ROUTING_OP } from './constants'; +import { ANGULAR_INIT_OP, ANGULAR_ROUTING_OP } from './constants'; import { IS_DEBUG_BUILD } from './flags'; import { runOutsideAngular } from './zone'; -import { URL_FULL, URL_PATH, URL_TEMPLATE } from '@sentry/conventions/attributes'; +import { CODE_FUNCTION_NAME, URL_FULL, URL_PATH, URL_TEMPLATE } from '@sentry/conventions/attributes'; +import { GENERAL_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; let instrumentationInitialized: boolean; @@ -387,10 +388,11 @@ export function TraceMethod(options?: TraceMethodOptions): MethodDecorator { startInactiveSpan({ onlyIfParent: true, name: `<${options?.name ? options.name : 'unnamed'}>`, - op: `${ANGULAR_OP}.${String(propertyKey)}`, + op: GENERAL_FUNCTION_SPAN_OP, startTime: now, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', + [CODE_FUNCTION_NAME]: String(propertyKey), }, }).end(now); }); From 9643fa29369fb64e1f94b3ebb00ad27fbf2b963c Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Tue, 28 Jul 2026 15:45:25 +0200 Subject: [PATCH 2/2] use sentry.op --- packages/angular/src/tracing.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/angular/src/tracing.ts b/packages/angular/src/tracing.ts index f55cdbf5141f..f59474b776e6 100644 --- a/packages/angular/src/tracing.ts +++ b/packages/angular/src/tracing.ts @@ -21,6 +21,8 @@ import { startInactiveSpan, getAbsoluteUrl, } from '@sentry/browser'; +import { CODE_FUNCTION_NAME, SENTRY_OP, URL_FULL, URL_PATH, URL_TEMPLATE } from '@sentry/conventions/attributes'; +import { GENERAL_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; import type { Integration, Span } from '@sentry/core'; import { debug, parseStringToURLObject, stripUrlQueryAndFragment, timestampInSeconds } from '@sentry/core'; import type { Observable } from 'rxjs'; @@ -29,8 +31,6 @@ import { filter, tap } from 'rxjs/operators'; import { ANGULAR_INIT_OP, ANGULAR_ROUTING_OP } from './constants'; import { IS_DEBUG_BUILD } from './flags'; import { runOutsideAngular } from './zone'; -import { CODE_FUNCTION_NAME, URL_FULL, URL_PATH, URL_TEMPLATE } from '@sentry/conventions/attributes'; -import { GENERAL_FUNCTION_SPAN_OP } from '@sentry/conventions/op'; let instrumentationInitialized: boolean; @@ -294,8 +294,10 @@ export class TraceDirective implements OnInit, AfterViewInit { this._tracingSpan = runOutsideAngular(() => startInactiveSpan({ name: `<${this.componentName}>`, - op: ANGULAR_INIT_OP, - attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive' }, + attributes: { + [SENTRY_OP]: ANGULAR_INIT_OP, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_directive', + }, }), ); } @@ -343,8 +345,8 @@ export function TraceClass(options?: TraceClassOptions): ClassDecorator { startInactiveSpan({ onlyIfParent: true, name: `<${options?.name || 'unnamed'}>`, - op: ANGULAR_INIT_OP, attributes: { + [SENTRY_OP]: ANGULAR_INIT_OP, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_class_decorator', }, }), @@ -388,9 +390,9 @@ export function TraceMethod(options?: TraceMethodOptions): MethodDecorator { startInactiveSpan({ onlyIfParent: true, name: `<${options?.name ? options.name : 'unnamed'}>`, - op: GENERAL_FUNCTION_SPAN_OP, startTime: now, attributes: { + [SENTRY_OP]: GENERAL_FUNCTION_SPAN_OP, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.angular.trace_method_decorator', [CODE_FUNCTION_NAME]: String(propertyKey), },