1414 limitations under the License.
1515*/
1616
17- // Package tracing relays OTel spans from a VM to the host.
1817package tracing
1918
2019import (
21- "bytes"
2220 "context"
23- "fmt "
21+ "encoding/hex "
2422 "net/http"
23+ "strconv"
2524 "time"
2625
2726 "github.com/containerd/log"
28- collectorpb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
29- commonpb "go.opentelemetry.io/proto/otlp/common/v1"
30- resourcepb "go.opentelemetry.io/proto/otlp/resource/v1"
31- tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
32- "google.golang.org/protobuf/proto"
3327
3428 tracespb "github.com/containerd/nerdbox/api/services/traces/v1"
3529)
3630
3731// ForwardTraces reads spans from the VM trace stream and exports them
38- // to the OTLP endpoint. hostBootTime is the host wall-clock time captured
39- // when ttrpc became responsive, used to correct VM-vs-host clock skew.
32+ // to the OTLP endpoint as JSON . hostBootTime is the host wall-clock time
33+ // captured when ttrpc became responsive, used to correct VM-vs-host clock skew.
4034func ForwardTraces (ctx context.Context , stream tracespb.TTRPCTraces_StreamClient , endpoint string , hostBootTime time.Time ) {
4135 client := & http.Client {}
4236
4337 // The VM's RTC has only second-level resolution, so its wall clock
4438 // can be up to ~1s behind the host. We compute the offset from the
45- // first otelttrpc interceptor span (which is created at the moment
46- // the first ttrpc RPC reaches the VM — a known sync point with the
47- // host). hostBootTime was captured on the host at the same logical
48- // moment (when ttrpc became responsive).
39+ // first interceptor span (which is created at the moment the first
40+ // ttrpc RPC reaches the VM — a known sync point with the host).
41+ // hostBootTime was captured on the host at the same logical moment
42+ // (when ttrpc became responsive).
4943 var clockOffset time.Duration
5044 offsetComputed := false
5145
@@ -63,70 +57,50 @@ func ForwardTraces(ctx context.Context, stream tracespb.TTRPCTraces_StreamClient
6357 log .G (ctx ).WithField ("offset" , clockOffset ).Debug ("VM clock offset computed" )
6458 }
6559
66- if err := exportSpan (ctx , client , endpoint , span , clockOffset ); err != nil {
60+ if err := exportVMSpan (ctx , client , endpoint , span , clockOffset ); err != nil {
6761 log .G (ctx ).WithError (err ).Warn ("trace relay export" )
6862 }
6963 }
7064}
7165
72- func exportSpan (ctx context.Context , client * http.Client , endpoint string , s * tracespb.Span , clockOffset time.Duration ) error {
66+ func exportVMSpan (ctx context.Context , client * http.Client , endpoint string , s * tracespb.Span , clockOffset time.Duration ) error {
7367 startNano := time .Unix (0 , s .StartTimeUnixNano ).Add (clockOffset ).UnixNano ()
7468 endNano := time .Unix (0 , s .EndTimeUnixNano ).Add (clockOffset ).UnixNano ()
7569
76- span := & tracepb. Span {
77- TraceId : s .TraceID ,
78- SpanId : s .SpanID ,
79- ParentSpanId : s .ParentSpanID ,
70+ span := otlpSpan {
71+ TraceID : hex . EncodeToString ( s .TraceID ) ,
72+ SpanID : hex . EncodeToString ( s .SpanID ) ,
73+ ParentSpanID : hex . EncodeToString ( s .ParentSpanID ) ,
8074 Name : s .Name ,
81- Kind : tracepb . Span_SpanKind (s .Kind ),
82- StartTimeUnixNano : uint64 (startNano ),
83- EndTimeUnixNano : uint64 (endNano ),
84- Status : & tracepb. Status {
85- Code : tracepb . Status_StatusCode (s .StatusCode ),
75+ Kind : int (s .Kind ),
76+ StartTimeUnixNano : strconv . FormatInt (startNano , 10 ),
77+ EndTimeUnixNano : strconv . FormatInt (endNano , 10 ),
78+ Status : otlpStatus {
79+ Code : int (s .StatusCode ),
8680 Message : s .StatusMessage ,
8781 },
8882 }
8983
9084 for _ , kv := range s .Attributes {
91- span .Attributes = append (span .Attributes , & commonpb. KeyValue {
85+ span .Attributes = append (span .Attributes , otlpKeyValue {
9286 Key : kv .Key ,
93- Value : & commonpb. AnyValue { Value : & commonpb. AnyValue_StringValue { StringValue : kv .Value } },
87+ Value : otlpAnyValue { StringValue : kv .Value },
9488 })
9589 }
9690
97- req := & collectorpb. ExportTraceServiceRequest {
98- ResourceSpans : []* tracepb. ResourceSpans {{
99- Resource : & resourcepb. Resource {
100- Attributes : []* commonpb. KeyValue {{
91+ req := otlpExportRequest {
92+ ResourceSpans : []otlpResourceSpans {{
93+ Resource : otlpResource {
94+ Attributes : []otlpKeyValue {{
10195 Key : "service.name" ,
102- Value : & commonpb. AnyValue { Value : & commonpb. AnyValue_StringValue { StringValue : "nerdbox-vm" } },
96+ Value : otlpAnyValue { StringValue : "nerdbox-vm" },
10397 }},
10498 },
105- ScopeSpans : []* tracepb. ScopeSpans {{
106- Spans : []* tracepb. Span {span },
99+ ScopeSpans : []otlpScopeSpans {{
100+ Spans : []otlpSpan {span },
107101 }},
108102 }},
109103 }
110104
111- data , err := proto .Marshal (req )
112- if err != nil {
113- return fmt .Errorf ("marshal OTLP request: %w" , err )
114- }
115-
116- httpReq , err := http .NewRequestWithContext (ctx , http .MethodPost , endpoint , bytes .NewReader (data ))
117- if err != nil {
118- return fmt .Errorf ("create HTTP request: %w" , err )
119- }
120- httpReq .Header .Set ("Content-Type" , "application/x-protobuf" )
121-
122- resp , err := client .Do (httpReq )
123- if err != nil {
124- return fmt .Errorf ("send OTLP request: %w" , err )
125- }
126- resp .Body .Close ()
127-
128- if resp .StatusCode >= 400 {
129- return fmt .Errorf ("OTLP export failed: %s" , resp .Status )
130- }
131- return nil
105+ return postOTLP (ctx , client , endpoint , req )
132106}
0 commit comments