Skip to content

Commit

Permalink
ref(tracing): Add origin to spans (#8765)
Browse files Browse the repository at this point in the history
This adds an `origin` to all spans, which defaults to `manual` but is
set to something more meaningful for all our auto instrumentation.

I tried to come up with reasonable origin names, I hope it makes sense
everywhere 😅 Also note that this now uses a new TS feature which seems
to be correctly transpiled to TS 3.8, as far as I can tell! 🎉

Closes #8510

---------

Co-authored-by: Luca Forstner <[email protected]>
Co-authored-by: Lukas Stracke <[email protected]>
  • Loading branch information
3 people authored Aug 29, 2023
1 parent e72c047 commit 0fd86bc
Show file tree
Hide file tree
Showing 85 changed files with 346 additions and 43 deletions.
6 changes: 6 additions & 0 deletions packages/angular/src/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function routingInstrumentation(
customStartTransaction({
name: WINDOW.location.pathname,
op: 'pageload',
origin: 'auto.pageload.angular',
metadata: { source: 'url' },
});
}
Expand Down Expand Up @@ -84,6 +85,7 @@ export class TraceService implements OnDestroy {
activeTransaction = stashedStartTransaction({
name: strippedUrl,
op: 'navigation',
origin: 'auto.navigation.angular',
metadata: { source: 'url' },
});
}
Expand All @@ -95,6 +97,7 @@ export class TraceService implements OnDestroy {
this._routingSpan = activeTransaction.startChild({
description: `${navigationEvent.url}`,
op: ANGULAR_ROUTING_OP,
origin: 'auto.ui.angular',
tags: {
'routing.instrumentation': '@sentry/angular',
url: strippedUrl,
Expand Down Expand Up @@ -192,6 +195,7 @@ export class TraceDirective implements OnInit, AfterViewInit {
this._tracingSpan = activeTransaction.startChild({
description: `<${this.componentName}>`,
op: ANGULAR_INIT_OP,
origin: 'auto.ui.angular.trace_directive',
});
}
}
Expand Down Expand Up @@ -233,6 +237,7 @@ export function TraceClassDecorator(): ClassDecorator {
tracingSpan = activeTransaction.startChild({
description: `<${target.name}>`,
op: ANGULAR_INIT_OP,
origin: 'auto.ui.angular.trace_class_decorator',
});
}
if (originalOnInit) {
Expand Down Expand Up @@ -270,6 +275,7 @@ export function TraceMethodDecorator(): MethodDecorator {
description: `<${target.constructor.name}>`,
endTimestamp: now,
op: `${ANGULAR_OP}.${String(propertyKey)}`,
origin: 'auto.ui.angular.trace_method_decorator',
startTimestamp: now,
});
}
Expand Down
8 changes: 8 additions & 0 deletions packages/angular/test/tracing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('Angular Tracing', () => {
expect(startTransaction).toHaveBeenCalledWith({
name: '/',
op: 'pageload',
origin: 'auto.pageload.angular',
metadata: { source: 'url' },
});
});
Expand Down Expand Up @@ -137,6 +138,7 @@ describe('Angular Tracing', () => {
expect(customStartTransaction).toHaveBeenCalledWith({
name: url,
op: 'pageload',
origin: 'auto.pageload.angular',
metadata: { source: 'url' },
});

Expand Down Expand Up @@ -327,6 +329,7 @@ describe('Angular Tracing', () => {
expect(customStartTransaction).toHaveBeenCalledWith({
name: url,
op: 'navigation',
origin: 'auto.navigation.angular',
metadata: { source: 'url' },
});
expect(transaction.setName).toHaveBeenCalledWith(result, 'route');
Expand Down Expand Up @@ -358,6 +361,7 @@ describe('Angular Tracing', () => {

expect(transaction.startChild).toHaveBeenCalledWith({
op: 'ui.angular.init',
origin: 'auto.ui.angular.trace_directive',
description: '<unknown>',
});

Expand All @@ -384,6 +388,7 @@ describe('Angular Tracing', () => {

expect(transaction.startChild).toHaveBeenCalledWith({
op: 'ui.angular.init',
origin: 'auto.ui.angular.trace_directive',
description: '<test-component>',
});

Expand Down Expand Up @@ -458,6 +463,7 @@ describe('Angular Tracing', () => {
expect(transaction.startChild).toHaveBeenCalledWith({
description: '<DecoratedComponent>',
op: 'ui.angular.init',
origin: 'auto.ui.angular.trace_class_decorator',
});

expect(origNgOnInitMock).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -511,13 +517,15 @@ describe('Angular Tracing', () => {
expect(transaction.startChild.mock.calls[0][0]).toEqual({
description: '<DecoratedComponent>',
op: 'ui.angular.ngOnInit',
origin: 'auto.ui.angular.trace_method_decorator',
startTimestamp: expect.any(Number),
endTimestamp: expect.any(Number),
});

expect(transaction.startChild.mock.calls[1][0]).toEqual({
description: '<DecoratedComponent>',
op: 'ui.angular.ngAfterViewInit',
origin: 'auto.ui.angular.trace_method_decorator',
startTimestamp: expect.any(Number),
endTimestamp: expect.any(Number),
});
Expand Down
6 changes: 5 additions & 1 deletion packages/browser/src/profiling/hubextensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,11 @@ export function wrapTransactionWithProfiling(transaction: Transaction): Transact

// This is temporary - we will use the collected span data to evaluate
// if deferring txn.finish until profiler resolves is a viable approach.
const stopProfilerSpan = transaction.startChild({ description: 'profiler.stop', op: 'profiler' });
const stopProfilerSpan = transaction.startChild({
description: 'profiler.stop',
op: 'profiler',
origin: 'auto.profiler.browser',
});

return profiler
.stop()
Expand Down
46 changes: 17 additions & 29 deletions packages/core/src/tracing/span.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
Primitive,
Span as SpanInterface,
SpanContext,
SpanOrigin,
TraceContext,
Transaction,
} from '@sentry/types';
Expand Down Expand Up @@ -115,30 +116,27 @@ export class Span implements SpanInterface {
*/
public instrumenter: Instrumenter;

/**
* The origin of the span, giving context about what created the span.
*/
public origin?: SpanOrigin;

/**
* You should never call the constructor manually, always use `Sentry.startTransaction()`
* or call `startChild()` on an existing span.
* @internal
* @hideconstructor
* @hidden
*/
public constructor(spanContext?: SpanContext) {
this.traceId = uuid4();
this.spanId = uuid4().substring(16);
this.startTimestamp = timestampInSeconds();
this.tags = {};
this.data = {};
this.instrumenter = 'sentry';

if (!spanContext) {
return this;
}
if (spanContext.traceId) {
this.traceId = spanContext.traceId;
}
if (spanContext.spanId) {
this.spanId = spanContext.spanId;
}
public constructor(spanContext: SpanContext = {}) {
this.traceId = spanContext.traceId || uuid4();
this.spanId = spanContext.spanId || uuid4().substring(16);
this.startTimestamp = spanContext.startTimestamp || timestampInSeconds();
this.tags = spanContext.tags || {};
this.data = spanContext.data || {};
this.instrumenter = spanContext.instrumenter || 'sentry';
this.origin = spanContext.origin || 'manual';

if (spanContext.parentSpanId) {
this.parentSpanId = spanContext.parentSpanId;
}
Expand All @@ -155,24 +153,12 @@ export class Span implements SpanInterface {
if (spanContext.name) {
this.description = spanContext.name;
}
if (spanContext.data) {
this.data = spanContext.data;
}
if (spanContext.tags) {
this.tags = spanContext.tags;
}
if (spanContext.status) {
this.status = spanContext.status;
}
if (spanContext.startTimestamp) {
this.startTimestamp = spanContext.startTimestamp;
}
if (spanContext.endTimestamp) {
this.endTimestamp = spanContext.endTimestamp;
}
if (spanContext.instrumenter) {
this.instrumenter = spanContext.instrumenter;
}
}

/**
Expand Down Expand Up @@ -355,6 +341,7 @@ export class Span implements SpanInterface {
tags?: { [key: string]: Primitive };
timestamp?: number;
trace_id: string;
origin?: SpanOrigin;
} {
return dropUndefinedKeys({
data: Object.keys(this.data).length > 0 ? this.data : undefined,
Expand All @@ -367,6 +354,7 @@ export class Span implements SpanInterface {
tags: Object.keys(this.tags).length > 0 ? this.tags : undefined,
timestamp: this.endTimestamp,
trace_id: this.traceId,
origin: this.origin,
});
}
}
Expand Down
9 changes: 8 additions & 1 deletion packages/ember/addon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,14 @@ export const instrumentRoutePerformance = <T extends RouteConstructor>(BaseRoute
if (!currentTransaction) {
return result;
}
currentTransaction.startChild({ op, description, startTimestamp }).finish();
currentTransaction
.startChild({
op,
description,
origin: 'auto.ui.ember',
startTimestamp,
})
.finish();
return result;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export function _instrumentEmberRouter(
activeTransaction = startTransaction({
name: `route:${routeInfo.name}`,
op: 'pageload',
origin: 'auto.pageload.ember',
tags: {
url,
toRoute: routeInfo.name,
Expand All @@ -141,6 +142,7 @@ export function _instrumentEmberRouter(
activeTransaction = startTransaction({
name: `route:${toRoute}`,
op: 'navigation',
origin: 'auto.navigation.ember',
tags: {
fromRoute,
toRoute,
Expand All @@ -150,6 +152,7 @@ export function _instrumentEmberRouter(
transitionSpan = activeTransaction?.startChild({
op: 'ui.ember.transition',
description: `route:${fromRoute} -> route:${toRoute}`,
origin: 'auto.ui.ember',
});
});

Expand Down Expand Up @@ -211,6 +214,7 @@ function _instrumentEmberRunloop(config: EmberSentryConfig): void {
activeTransaction
?.startChild({
op: `ui.ember.runloop.${queue}`,
origin: 'auto.ui.ember',
startTimestamp: currentQueueStart,
endTimestamp: now,
})
Expand Down Expand Up @@ -287,6 +291,7 @@ function processComponentRenderAfter(
activeTransaction?.startChild({
op,
description: payload.containerKey || payload.object,
origin: 'auto.ui.ember',
startTimestamp: begin.now,
endTimestamp: now,
});
Expand Down Expand Up @@ -370,6 +375,7 @@ function _instrumentInitialLoad(config: EmberSentryConfig): void {
const transaction = getActiveTransaction();
const span = transaction?.startChild({
op: 'ui.ember.init',
origin: 'auto.ui.ember',
startTimestamp,
});
span?.finish(endTimestamp);
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/src/client/performance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export function nextRouterInstrumentation(
const nextRouteChangeSpan = navigationTransaction.startChild({
op: 'ui.nextjs.route-change',
description: 'Next.js Route Change',
origin: 'auto.navigation.nextjs',
});

const finishRouteChangeSpan = (): void => {
Expand Down
2 changes: 2 additions & 0 deletions packages/nextjs/src/common/utils/edgeWrapperUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function withEdgeWrapping<H extends EdgeRouteHandler>(
span = prevSpan.startChild({
description: options.spanDescription,
op: options.spanOp,
origin: 'auto.function.nextjs',
});
} else if (req instanceof Request) {
const sentryTrace = req.headers.get('sentry-trace') || '';
Expand All @@ -39,6 +40,7 @@ export function withEdgeWrapping<H extends EdgeRouteHandler>(
span = startTransaction({
name: options.spanDescription,
op: options.spanOp,
origin: 'auto.ui.nextjs.withEdgeWrapping',
...traceparentData,
metadata: {
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
Expand Down
4 changes: 4 additions & 0 deletions packages/nextjs/src/common/utils/wrapperUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
{
op: 'http.server',
name: options.requestedRouteName,
origin: 'auto.function.nextjs',
...traceparentData,
status: 'ok',
metadata: {
Expand Down Expand Up @@ -131,12 +132,14 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
dataFetcherSpan = spanToContinue.startChild({
op: 'function.nextjs',
description: `${options.dataFetchingMethodName} (${options.dataFetcherRouteName})`,
origin: 'auto.function.nextjs',
status: 'ok',
});
} else {
dataFetcherSpan = startTransaction({
op: 'function.nextjs',
name: `${options.dataFetchingMethodName} (${options.dataFetcherRouteName})`,
origin: 'auto.function.nextjs',
...traceparentData,
status: 'ok',
metadata: {
Expand Down Expand Up @@ -203,6 +206,7 @@ export async function callDataFetcherTraced<F extends (...args: any[]) => Promis
// route's transaction
const span = transaction.startChild({
op: 'function.nextjs',
origin: 'auto.function.nextjs',
description: `${dataFetchingMethodName} (${parameterizedRoute})`,
status: 'ok',
});
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/src/common/wrapApiHandlerWithSentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export function withSentry(apiHandler: NextApiHandler, parameterizedRoute?: stri
{
name: `${reqMethod}${reqPath}`,
op: 'http.server',
origin: 'auto.http.nextjs',
...traceparentData,
metadata: {
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function wrapServerComponentWithSentry<F extends (...args: any[]) => any>
op: 'function.nextjs',
name: `${componentType} Server Component (${componentRoute})`,
status: 'ok',
origin: 'auto.function.nextjs',
...traceparentData,
metadata: {
source: 'component',
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/test/config/withSentry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ describe('withSentry', () => {
{
name: 'GET http://dogs.are.great',
op: 'http.server',
origin: 'auto.http.nextjs',

metadata: {
source: 'route',
Expand Down
11 changes: 10 additions & 1 deletion packages/node-experimental/src/integrations/express.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Instrumentation } from '@opentelemetry/instrumentation';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { addOtelSpanData } from '@sentry/opentelemetry-node';
import type { Integration } from '@sentry/types';

import { NodePerformanceIntegration } from './NodePerformanceIntegration';
Expand Down Expand Up @@ -27,6 +28,14 @@ export class Express extends NodePerformanceIntegration<void> implements Integra

/** @inheritDoc */
public setupInstrumentation(): void | Instrumentation[] {
return [new ExpressInstrumentation()];
return [
new ExpressInstrumentation({
requestHook(span) {
addOtelSpanData(span.spanContext().spanId, {
origin: 'auto.http.otel-express',
});
},
}),
];
}
}
Loading

0 comments on commit 0fd86bc

Please sign in to comment.