Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2

### :rocket: Features

* feat(otlp-transformer): add span flags support for isRemote property [#5910](https://github.com/open-telemetry/opentelemetry-js/pull/5910) @nikhilmantri0902
Comment thread
nikhilmantri0902 marked this conversation as resolved.
Outdated

### :bug: Bug Fixes

### :books: Documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ export interface ISpan {

/** Span status */
status: IStatus;

/** Span flags */
flags?: number;
}

/**
Expand Down Expand Up @@ -185,4 +188,7 @@ export interface ILink {

/** Link droppedAttributesCount */
droppedAttributesCount: number;

/** Link flags */
flags?: number;
}
20 changes: 19 additions & 1 deletion experimental/packages/otlp-transformer/src/trace/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Link } from '@opentelemetry/api';
import type { Link, SpanContext } from '@opentelemetry/api';
import { Resource } from '@opentelemetry/resources';
import type { ReadableSpan, TimedEvent } from '@opentelemetry/sdk-trace-base';
import type { Encoder } from '../common/utils';
Expand All @@ -34,6 +34,22 @@ import {
import { OtlpEncodingOptions } from '../common/internal-types';
import { getOtlpEncoder } from '../common/utils';

// Span flags constants matching the OTLP specification
const SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK = 0x100;
const SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK = 0x200;

/**
* Builds span flags based on the parent span context's isRemote property.
* This follows the OTLP specification for span flags.
*/
function buildSpanFlags(parentSpanContext?: SpanContext): number {
let flags = SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK;
if (parentSpanContext?.isRemote) {
flags |= SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK;
}
return flags;
Comment thread
nikhilmantri0902 marked this conversation as resolved.
Outdated
}

export function sdkSpanToOtlpSpan(span: ReadableSpan, encoder: Encoder): ISpan {
const ctx = span.spanContext();
const status = span.status;
Expand Down Expand Up @@ -61,6 +77,7 @@ export function sdkSpanToOtlpSpan(span: ReadableSpan, encoder: Encoder): ISpan {
},
links: span.links.map(link => toOtlpLink(link, encoder)),
droppedLinksCount: span.droppedLinksCount,
flags: buildSpanFlags(span.parentSpanContext),
};
}

Expand All @@ -71,6 +88,7 @@ export function toOtlpLink(link: Link, encoder: Encoder): ILink {
traceId: encoder.encodeSpanContext(link.context.traceId),
traceState: link.context.traceState?.serialize(),
droppedAttributesCount: link.droppedAttributesCount || 0,
flags: buildSpanFlags(link.context),
};
}

Expand Down
74 changes: 74 additions & 0 deletions experimental/packages/otlp-transformer/test/trace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ function createExpectedSpanJson(options: OtlpEncodingOptions) {
},
},
],
flags: 0x100, // SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
},
],
startTimeUnixNano: startTime,
Expand Down Expand Up @@ -129,6 +130,7 @@ function createExpectedSpanJson(options: OtlpEncodingOptions) {
code: EStatusCode.STATUS_CODE_OK,
message: undefined,
},
flags: 0x100, // SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
},
],
schemaUrl: 'http://url.to.schema',
Expand Down Expand Up @@ -187,6 +189,7 @@ function createExpectedSpanProtobuf() {
},
},
],
flags: 0x100, // SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
},
],
startTimeUnixNano: startTime,
Expand Down Expand Up @@ -218,6 +221,7 @@ function createExpectedSpanProtobuf() {
status: {
code: EStatusCode.STATUS_CODE_OK,
},
flags: 0x100, // SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
},
],
schemaUrl: 'http://url.to.schema',
Expand Down Expand Up @@ -574,4 +578,74 @@ describe('Trace', () => {
);
});
});

describe('span flags', () => {
it('sets flags to 0x100 for local parent span context', () => {
const exportRequest = createExportTraceServiceRequest([span], {
useHex: true,
});
assert.ok(exportRequest);
const spanFlags = exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].flags;
assert.strictEqual(spanFlags, 0x100); // SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
});

it('sets flags to 0x300 for remote parent span context', () => {
// Create a span with a remote parent context
const remoteParentSpanContext = {
spanId: '0000000000000001',
traceId: '00000000000000000000000000000001',
traceFlags: TraceFlags.SAMPLED,
isRemote: true, // This is the key difference
};

const spanWithRemoteParent = {
...span,
parentSpanContext: remoteParentSpanContext,
};

const exportRequest = createExportTraceServiceRequest([spanWithRemoteParent], {
useHex: true,
});
assert.ok(exportRequest);
const spanFlags = exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].flags;
assert.strictEqual(spanFlags, 0x300); // SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK | SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK
});

it('sets flags to 0x100 for links with local context', () => {
const exportRequest = createExportTraceServiceRequest([span], {
useHex: true,
});
assert.ok(exportRequest);
const linkFlags = exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].links?.[0].flags;
assert.strictEqual(linkFlags, 0x100); // SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
});

it('sets flags to 0x300 for links with remote context', () => {
// Create a span with a remote link context
const remoteLinkContext = {
spanId: '0000000000000003',
traceId: '00000000000000000000000000000002',
traceFlags: TraceFlags.SAMPLED,
isRemote: true, // This is the key difference
};

const remoteLink = {
context: remoteLinkContext,
attributes: { 'link-attribute': 'string value' },
droppedAttributesCount: 0,
};

const spanWithRemoteLink = {
...span,
links: [remoteLink],
};

const exportRequest = createExportTraceServiceRequest([spanWithRemoteLink], {
useHex: true,
});
assert.ok(exportRequest);
const linkFlags = exportRequest.resourceSpans?.[0].scopeSpans[0].spans?.[0].links?.[0].flags;
assert.strictEqual(linkFlags, 0x300); // SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK | SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK
});
});
});
Loading