@@ -97,6 +97,7 @@ function createExpectedSpanJson(options: OtlpEncodingOptions) {
9797 } ,
9898 } ,
9999 ] ,
100+ flags : 0x101 , // TraceFlags (0x01) | HAS_IS_REMOTE
100101 } ,
101102 ] ,
102103 startTimeUnixNano : startTime ,
@@ -129,6 +130,7 @@ function createExpectedSpanJson(options: OtlpEncodingOptions) {
129130 code : EStatusCode . STATUS_CODE_OK ,
130131 message : undefined ,
131132 } ,
133+ flags : 0x101 , // TraceFlags (0x01) | HAS_IS_REMOTE
132134 } ,
133135 ] ,
134136 schemaUrl : 'http://url.to.schema' ,
@@ -187,6 +189,7 @@ function createExpectedSpanProtobuf() {
187189 } ,
188190 } ,
189191 ] ,
192+ flags : 0x101 , // TraceFlags (0x01) | HAS_IS_REMOTE
190193 } ,
191194 ] ,
192195 startTimeUnixNano : startTime ,
@@ -218,6 +221,7 @@ function createExpectedSpanProtobuf() {
218221 status : {
219222 code : EStatusCode . STATUS_CODE_OK ,
220223 } ,
224+ flags : 0x101 , // TraceFlags (0x01) | HAS_IS_REMOTE
221225 } ,
222226 ] ,
223227 schemaUrl : 'http://url.to.schema' ,
@@ -580,4 +584,202 @@ describe('Trace', () => {
580584 ) ;
581585 } ) ;
582586 } ) ;
587+
588+ describe ( 'span flags' , ( ) => {
589+ it ( 'sets flags to 0x101 for local parent span context' , ( ) => {
590+ const exportRequest = createExportTraceServiceRequest ( [ span ] , {
591+ useHex : true ,
592+ } ) ;
593+ assert . ok ( exportRequest ) ;
594+ const spanFlags =
595+ exportRequest . resourceSpans ?. [ 0 ] . scopeSpans [ 0 ] . spans ?. [ 0 ] . flags ;
596+ assert . strictEqual ( spanFlags , 0x101 ) ; // TraceFlags (0x01) | HAS_IS_REMOTE
597+ } ) ;
598+
599+ it ( 'sets flags to 0x301 for remote parent span context' , ( ) => {
600+ // Create a span with a remote parent context
601+ const remoteParentSpanContext = {
602+ spanId : '0000000000000001' ,
603+ traceId : '00000000000000000000000000000001' ,
604+ traceFlags : TraceFlags . SAMPLED ,
605+ isRemote : true , // This is the key difference
606+ } ;
607+
608+ const spanWithRemoteParent = {
609+ ...span ,
610+ parentSpanContext : remoteParentSpanContext ,
611+ } ;
612+
613+ const exportRequest = createExportTraceServiceRequest (
614+ [ spanWithRemoteParent ] ,
615+ {
616+ useHex : true ,
617+ }
618+ ) ;
619+ assert . ok ( exportRequest ) ;
620+ const spanFlags =
621+ exportRequest . resourceSpans ?. [ 0 ] . scopeSpans [ 0 ] . spans ?. [ 0 ] . flags ;
622+ assert . strictEqual ( spanFlags , 0x301 ) ; // TraceFlags (0x01) | HAS_IS_REMOTE | IS_REMOTE
623+ } ) ;
624+
625+ it ( 'sets flags to 0x101 for links with local context' , ( ) => {
626+ const exportRequest = createExportTraceServiceRequest ( [ span ] , {
627+ useHex : true ,
628+ } ) ;
629+ assert . ok ( exportRequest ) ;
630+ const linkFlags =
631+ exportRequest . resourceSpans ?. [ 0 ] . scopeSpans [ 0 ] . spans ?. [ 0 ] . links ?. [ 0 ]
632+ . flags ;
633+ assert . strictEqual ( linkFlags , 0x101 ) ; // TraceFlags (0x01) | HAS_IS_REMOTE
634+ } ) ;
635+
636+ it ( 'sets flags to 0x301 for links with remote context' , ( ) => {
637+ // Create a span with a remote link context
638+ const remoteLinkContext = {
639+ spanId : '0000000000000003' ,
640+ traceId : '00000000000000000000000000000002' ,
641+ traceFlags : TraceFlags . SAMPLED ,
642+ isRemote : true , // This is the key difference
643+ } ;
644+
645+ const remoteLink = {
646+ context : remoteLinkContext ,
647+ attributes : { 'link-attribute' : 'string value' } ,
648+ droppedAttributesCount : 0 ,
649+ } ;
650+
651+ const spanWithRemoteLink = {
652+ ...span ,
653+ links : [ remoteLink ] ,
654+ } ;
655+
656+ const exportRequest = createExportTraceServiceRequest (
657+ [ spanWithRemoteLink ] ,
658+ {
659+ useHex : true ,
660+ }
661+ ) ;
662+ assert . ok ( exportRequest ) ;
663+ const linkFlags =
664+ exportRequest . resourceSpans ?. [ 0 ] . scopeSpans [ 0 ] . spans ?. [ 0 ] . links ?. [ 0 ]
665+ . flags ;
666+ assert . strictEqual ( linkFlags , 0x301 ) ; // TraceFlags (0x01) | HAS_IS_REMOTE | IS_REMOTE
667+ } ) ;
668+ } ) ;
669+
670+ describe ( 'span/link flags matrix' , ( ) => {
671+ const cases = [
672+ { tf : 0x00 , local : 0x100 , remote : 0x300 } ,
673+ { tf : 0x01 , local : 0x101 , remote : 0x301 } ,
674+ { tf : 0x05 , local : 0x105 , remote : 0x305 } ,
675+ { tf : 0xff , local : 0x1ff , remote : 0x3ff } ,
676+ ] ;
677+
678+ it ( 'composes span flags with local and remote parent across traceFlags' , ( ) => {
679+ const baseCtx = span . spanContext ( ) ;
680+ for ( const c of cases ) {
681+ // Local parent
682+ const spanLocal = {
683+ ...span ,
684+ spanContext : ( ) => ( {
685+ spanId : baseCtx . spanId ,
686+ traceId : baseCtx . traceId ,
687+ traceFlags : c . tf ,
688+ isRemote : false ,
689+ traceState : baseCtx . traceState ,
690+ } ) ,
691+ parentSpanContext : {
692+ ...span . parentSpanContext ,
693+ isRemote : false ,
694+ } ,
695+ } as unknown as ReadableSpan ;
696+ const reqLocal = createExportTraceServiceRequest ( [ spanLocal ] , {
697+ useHex : true ,
698+ } ) ;
699+ const spanFlagsLocal =
700+ reqLocal . resourceSpans ?. [ 0 ] . scopeSpans [ 0 ] . spans ?. [ 0 ] . flags ;
701+ assert . strictEqual ( spanFlagsLocal , c . local ) ;
702+
703+ // Remote parent
704+ const spanRemote = {
705+ ...spanLocal ,
706+ parentSpanContext : {
707+ ...span . parentSpanContext ,
708+ isRemote : true ,
709+ } ,
710+ } as unknown as ReadableSpan ;
711+ const reqRemote = createExportTraceServiceRequest ( [ spanRemote ] , {
712+ useHex : true ,
713+ } ) ;
714+ const spanFlagsRemote =
715+ reqRemote . resourceSpans ?. [ 0 ] . scopeSpans [ 0 ] . spans ?. [ 0 ] . flags ;
716+ assert . strictEqual ( spanFlagsRemote , c . remote ) ;
717+ }
718+ } ) ;
719+
720+ it ( 'composes link flags with local and remote context across traceFlags' , ( ) => {
721+ for ( const c of cases ) {
722+ const linkLocal = {
723+ context : {
724+ spanId : '0000000000000003' ,
725+ traceId : '00000000000000000000000000000002' ,
726+ traceFlags : c . tf ,
727+ isRemote : false ,
728+ traceState : new TraceState ( 'link=foo' ) ,
729+ } ,
730+ attributes : { 'link-attribute' : 'string value' } ,
731+ droppedAttributesCount : 0 ,
732+ } ;
733+ const spanWithLocalLink = {
734+ ...span ,
735+ links : [ linkLocal ] ,
736+ } as unknown as ReadableSpan ;
737+ const reqLocal = createExportTraceServiceRequest ( [ spanWithLocalLink ] , {
738+ useHex : true ,
739+ } ) ;
740+ const linkFlagsLocal =
741+ reqLocal . resourceSpans ?. [ 0 ] . scopeSpans [ 0 ] . spans ?. [ 0 ] . links ?. [ 0 ] . flags ;
742+ assert . strictEqual ( linkFlagsLocal , c . local ) ;
743+
744+ const linkRemote = {
745+ ...linkLocal ,
746+ context : { ...linkLocal . context , isRemote : true } ,
747+ } ;
748+ const spanWithRemoteLink = {
749+ ...span ,
750+ links : [ linkRemote ] ,
751+ } as unknown as ReadableSpan ;
752+ const reqRemote = createExportTraceServiceRequest (
753+ [ spanWithRemoteLink ] ,
754+ { useHex : true }
755+ ) ;
756+ const linkFlagsRemote =
757+ reqRemote . resourceSpans ?. [ 0 ] . scopeSpans [ 0 ] . spans ?. [ 0 ] . links ?. [ 0 ]
758+ . flags ;
759+ assert . strictEqual ( linkFlagsRemote , c . remote ) ;
760+ }
761+ } ) ;
762+
763+ it ( 'composes root span flags across traceFlags (no parent)' , ( ) => {
764+ const baseCtx = span . spanContext ( ) ;
765+ for ( const c of cases ) {
766+ const rootSpan = {
767+ ...span ,
768+ spanContext : ( ) => ( {
769+ spanId : baseCtx . spanId ,
770+ traceId : baseCtx . traceId ,
771+ traceFlags : c . tf ,
772+ isRemote : false ,
773+ traceState : baseCtx . traceState ,
774+ } ) ,
775+ parentSpanContext : undefined ,
776+ } as unknown as ReadableSpan ;
777+ const req = createExportTraceServiceRequest ( [ rootSpan ] , {
778+ useHex : true ,
779+ } ) ;
780+ const flags = req . resourceSpans ?. [ 0 ] . scopeSpans [ 0 ] . spans ?. [ 0 ] . flags ;
781+ assert . strictEqual ( flags , c . local ) ;
782+ }
783+ } ) ;
784+ } ) ;
583785} ) ;
0 commit comments