cloudflared-mirror/tracing/identity.go

111 lines
2.9 KiB
Go

package tracing
import (
"bytes"
"encoding/binary"
"fmt"
"strconv"
"strings"
)
const (
// 16 bytes for tracing ID, 8 bytes for span ID and 1 byte for flags
IdentityLength = 16 + 8 + 1
)
type Identity struct {
// Based on https://www.jaegertracing.io/docs/1.36/client-libraries/#value
// parent span ID is always 0 for our case
traceIDUpper uint64
traceIDLower uint64
spanID uint64
flags uint8
}
// TODO: TUN-6604 Remove this. To reconstruct into Jaeger propagation format, convert tracingContext to tracing.Identity
func (tc *Identity) String() string {
return fmt.Sprintf("%016x%016x:%x:0:%x", tc.traceIDUpper, tc.traceIDLower, tc.spanID, tc.flags)
}
func (tc *Identity) MarshalBinary() ([]byte, error) {
buf := bytes.NewBuffer(make([]byte, 0, IdentityLength))
for _, field := range []interface{}{
tc.traceIDUpper,
tc.traceIDLower,
tc.spanID,
tc.flags,
} {
if err := binary.Write(buf, binary.BigEndian, field); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
func (tc *Identity) UnmarshalBinary(data []byte) error {
if len(data) < IdentityLength {
return fmt.Errorf("expect tracingContext to have at least %d bytes, got %d", IdentityLength, len(data))
}
buf := bytes.NewBuffer(data)
for _, field := range []interface{}{
&tc.traceIDUpper,
&tc.traceIDLower,
&tc.spanID,
&tc.flags,
} {
if err := binary.Read(buf, binary.BigEndian, field); err != nil {
return err
}
}
return nil
}
func NewIdentity(trace string) (*Identity, error) {
parts := strings.Split(trace, separator)
if len(parts) != 4 {
return nil, fmt.Errorf("trace '%s' doesn't have exactly 4 parts separated by %s", trace, separator)
}
const base = 16
tracingID, err := padTracingID(parts[0])
if err != nil {
return nil, err
}
traceIDUpper, err := strconv.ParseUint(tracingID[:16], base, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse first 16 bytes of tracing ID as uint64, err: %w", err)
}
traceIDLower, err := strconv.ParseUint(tracingID[16:], base, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse last 16 bytes of tracing ID as uint64, err: %w", err)
}
spanID, err := strconv.ParseUint(parts[1], base, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse span ID as uint64, err: %w", err)
}
flags, err := strconv.ParseUint(parts[3], base, 8)
if err != nil {
return nil, fmt.Errorf("failed to parse flag as uint8, err: %w", err)
}
return &Identity{
traceIDUpper: traceIDUpper,
traceIDLower: traceIDLower,
spanID: spanID,
flags: uint8(flags),
}, nil
}
func padTracingID(tracingID string) (string, error) {
if len(tracingID) == 0 {
return "", fmt.Errorf("missing tracing ID")
}
if len(tracingID) == traceID128bitsWidth {
return tracingID, nil
}
// Correctly left pad the trace to a length of 32
left := traceID128bitsWidth - len(tracingID)
paddedTracingID := strings.Repeat("0", left) + tracingID
return paddedTracingID, nil
}