2022-10-13 20:30:43 +00:00
|
|
|
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 {
|
2022-10-18 12:09:38 +00:00
|
|
|
return fmt.Sprintf("%016x%016x:%x:0:%x", tc.traceIDUpper, tc.traceIDLower, tc.spanID, tc.flags)
|
2022-10-13 20:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2022-10-18 12:09:38 +00:00
|
|
|
tracingID, err := padTracingID(parts[0])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2022-10-13 20:30:43 +00:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
2022-10-18 12:09:38 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|