Skip to content

Commit 10337a5

Browse files
committed
feat(core): Create types and utilities for span links
1 parent cea9484 commit 10337a5

File tree

7 files changed

+63
-33
lines changed

7 files changed

+63
-33
lines changed

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export { handleCallbackErrors } from './utils/handleCallbackErrors';
7676
export { parameterize } from './utils/parameterize';
7777
export { addAutoIpAddressToSession, addAutoIpAddressToUser } from './utils/ipAddress';
7878
export {
79+
convertSpanLinksForEnvelope,
7980
spanToTraceHeader,
8081
spanToJSON,
8182
spanIsSampled,

packages/core/src/tracing/sentryNonRecordingSpan.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,12 @@ export class SentryNonRecordingSpan implements Span {
6969
return this;
7070
}
7171

72-
/**
73-
* This should generally not be used,
74-
* but we need it for being compliant with the OTEL Span interface.
75-
*
76-
* @hidden
77-
* @internal
78-
*/
72+
/** @inheritDoc */
7973
public addLink(_link: unknown): this {
8074
return this;
8175
}
8276

83-
/**
84-
* This should generally not be used,
85-
* but we need it for being compliant with the OTEL Span interface.
86-
*
87-
* @hidden
88-
* @internal
89-
*/
77+
/** @inheritDoc */
9078
public addLinks(_links: unknown[]): this {
9179
return this;
9280
}

packages/core/src/tracing/sentrySpan.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,24 +109,12 @@ export class SentrySpan implements Span {
109109
}
110110
}
111111

112-
/**
113-
* This should generally not be used,
114-
* but it is needed for being compliant with the OTEL Span interface.
115-
*
116-
* @hidden
117-
* @internal
118-
*/
112+
/** @inheritDoc */
119113
public addLink(_link: unknown): this {
120114
return this;
121115
}
122116

123-
/**
124-
* This should generally not be used,
125-
* but it is needed for being compliant with the OTEL Span interface.
126-
*
127-
* @hidden
128-
* @internal
129-
*/
117+
/** @inheritDoc */
130118
public addLinks(_links: unknown[]): this {
131119
return this;
132120
}

packages/core/src/types-hoist/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export type {
113113
SpanContextData,
114114
TraceFlag,
115115
} from './span';
116+
export type { SpanLink, SpanLinkJSON } from './link';
116117
export type { SpanStatus } from './spanStatus';
117118
export type { TimedEvent } from './timedEvent';
118119
export type { StackFrame } from './stackframe';

packages/core/src/types-hoist/link.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { SpanAttributeValue, SpanContextData } from './span';
2+
3+
type SpanLinkAttributes = {
4+
/**
5+
* Setting the link type to 'previous_trace' helps the Sentry product linking to the previous trace
6+
*/
7+
'sentry.link.type'?: string | 'previous_trace';
8+
} & Record<string, SpanAttributeValue | undefined>;
9+
10+
export interface SpanLink {
11+
/**
12+
* Contains the SpanContext of the span to link to
13+
*/
14+
context: SpanContextData;
15+
/**
16+
* A key-value pair with primitive values or an array of primitive values
17+
*/
18+
attributes?: SpanLinkAttributes;
19+
}
20+
21+
/**
22+
* Link interface for the event envelope item. It's a flattened representation of `SpanLink`.
23+
* Can include additional fields defined by OTel.
24+
*/
25+
export interface SpanLinkJSON extends Record<string, unknown> {
26+
span_id: string;
27+
trace_id: string;
28+
sampled?: boolean;
29+
attributes?: SpanLinkAttributes;
30+
}

packages/core/src/types-hoist/span.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { SpanLink, SpanLinkJSON } from './link';
12
import type { Measurements } from './measurement';
23
import type { HrTime } from './opentelemetry';
34
import type { SpanStatus } from './spanStatus';
@@ -50,6 +51,7 @@ export interface SpanJSON {
5051
measurements?: Measurements;
5152
is_segment?: boolean;
5253
segment_id?: string;
54+
links?: SpanLinkJSON[];
5355
}
5456

5557
// These are aligned with OpenTelemetry trace flags
@@ -249,14 +251,19 @@ export interface Span {
249251
addEvent(name: string, attributesOrStartTime?: SpanAttributes | SpanTimeInput, startTime?: SpanTimeInput): this;
250252

251253
/**
252-
* NOT USED IN SENTRY, only added for compliance with OTEL Span interface
254+
* Associates this span with a related span. Links can reference spans from the same or different trace
255+
* and are typically used for batch operations, cross-trace scenarios, or scatter/gather patterns.
256+
*
257+
* Prefer setting links during span creation when possible to support sampling decisions.
258+
* @param link - The link containing the context of the span to link to and optional attributes
253259
*/
254-
addLink(link: unknown): this;
260+
addLink(link: SpanLink): this;
255261

256262
/**
257-
* NOT USED IN SENTRY, only added for compliance with OTEL Span interface
263+
* Associates this span with multiple related spans. See {@link addLink} for more details.
264+
* @param links - Array of links to associate with this span
258265
*/
259-
addLinks(links: unknown): this;
266+
addLinks(links: SpanLink[]): this;
260267

261268
/**
262269
* NOT USED IN SENTRY, only added for compliance with OTEL Span interface

packages/core/src/utils/spanUtils.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import type {
1414
Span,
1515
SpanAttributes,
1616
SpanJSON,
17+
SpanLink,
18+
SpanLinkJSON,
1719
SpanOrigin,
1820
SpanStatus,
1921
SpanTimeInput,
@@ -81,6 +83,19 @@ export function spanToTraceHeader(span: Span): string {
8183
return generateSentryTraceHeader(traceId, spanId, sampled);
8284
}
8385

86+
/**
87+
* Converts the span links array to a flattened version to be sent within an envelope
88+
*/
89+
export function convertSpanLinksForEnvelope(links: SpanLink[]): SpanLinkJSON[] {
90+
return links.map(({ context: { spanId, traceId, traceFlags, ...restContext }, attributes }) => ({
91+
span_id: spanId,
92+
trace_id: traceId,
93+
sampled: traceFlags === TRACE_FLAG_SAMPLED,
94+
attributes,
95+
...restContext,
96+
}));
97+
}
98+
8499
/**
85100
* Convert a span time input into a timestamp in seconds.
86101
*/
@@ -180,7 +195,7 @@ function spanIsSentrySpan(span: Span): span is SentrySpan {
180195
* However, this has a slightly different semantic, as it also returns false if the span is finished.
181196
* So in the case where this distinction is important, use this method.
182197
*/
183-
export function spanIsSampled(span: Span): boolean {
198+
export function spanIsSampled(span: { spanContext: Span['spanContext'] }): boolean {
184199
// We align our trace flags with the ones OpenTelemetry use
185200
// So we also check for sampled the same way they do.
186201
const { traceFlags } = span.spanContext();

0 commit comments

Comments
 (0)