Skip to content
Open
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
4 changes: 4 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ export type {
ProfileChunkEnvelope,
ProfileChunkItem,
SpanEnvelope,
SpanV2Envelope,
SpanItem,
LogEnvelope,
MetricEnvelope,
Expand Down Expand Up @@ -451,6 +452,9 @@ export type {
SpanJSON,
SpanContextData,
TraceFlag,
SpanV2JSON,
SerializedSpanContainer,
SerializedSpan,
} from './types-hoist/span';
export type { SpanStatus } from './types-hoist/spanStatus';
export type { Log, LogSeverityLevel } from './types-hoist/log';
Expand Down
21 changes: 20 additions & 1 deletion packages/core/src/types-hoist/envelope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { Profile, ProfileChunk } from './profiling';
import type { ReplayEvent, ReplayRecordingData } from './replay';
import type { SdkInfo } from './sdkinfo';
import type { SerializedSession, SessionAggregates } from './session';
import type { SpanJSON } from './span';
import type { SerializedSpanContainer, SpanJSON } from './span';

// Based on: https://develop.sentry.dev/sdk/envelopes/

Expand Down Expand Up @@ -91,6 +91,21 @@ type CheckInItemHeaders = { type: 'check_in' };
type ProfileItemHeaders = { type: 'profile' };
type ProfileChunkItemHeaders = { type: 'profile_chunk' };
type SpanItemHeaders = { type: 'span' };
type SpanContainerItemHeaders = {
/**
* Same as v1 span item type but this envelope is distinguished by {@link SpanContainerItemHeaders.content_type}.
*/
type: 'span';
/**
* The number of span items in the container. This must be the same as the number of span items in the payload.
*/
item_count: number;
/**
* The content type of the span items. This must be `application/vnd.sentry.items.span.v2+json`.
* (the presence of this field also distinguishes the span item from the v1 span item)
*/
content_type: 'application/vnd.sentry.items.span.v2+json';
};
type LogContainerItemHeaders = {
type: 'log';
/**
Expand Down Expand Up @@ -123,6 +138,7 @@ export type FeedbackItem = BaseEnvelopeItem<FeedbackItemHeaders, FeedbackEvent>;
export type ProfileItem = BaseEnvelopeItem<ProfileItemHeaders, Profile>;
export type ProfileChunkItem = BaseEnvelopeItem<ProfileChunkItemHeaders, ProfileChunk>;
export type SpanItem = BaseEnvelopeItem<SpanItemHeaders, Partial<SpanJSON>>;
export type SpanContainerItem = BaseEnvelopeItem<SpanContainerItemHeaders, SerializedSpanContainer>;
export type LogContainerItem = BaseEnvelopeItem<LogContainerItemHeaders, SerializedLogContainer>;
export type MetricContainerItem = BaseEnvelopeItem<MetricContainerItemHeaders, SerializedMetricContainer>;
export type RawSecurityItem = BaseEnvelopeItem<RawSecurityHeaders, LegacyCSPReport>;
Expand All @@ -133,6 +149,7 @@ type CheckInEnvelopeHeaders = { trace?: DynamicSamplingContext };
type ClientReportEnvelopeHeaders = BaseEnvelopeHeaders;
type ReplayEnvelopeHeaders = BaseEnvelopeHeaders;
type SpanEnvelopeHeaders = BaseEnvelopeHeaders & { trace?: DynamicSamplingContext };
type SpanV2EnvelopeHeaders = BaseEnvelopeHeaders & { trace?: DynamicSamplingContext };
type LogEnvelopeHeaders = BaseEnvelopeHeaders;
type MetricEnvelopeHeaders = BaseEnvelopeHeaders;
export type EventEnvelope = BaseEnvelope<
Expand All @@ -144,6 +161,7 @@ export type ClientReportEnvelope = BaseEnvelope<ClientReportEnvelopeHeaders, Cli
export type ReplayEnvelope = [ReplayEnvelopeHeaders, [ReplayEventItem, ReplayRecordingItem]];
export type CheckInEnvelope = BaseEnvelope<CheckInEnvelopeHeaders, CheckInItem>;
export type SpanEnvelope = BaseEnvelope<SpanEnvelopeHeaders, SpanItem>;
export type SpanV2Envelope = BaseEnvelope<SpanV2EnvelopeHeaders, SpanContainerItem>;
export type ProfileChunkEnvelope = BaseEnvelope<BaseEnvelopeHeaders, ProfileChunkItem>;
export type RawSecurityEnvelope = BaseEnvelope<BaseEnvelopeHeaders, RawSecurityItem>;
export type LogEnvelope = BaseEnvelope<LogEnvelopeHeaders, LogContainerItem>;
Expand All @@ -157,6 +175,7 @@ export type Envelope =
| ReplayEnvelope
| CheckInEnvelope
| SpanEnvelope
| SpanV2Envelope
| RawSecurityEnvelope
| LogEnvelope
| MetricEnvelope;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/types-hoist/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ export interface SpanLink {
* Link interface for the event envelope item. It's a flattened representation of `SpanLink`.
* Can include additional fields defined by OTel.
*/
export interface SpanLinkJSON extends Record<string, unknown> {
export interface SpanLinkJSON<TAttributes = SpanLinkAttributes> extends Record<string, unknown> {
span_id: string;
trace_id: string;
sampled?: boolean;
attributes?: SpanLinkAttributes;
attributes?: TAttributes;
}
38 changes: 38 additions & 0 deletions packages/core/src/types-hoist/span.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Attributes, RawAttributes } from '../attributes';
import type { SpanLink, SpanLinkJSON } from './link';
import type { Measurements } from './measurement';
import type { HrTime } from './opentelemetry';
Expand Down Expand Up @@ -34,6 +35,43 @@ export type SpanAttributes = Partial<{
/** This type is aligned with the OpenTelemetry TimeInput type. */
export type SpanTimeInput = HrTime | number | Date;

/**
* Intermediate JSON reporesentation of a v2 span, which users and our SDK integrations will interact with.
* This is NOT the final serialized JSON span, but an intermediate step still holding raw attributes.
* The final, serialized span is a {@link SerializedSpan}.
* Main reason: Make it easier and safer for users to work with attributes.
*/
export interface SpanV2JSON {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l/q: Before we merge it as SpanV2JSON. Is SpanV2 the official term? Not sure how other SDKs will handle this, but maybe we could verify that before we get into future confusion what the difference between "span v2" and "span streaming" is

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree that SpanV2JSON is not the ideal name here 😅 SpanJSON would be ideal but that's already taken. Reusing this is IMHO not really possible because it still features properties like description, measurements, data, etc. All those properties and concepts have no place in the new v2 span protocol.

There's no official term for this intermediate step, as most SDKs besides JS never had a reason to surface serialized spans before. They just expose the regular span class instance to users. But we can't do that because OTel span instances are no longer writable at the point where they reach beforeSendSpan.

The old transaction based pattern basically was:
Span -> SpanJSON -> sent as Transaction event envelope

Now we need some other kind of intermediate name. Something like

Span -> StreamedSpanJSON -> sent as SerializedSpan array envelope

The alternative here would be to directly convert the Span to a SerializedSpan and let users deal with the complicated attribute type. This has however the consequence that they could set a type that doesn't conform with the value property of the attribute. Which is why I figured I'd prefer keeping the intermediate step.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The good news here is, as long as we merge against the epic branch, we can break as much as we want with the names 😅

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. Thanks - overall SpanV2 is fine, I just wanted to challenge it once more

trace_id: string;
parent_span_id?: string;
span_id: string;
name: string;
start_timestamp: number;
end_timestamp: number;
status: 'ok' | 'error';
is_segment: boolean;
attributes?: RawAttributes<Record<string, unknown>>;
links?: SpanLinkJSON<RawAttributes<Record<string, unknown>>>[];
}

/**
* Serialized span item.
* This is the final, serialized span format that is sent to Sentry.
* The intermediate representation is {@link SpanV2JSON}.
* Main difference: Attributes are converted to {@link Attributes}, thus including the `type` annotation.
*/
export type SerializedSpan = Omit<SpanV2JSON, 'attributes' | 'links'> & {
attributes?: Attributes;
links?: SpanLinkJSON<Attributes>[];
};

/**
* Envelope span item container.
*/
export type SerializedSpanContainer = {
items: Array<SerializedSpan>;
};

/** A JSON representation of a span. */
export interface SpanJSON {
data: SpanAttributes;
Expand Down
Loading