Skip to content

feat(core): Make custom tracing methods return spans & set default op #10633

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/angular/src/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ export class TraceService implements OnDestroy {
if (!getActiveSpan()) {
startBrowserTracingNavigationSpan(client, {
name: strippedUrl,
op: 'navigation',
origin: 'auto.navigation.angular',
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
Expand Down
17 changes: 9 additions & 8 deletions packages/ember/addon/instance-initializers/sentry-performance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { ExtendedBackburner } from '@sentry/ember/runloop';
import type { Span } from '@sentry/types';
import { GLOBAL_OBJ, browserPerformanceTimeOrigin, timestampInSeconds } from '@sentry/utils';

import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
import type { BrowserClient } from '..';
import { getActiveSpan, startInactiveSpan } from '..';
import type { EmberRouterMain, EmberSentryConfig, GlobalConfig, OwnConfig } from '../types';
Expand Down Expand Up @@ -111,17 +111,18 @@ export function _instrumentEmberRouter(

if (url && browserTracingOptions.instrumentPageLoad !== false) {
const routeInfo = routerService.recognize(url);
Sentry.startBrowserTracingPageLoadSpan(client, {
activeRootSpan = Sentry.startBrowserTracingPageLoadSpan(client, {
name: `route:${routeInfo.name}`,
op: 'pageload',
origin: 'auto.pageload.ember',
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
},
tags: {
url,
toRoute: routeInfo.name,
'routing.instrumentation': '@sentry/ember',
},
});
activeRootSpan = getActiveSpan();
}

const finishActiveTransaction = (_: unknown, nextInstance: unknown): void => {
Expand All @@ -140,19 +141,19 @@ export function _instrumentEmberRouter(
const { fromRoute, toRoute } = getTransitionInformation(transition, routerService);
activeRootSpan?.end();

Sentry.startBrowserTracingNavigationSpan(client, {
activeRootSpan = Sentry.startBrowserTracingNavigationSpan(client, {
name: `route:${toRoute}`,
op: 'navigation',
origin: 'auto.navigation.ember',
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
},
tags: {
fromRoute,
toRoute,
'routing.instrumentation': '@sentry/ember',
},
});

activeRootSpan = getActiveSpan();

transitionSpan = startInactiveSpan({
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.ember',
Expand Down
28 changes: 20 additions & 8 deletions packages/tracing-internal/src/browser/browserTracingIntegration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable max-lines, complexity */
/* eslint-disable max-lines */
import type { IdleTransaction } from '@sentry/core';
import { getActiveSpan } from '@sentry/core';
import { getCurrentHub } from '@sentry/core';
import {
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
Expand Down Expand Up @@ -237,7 +238,6 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
latestRouteName = finalContext.name;
latestRouteSource = getSource(finalContext);

// eslint-disable-next-line deprecation/deprecation
if (finalContext.sampled === false) {
DEBUG_BUILD && logger.log(`[Tracing] Will not send ${finalContext.op} transaction because of beforeNavigate.`);
}
Expand Down Expand Up @@ -315,7 +315,10 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
// If there's an open transaction on the scope, we need to finish it before creating an new one.
activeSpan.end();
}
activeSpan = _createRouteTransaction(context);
activeSpan = _createRouteTransaction({
op: 'navigation',
...context,
});
});

client.on('startPageLoadSpan', (context: StartSpanOptions) => {
Expand All @@ -324,15 +327,17 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
// If there's an open transaction on the scope, we need to finish it before creating an new one.
activeSpan.end();
}
activeSpan = _createRouteTransaction(context);
activeSpan = _createRouteTransaction({
op: 'pageload',
...context,
});
});

if (options.instrumentPageLoad) {
const context: StartSpanOptions = {
name: WINDOW.location.pathname,
// pageload should always start at timeOrigin (and needs to be in s, not ms)
startTimestamp: browserPerformanceTimeOrigin ? browserPerformanceTimeOrigin / 1000 : undefined,
op: 'pageload',
origin: 'auto.pageload.browser',
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
Expand Down Expand Up @@ -361,7 +366,6 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
startingUrl = undefined;
const context: StartSpanOptions = {
name: WINDOW.location.pathname,
op: 'navigation',
origin: 'auto.navigation.browser',
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
Expand Down Expand Up @@ -399,16 +403,24 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
* Manually start a page load span.
* This will only do something if the BrowserTracing integration has been setup.
*/
export function startBrowserTracingPageLoadSpan(client: Client, spanOptions: StartSpanOptions): void {
export function startBrowserTracingPageLoadSpan(client: Client, spanOptions: StartSpanOptions): Span | undefined {
client.emit('startPageLoadSpan', spanOptions);

const span = getActiveSpan();
const op = span && spanToJSON(span).op;
return op === 'pageload' ? span : undefined;
}

/**
* Manually start a navigation span.
* This will only do something if the BrowserTracing integration has been setup.
*/
export function startBrowserTracingNavigationSpan(client: Client, spanOptions: StartSpanOptions): void {
export function startBrowserTracingNavigationSpan(client: Client, spanOptions: StartSpanOptions): Span | undefined {
client.emit('startNavigationSpan', spanOptions);

const span = getActiveSpan();
const op = span && spanToJSON(span).op;
return op === 'navigation' ? span : undefined;
Comment on lines 419 to +423
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we maybe have the emit hook return the span? Otherwise, we start racing again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so hooks so far have been designed with having no return value, always - which sometimes is a bit of a restriction but keeps the API very clean and slim. IMHO this should be very safe, we are in browser and there is only a single thread, there isn't really a way this could not work as far as I see, and type-wise we return Span | undefined anyhow so you have to write this defensively anyhow...?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note that we can still change this later, as this is completely internal now - so if we figure this is not accurate enough, we can change how this works transparently :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if this is clean and slim 🤔 Fine for now but to me this is very smelly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the hooks API itself is clean, in this concrete case it is not ideal of course - we can revisit this for sure!

}

/** Returns the value of a meta tag */
Expand Down
Loading