Skip to content

fix(core): Use consistent continueTrace implementation in core #14813

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 5 commits into from
Dec 20, 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
2 changes: 0 additions & 2 deletions packages/astro/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ export declare function flush(timeout?: number | undefined): PromiseLike<boolean

// eslint-disable-next-line deprecation/deprecation
export declare const getCurrentHub: typeof clientSdk.getCurrentHub;
export declare const getClient: typeof clientSdk.getClient;
export declare const continueTrace: typeof clientSdk.continueTrace;

export declare const Span: clientSdk.Span;

Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/asyncContext/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Scope } from '../scope';
import type { getTraceData } from '../utils/traceData';
import type {
continueTrace,
startInactiveSpan,
startSpan,
startSpanManual,
Expand Down Expand Up @@ -68,4 +69,11 @@ export interface AsyncContextStrategy {

/** Get trace data as serialized string values for propagation via `sentry-trace` and `baggage`. */
getTraceData?: typeof getTraceData;

/**
* Continue a trace from `sentry-trace` and `baggage` values.
* These values can be obtained from incoming request headers, or in the browser from `<meta name="sentry-trace">`
* and `<meta name="baggage">` HTML tags.
*/
continueTrace?: typeof continueTrace;
}
13 changes: 9 additions & 4 deletions packages/core/src/tracing/trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,20 @@ export function startInactiveSpan(options: StartSpanOptions): Span {
* be attached to the incoming trace.
*/
export const continueTrace = <V>(
{
sentryTrace,
baggage,
}: {
options: {
sentryTrace: Parameters<typeof propagationContextFromHeaders>[0];
baggage: Parameters<typeof propagationContextFromHeaders>[1];
},
callback: () => V,
): V => {
const carrier = getMainCarrier();
const acs = getAsyncContextStrategy(carrier);
if (acs.continueTrace) {
return acs.continueTrace(options, callback);
}

const { sentryTrace, baggage } = options;

return withScope(scope => {
const propagationContext = propagationContextFromHeaders(sentryTrace, baggage);
scope.setPropagationContext(propagationContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
captureException,
continueTrace,
getActiveSpan,
httpRequestToRequestData,
isString,
logger,
Expand Down Expand Up @@ -59,7 +60,13 @@ export function wrapApiHandlerWithSentry(apiHandler: NextApiHandler, parameteriz
req.__withSentry_applied__ = true;

return withIsolationScope(isolationScope => {
return continueTrace(
// Normally, there is an active span here (from Next.js OTEL) and we just use that as parent
// Else, we manually continueTrace from the incoming headers
const continueTraceIfNoActiveSpan = getActiveSpan()
? <T>(_opts: unknown, callback: () => T) => callback()
: continueTrace;

return continueTraceIfNoActiveSpan(
{
sentryTrace:
req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { RequestEventData } from '@sentry/core';
import { getActiveSpan } from '@sentry/core';
import {
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
SPAN_STATUS_ERROR,
Expand Down Expand Up @@ -95,7 +96,13 @@ async function withServerActionInstrumentationImplementation<A extends (...args:
} satisfies RequestEventData,
});

return continueTrace(
// Normally, there is an active span here (from Next.js OTEL) and we just use that as parent
// Else, we manually continueTrace from the incoming headers
const continueTraceIfNoActiveSpan = getActiveSpan()
? <T>(_opts: unknown, callback: () => T) => callback()
: continueTrace;

return continueTraceIfNoActiveSpan(
{
sentryTrace: sentryTraceHeader,
baggage: baggageHeader,
Expand Down
4 changes: 0 additions & 4 deletions packages/nextjs/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ export declare function init(
options: Options | clientSdk.BrowserOptions | serverSdk.NodeOptions | edgeSdk.EdgeOptions,
): Client | undefined;

export declare const getClient: typeof clientSdk.getClient;
export declare const getRootSpan: typeof serverSdk.getRootSpan;
export declare const continueTrace: typeof clientSdk.continueTrace;

export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration;
export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration;

Expand Down
4 changes: 1 addition & 3 deletions packages/node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ export type { NodeOptions } from './types';
export { addRequestDataToEvent, DEFAULT_USER_INCLUDES, extractRequestData } from '@sentry/core';

export {
// These are custom variants that need to be used instead of the core one
// As they have slightly different implementations
continueTrace,
// This needs exporting so the NodeClient can be used without calling init
setOpenTelemetryContextAsyncContextStrategy as setNodeAsyncContextStrategy,
} from '@sentry/opentelemetry';
Expand Down Expand Up @@ -105,6 +102,7 @@ export {
getIsolationScope,
getTraceData,
getTraceMetaTags,
continueTrace,
withScope,
withIsolationScope,
captureException,
Expand Down
1 change: 0 additions & 1 deletion packages/nuxt/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@ export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsInteg
export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration;
export declare const getDefaultIntegrations: (options: Options) => Integration[];
export declare const defaultStackParser: StackParser;
export declare const continueTrace: typeof clientSdk.continueTrace;
3 changes: 2 additions & 1 deletion packages/opentelemetry/src/asyncContextStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY,
SENTRY_FORK_SET_SCOPE_CONTEXT_KEY,
} from './constants';
import { startInactiveSpan, startSpan, startSpanManual, withActiveSpan } from './trace';
import { continueTrace, startInactiveSpan, startSpan, startSpanManual, withActiveSpan } from './trace';
import type { CurrentScopes } from './types';
import { getScopesFromContext } from './utils/contextData';
import { getActiveSpan } from './utils/getActiveSpan';
Expand Down Expand Up @@ -103,6 +103,7 @@ export function setOpenTelemetryContextAsyncContextStrategy(): void {
getActiveSpan,
suppressTracing,
getTraceData,
continueTrace,
// The types here don't fully align, because our own `Span` type is narrower
// than the OTEL one - but this is OK for here, as we now we'll only have OTEL spans passed around
withActiveSpan: withActiveSpan as typeof defaultWithActiveSpan,
Expand Down
14 changes: 9 additions & 5 deletions packages/opentelemetry/src/trace.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import type { Context, Span, SpanContext, SpanOptions, Tracer } from '@opentelemetry/api';
import { SpanStatusCode, TraceFlags, context, trace } from '@opentelemetry/api';
import { suppressTracing } from '@opentelemetry/core';
import type { Client, DynamicSamplingContext, Scope, Span as SentrySpan, TraceContext } from '@sentry/core';
import type {
Client,
DynamicSamplingContext,
Scope,
Span as SentrySpan,
TraceContext,
continueTrace as baseContinueTrace,
} from '@sentry/core';
import {
SDK_VERSION,
SEMANTIC_ATTRIBUTE_SENTRY_OP,
continueTrace as baseContinueTrace,
getClient,
getCurrentScope,
getDynamicSamplingContextFromScope,
Expand Down Expand Up @@ -247,9 +253,7 @@ function getContextForScope(scope?: Scope): Context {
* It propagates the trace as a remote span, in addition to setting it on the propagation context.
*/
export function continueTrace<T>(options: Parameters<typeof baseContinueTrace>[0], callback: () => T): T {
return baseContinueTrace(options, () => {
return continueTraceAsRemoteSpan(context.active(), options, callback);
});
return continueTraceAsRemoteSpan(context.active(), options, callback);
}

/**
Expand Down
22 changes: 3 additions & 19 deletions packages/opentelemetry/test/trace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1576,11 +1576,8 @@ describe('continueTrace', () => {
);

expect(scope.getPropagationContext()).toEqual({
dsc: {}, // DSC should be an empty object (frozen), because there was an incoming trace
sampled: false,
parentSpanId: '1121201211212012',
spanId: expect.any(String),
traceId: '12312012123120121231201212312012',
traceId: expect.any(String),
});

expect(scope.getScopeData().sdkProcessingMetadata).toEqual({});
Expand Down Expand Up @@ -1609,14 +1606,8 @@ describe('continueTrace', () => {
);

expect(scope.getPropagationContext()).toEqual({
dsc: {
environment: 'production',
version: '1.0',
},
sampled: true,
parentSpanId: '1121201211212012',
spanId: expect.any(String),
traceId: '12312012123120121231201212312012',
traceId: expect.any(String),
});

expect(scope.getScopeData().sdkProcessingMetadata).toEqual({});
Expand Down Expand Up @@ -1645,16 +1636,9 @@ describe('continueTrace', () => {
);

expect(scope.getPropagationContext()).toEqual({
dsc: {
environment: 'production',
version: '1.0',
},
sampled: true,
parentSpanId: '1121201211212012',
spanId: expect.any(String),
traceId: '12312012123120121231201212312012',
traceId: expect.any(String),
});

expect(scope.getScopeData().sdkProcessingMetadata).toEqual({});
});

Expand Down
2 changes: 0 additions & 2 deletions packages/remix/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ declare const runtime: 'client' | 'server';

// eslint-disable-next-line deprecation/deprecation
export declare const getCurrentHub: typeof clientSdk.getCurrentHub;
export declare const getClient: typeof clientSdk.getClient;
export declare const continueTrace: typeof clientSdk.continueTrace;

export const close = runtime === 'client' ? clientSdk.close : serverSdk.close;
export const flush = runtime === 'client' ? clientSdk.flush : serverSdk.flush;
Expand Down
2 changes: 1 addition & 1 deletion packages/remix/src/utils/instrumentServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
SEMANTIC_ATTRIBUTE_SENTRY_OP,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
continueTrace,
fill,
getActiveSpan,
getClient,
Expand All @@ -19,7 +20,6 @@ import {
winterCGRequestToRequestData,
withIsolationScope,
} from '@sentry/core';
import { continueTrace } from '@sentry/opentelemetry';
import { DEBUG_BUILD } from './debug-build';
import { captureRemixServerException, errorHandleDataFunction, errorHandleDocumentRequestFunction } from './errors';
import { getFutureFlagsServer, getRemixVersionFromBuild } from './futureFlags';
Expand Down
4 changes: 0 additions & 4 deletions packages/solidstart/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ export declare const contextLinesIntegration: typeof clientSdk.contextLinesInteg
export declare const getDefaultIntegrations: (options: Options) => Integration[];
export declare const defaultStackParser: StackParser;

export declare const getClient: typeof clientSdk.getClient;

export declare function close(timeout?: number | undefined): PromiseLike<boolean>;
export declare function flush(timeout?: number | undefined): PromiseLike<boolean>;
export declare function lastEventId(): string | undefined;

export declare const continueTrace: typeof clientSdk.continueTrace;
3 changes: 0 additions & 3 deletions packages/sveltekit/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,11 @@ export declare const contextLinesIntegration: typeof clientSdk.contextLinesInteg
export declare const getDefaultIntegrations: (options: Options) => Integration[];
export declare const defaultStackParser: StackParser;

export declare const getClient: typeof clientSdk.getClient;
// eslint-disable-next-line deprecation/deprecation
export declare const getCurrentHub: typeof clientSdk.getCurrentHub;

export declare function close(timeout?: number | undefined): PromiseLike<boolean>;
export declare function flush(timeout?: number | undefined): PromiseLike<boolean>;
export declare function lastEventId(): string | undefined;

export declare const continueTrace: typeof clientSdk.continueTrace;

export declare function trackComponent(options: clientSdk.TrackingOptions): ReturnType<typeof clientSdk.trackComponent>;
2 changes: 1 addition & 1 deletion packages/sveltekit/src/server/handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Span } from '@sentry/core';
import {
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
continueTrace,
getActiveSpan,
getCurrentScope,
getDefaultIsolationScope,
Expand All @@ -13,7 +14,6 @@ import {
winterCGRequestToRequestData,
withIsolationScope,
} from '@sentry/core';
import { continueTrace } from '@sentry/node';
import type { Handle, ResolveOptions } from '@sveltejs/kit';

import { DEBUG_BUILD } from '../common/debug-build';
Expand Down
Loading