package propagation
import (
"context"
"encoding/hex"
"fmt"
"strings"
"go.opentelemetry.io/otel/trace"
)
const (
supportedVersion = 0
maxVersion = 254
traceparentHeader = "traceparent"
tracestateHeader = "tracestate"
delimiter = "-"
)
type TraceContext struct {}
var (
_ TextMapPropagator = TraceContext {}
versionPart = fmt .Sprintf ("%.2X" , supportedVersion )
)
func (TraceContext ) Inject (ctx context .Context , carrier TextMapCarrier ) {
sc := trace .SpanContextFromContext (ctx )
if !sc .IsValid () {
return
}
if ts := sc .TraceState ().String (); ts != "" {
carrier .Set (tracestateHeader , ts )
}
flags := sc .TraceFlags () & trace .FlagsSampled
var sb strings .Builder
sb .Grow (2 + 32 + 16 + 2 + 3 )
_, _ = sb .WriteString (versionPart )
traceID := sc .TraceID ()
spanID := sc .SpanID ()
flagByte := [1 ]byte {byte (flags )}
var buf [32 ]byte
for _ , src := range [][]byte {traceID [:], spanID [:], flagByte [:]} {
_ = sb .WriteByte (delimiter [0 ])
n := hex .Encode (buf [:], src )
_, _ = sb .Write (buf [:n ])
}
carrier .Set (traceparentHeader , sb .String ())
}
func (tc TraceContext ) Extract (ctx context .Context , carrier TextMapCarrier ) context .Context {
sc := tc .extract (carrier )
if !sc .IsValid () {
return ctx
}
return trace .ContextWithRemoteSpanContext (ctx , sc )
}
func (TraceContext ) extract (carrier TextMapCarrier ) trace .SpanContext {
h := carrier .Get (traceparentHeader )
if h == "" {
return trace .SpanContext {}
}
var ver [1 ]byte
if !extractPart (ver [:], &h , 2 ) {
return trace .SpanContext {}
}
version := int (ver [0 ])
if version > maxVersion {
return trace .SpanContext {}
}
var scc trace .SpanContextConfig
if !extractPart (scc .TraceID [:], &h , 32 ) {
return trace .SpanContext {}
}
if !extractPart (scc .SpanID [:], &h , 16 ) {
return trace .SpanContext {}
}
var opts [1 ]byte
if !extractPart (opts [:], &h , 2 ) {
return trace .SpanContext {}
}
if version == 0 && (h != "" || opts [0 ] > 2 ) {
return trace .SpanContext {}
}
scc .TraceFlags = trace .TraceFlags (opts [0 ]) & trace .FlagsSampled
scc .TraceState , _ = trace .ParseTraceState (carrier .Get (tracestateHeader ))
scc .Remote = true
sc := trace .NewSpanContext (scc )
if !sc .IsValid () {
return trace .SpanContext {}
}
return sc
}
func upperHex(v string ) bool {
for _ , c := range v {
if c >= 'A' && c <= 'F' {
return true
}
}
return false
}
func extractPart(dst []byte , h *string , n int ) bool {
part , left , _ := strings .Cut (*h , delimiter )
*h = left
if len (part ) != n || upperHex (part ) {
return false
}
if p , err := hex .Decode (dst , []byte (part )); err != nil || p != n /2 {
return false
}
return true
}
func (TraceContext ) Fields () []string {
return []string {traceparentHeader , tracestateHeader }
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .