Skip to content

Commit 03f20f6

Browse files
committed
feat(node-experimental): Implement new performance APIs
1 parent 13b4729 commit 03f20f6

File tree

3 files changed

+121
-1
lines changed

3 files changed

+121
-1
lines changed

packages/node-experimental/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export { init } from './sdk/init';
1111
export { INTEGRATIONS as Integrations };
1212
export { getAutoPerformanceIntegrations } from './integrations/getAutoPerformanceIntegrations';
1313
export * as Handlers from './sdk/handlers';
14+
export * from './sdk/trace';
1415

1516
export {
1617
makeNodeTransport,
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import type { Span as OtelSpan, Tracer } from '@opentelemetry/api';
2+
import { trace } from '@opentelemetry/api';
3+
import { getCurrentHub, hasTracingEnabled, Transaction } from '@sentry/core';
4+
import { _INTERNAL_getSentrySpan } from '@sentry/opentelemetry-node';
5+
import type { Span, TransactionContext } from '@sentry/types';
6+
import { isThenable } from '@sentry/utils';
7+
8+
import type { NodeExperimentalClient } from './client';
9+
10+
/**
11+
* Wraps a function with a transaction/span and finishes the span after the function is done.
12+
* The created span is the active span and will be used as parent by other spans created inside the function
13+
* and can be accessed via `Sentry.getSpan()`, as long as the function is executed while the scope is active.
14+
*
15+
* If you want to create a span that is not set as active, use {@link startSpan}.
16+
*
17+
* Note that if you have not enabled tracing extensions via `addTracingExtensions`
18+
* or you didn't set `tracesSampleRate`, this function will not generate spans
19+
* and the `span` returned from the callback will be undefined.
20+
*/
21+
export function startActiveSpan<T>(context: TransactionContext, callback: (span: Span | undefined) => T): T {
22+
const tracer = getTracer();
23+
if (!tracer) {
24+
return callback(undefined);
25+
}
26+
27+
const name = context.description || context.op || '<unknown>';
28+
29+
return tracer.startActiveSpan(name, (span: OtelSpan): T => {
30+
const otelSpanId = span.spanContext().spanId;
31+
32+
const sentrySpan = _INTERNAL_getSentrySpan(otelSpanId);
33+
34+
if (sentrySpan && isTransaction(sentrySpan) && context.metadata) {
35+
sentrySpan.setMetadata(context.metadata);
36+
}
37+
38+
function finishSpan(): void {
39+
span.end();
40+
}
41+
42+
let maybePromiseResult: T;
43+
try {
44+
maybePromiseResult = callback(sentrySpan);
45+
} catch (e) {
46+
sentrySpan && sentrySpan.setStatus('internal_error');
47+
finishSpan();
48+
throw e;
49+
}
50+
51+
if (isThenable(maybePromiseResult)) {
52+
Promise.resolve(maybePromiseResult).then(
53+
() => {
54+
finishSpan();
55+
},
56+
() => {
57+
sentrySpan && sentrySpan.setStatus('internal_error');
58+
finishSpan();
59+
},
60+
);
61+
} else {
62+
finishSpan();
63+
}
64+
65+
return maybePromiseResult;
66+
});
67+
}
68+
69+
/**
70+
* Creates a span. This span is not set as active, so will not get automatic instrumentation spans
71+
* as children or be able to be accessed via `Sentry.getSpan()`.
72+
*
73+
* If you want to create a span that is set as active, use {@link startActiveSpan}.
74+
*
75+
* Note that if you have not enabled tracing extensions via `addTracingExtensions`
76+
* or you didn't set `tracesSampleRate` or `tracesSampler`, this function will not generate spans
77+
* and the `span` returned from the callback will be undefined.
78+
*/
79+
export function startSpan(context: TransactionContext): Span | undefined {
80+
const tracer = getTracer();
81+
if (!tracer) {
82+
return undefined;
83+
}
84+
85+
const name = context.description || context.op || '<unknown>';
86+
const otelSpan = tracer.startSpan(name);
87+
88+
const otelSpanId = otelSpan.spanContext().spanId;
89+
90+
const sentrySpan = _INTERNAL_getSentrySpan(otelSpanId);
91+
92+
if (sentrySpan && isTransaction(sentrySpan) && context.metadata) {
93+
sentrySpan.setMetadata(context.metadata);
94+
}
95+
96+
return sentrySpan;
97+
}
98+
99+
/**
100+
* Returns the currently active span.
101+
*/
102+
export function getActiveSpan(): Span | undefined {
103+
const otelSpan = trace.getActiveSpan();
104+
const spanId = otelSpan && otelSpan.spanContext().spanId;
105+
return spanId ? _INTERNAL_getSentrySpan(spanId) : undefined;
106+
}
107+
108+
function getTracer(): Tracer | undefined {
109+
if (!hasTracingEnabled()) {
110+
return undefined;
111+
}
112+
113+
const client = getCurrentHub().getClient<NodeExperimentalClient>();
114+
return client && client.tracer;
115+
}
116+
117+
function isTransaction(span: Span): span is Transaction {
118+
return span instanceof Transaction;
119+
}

packages/opentelemetry-node/src/spanprocessor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export class SentrySpanProcessor implements OtelSpanProcessor {
9292
*/
9393
public onEnd(otelSpan: OtelSpan): void {
9494
const otelSpanId = otelSpan.spanContext().spanId;
95-
const sentrySpan = SENTRY_SPAN_PROCESSOR_MAP.get(otelSpanId);
95+
const sentrySpan = getSentrySpan(otelSpanId);
9696

9797
if (!sentrySpan) {
9898
__DEBUG_BUILD__ &&

0 commit comments

Comments
 (0)