Skip to content

feat(core): Introduce startSpanManual #8913

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 1 commit into from
Sep 11, 2023
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: 1 addition & 0 deletions packages/browser/src/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export {
getActiveSpan,
startSpan,
startInactiveSpan,
startSpanManual,
SDK_VERSION,
setContext,
setExtra,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/tracing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export { extractTraceparentData, getActiveTransaction } from './utils';
export { SpanStatus } from './spanstatus';
export type { SpanStatusType } from './span';
// eslint-disable-next-line deprecation/deprecation
export { trace, getActiveSpan, startSpan, startInactiveSpan, startActiveSpan } from './trace';
export { trace, getActiveSpan, startSpan, startInactiveSpan, startActiveSpan, startSpanManual } from './trace';
export { getDynamicSamplingContextFromClient } from './dynamicSamplingContext';
export { setMeasurement } from './measurement';
99 changes: 72 additions & 27 deletions packages/core/src/tracing/trace.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { TransactionContext } from '@sentry/types';
import { isThenable } from '@sentry/utils';

import type { Hub } from '../hub';
import { getCurrentHub } from '../hub';
import { hasTracingEnabled } from '../utils/hasTracingEnabled';
import type { Span } from './span';
Expand All @@ -23,25 +24,14 @@ export function trace<T>(
// eslint-disable-next-line @typescript-eslint/no-empty-function
onError: (error: unknown) => void = () => {},
): T {
const ctx = { ...context };
// If a name is set and a description is not, set the description to the name.
if (ctx.name !== undefined && ctx.description === undefined) {
ctx.description = ctx.name;
}
const ctx = normalizeContext(context);

const hub = getCurrentHub();
const scope = hub.getScope();

const parentSpan = scope.getSpan();

function createChildSpanOrTransaction(): Span | undefined {
if (!hasTracingEnabled()) {
return undefined;
}
return parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
}
const activeSpan = createChildSpanOrTransaction(hub, parentSpan, ctx);

const activeSpan = createChildSpanOrTransaction();
scope.setSpan(activeSpan);

function finishAndSetSpan(): void {
Expand Down Expand Up @@ -89,25 +79,13 @@ export function trace<T>(
* and the `span` returned from the callback will be undefined.
*/
export function startSpan<T>(context: TransactionContext, callback: (span: Span | undefined) => T): T {
const ctx = { ...context };
// If a name is set and a description is not, set the description to the name.
if (ctx.name !== undefined && ctx.description === undefined) {
ctx.description = ctx.name;
}
const ctx = normalizeContext(context);

const hub = getCurrentHub();
const scope = hub.getScope();

const parentSpan = scope.getSpan();

function createChildSpanOrTransaction(): Span | undefined {
if (!hasTracingEnabled()) {
return undefined;
}
return parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
}

const activeSpan = createChildSpanOrTransaction();
const activeSpan = createChildSpanOrTransaction(hub, parentSpan, ctx);
scope.setSpan(activeSpan);

function finishAndSetSpan(): void {
Expand Down Expand Up @@ -146,6 +124,52 @@ export function startSpan<T>(context: TransactionContext, callback: (span: Span
*/
export const startActiveSpan = startSpan;

/**
* Similar to `Sentry.startSpan`. Wraps a function with a transaction/span, but does not finish the span
* after the function is done automatically.
*
* The created span is the active span and will be used as parent by other spans created inside the function
* and can be accessed via `Sentry.getActiveSpan()`, as long as the function is executed while the scope is active.
*
* Note that if you have not enabled tracing extensions via `addTracingExtensions`
* or you didn't set `tracesSampleRate`, this function will not generate spans
* and the `span` returned from the callback will be undefined.
*/
export function startSpanManual<T>(
context: TransactionContext,
callback: (span: Span | undefined, finish: () => void) => T,
): T {
const ctx = normalizeContext(context);

const hub = getCurrentHub();
const scope = hub.getScope();
const parentSpan = scope.getSpan();

const activeSpan = createChildSpanOrTransaction(hub, parentSpan, ctx);
scope.setSpan(activeSpan);

function finishAndSetSpan(): void {
activeSpan && activeSpan.finish();
hub.getScope().setSpan(parentSpan);
}

let maybePromiseResult: T;
try {
maybePromiseResult = callback(activeSpan, finishAndSetSpan);
} catch (e) {
activeSpan && activeSpan.setStatus('internal_error');
throw e;
}

if (isThenable(maybePromiseResult)) {
Promise.resolve(maybePromiseResult).then(undefined, () => {
activeSpan && activeSpan.setStatus('internal_error');
});
}

return maybePromiseResult;
}

/**
* Creates a span. This span is not set as active, so will not get automatic instrumentation spans
* as children or be able to be accessed via `Sentry.getSpan()`.
Expand Down Expand Up @@ -178,3 +202,24 @@ export function startInactiveSpan(context: TransactionContext): Span | undefined
export function getActiveSpan(): Span | undefined {
return getCurrentHub().getScope().getSpan();
}

function createChildSpanOrTransaction(
hub: Hub,
parentSpan: Span | undefined,
ctx: TransactionContext,
): Span | undefined {
if (!hasTracingEnabled()) {
return undefined;
}
return parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
}

function normalizeContext(context: TransactionContext): TransactionContext {
const ctx = { ...context };
// If a name is set and a description is not, set the description to the name.
if (ctx.name !== undefined && ctx.description === undefined) {
ctx.description = ctx.name;
}

return ctx;
}
1 change: 1 addition & 0 deletions packages/node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export {
// eslint-disable-next-line deprecation/deprecation
startActiveSpan,
startInactiveSpan,
startSpanManual,
} from '@sentry/core';
export type { SpanStatusType } from '@sentry/core';
export { autoDiscoverNodePerformanceMonitoringIntegrations } from './tracing';
Expand Down
1 change: 1 addition & 0 deletions packages/serverless/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@ export {
// eslint-disable-next-line deprecation/deprecation
startActiveSpan,
startInactiveSpan,
startSpanManual,
} from '@sentry/node';
1 change: 1 addition & 0 deletions packages/sveltekit/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export {
// eslint-disable-next-line deprecation/deprecation
startActiveSpan,
startInactiveSpan,
startSpanManual,
} from '@sentry/node';

// We can still leave this for the carrier init and type exports
Expand Down