mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-11-28 08:38:51 +02:00
api(trace): change SpanID to byte array (#241)
* api(trace): change SpanID to byte array * fix doc and create const errors
This commit is contained in:
parent
d9c4aa5cee
commit
4e545e2ab8
@ -18,8 +18,6 @@ import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -30,25 +28,89 @@ const (
|
||||
// for SpanContext TraceFlags field when a trace is sampled.
|
||||
TraceFlagsSampled = traceFlagsBitMaskSampled
|
||||
TraceFlagsUnused = traceFlagsBitMaskUnused
|
||||
|
||||
ErrInvalidHexID errorConst = "trace-id and span-id can only contain [0-9a-f] characters, all lowercase"
|
||||
|
||||
ErrInvalidTraceIDLength errorConst = "hex encoded trace-id must have length equals to 32"
|
||||
ErrNilTraceID errorConst = "trace-id can't be all zero"
|
||||
|
||||
ErrInvalidSpanIDLength errorConst = "hex encoded span-id must have length equals to 16"
|
||||
ErrNilSpanID errorConst = "span-id can't be all zero"
|
||||
)
|
||||
|
||||
type errorConst string
|
||||
|
||||
func (e errorConst) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
type TraceID [16]byte
|
||||
|
||||
var nilTraceID TraceID
|
||||
var _ json.Marshaler = nilTraceID
|
||||
|
||||
func (t TraceID) isValid() bool {
|
||||
func (t TraceID) IsValid() bool {
|
||||
return !bytes.Equal(t[:], nilTraceID[:])
|
||||
}
|
||||
|
||||
// MarshalJSON implements a custom marshal function to encode TraceID as a hex string
|
||||
func (t TraceID) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(hex.EncodeToString(t[:]))
|
||||
}
|
||||
|
||||
type SpanID [8]byte
|
||||
|
||||
var nilSpanID SpanID
|
||||
var _ json.Marshaler = nilSpanID
|
||||
|
||||
func (s SpanID) IsValid() bool {
|
||||
return !bytes.Equal(s[:], nilSpanID[:])
|
||||
}
|
||||
|
||||
// MarshalJSON implements a custom marshal function to encode SpanID as a hex string
|
||||
func (s SpanID) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(hex.EncodeToString(s[:]))
|
||||
}
|
||||
|
||||
// TraceIDFromHex returns a TraceID from a hex string if it is compliant
|
||||
// with the w3c trace-context specification.
|
||||
// See more at https://www.w3.org/TR/trace-context/#trace-id
|
||||
func TraceIDFromHex(h string) (TraceID, error) {
|
||||
t := TraceID{}
|
||||
if len(h) != 32 {
|
||||
return t, errors.New("hex encoded trace-id must have length equals to 32")
|
||||
return t, ErrInvalidTraceIDLength
|
||||
}
|
||||
|
||||
if err := decodeHex(h, t[:]); err != nil {
|
||||
return t, err
|
||||
}
|
||||
|
||||
if !t.IsValid() {
|
||||
return t, ErrNilTraceID
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// SpanIDFromHex returns a SpanID from a hex string if it is compliant
|
||||
// with the w3c trace-context specification.
|
||||
// See more at https://www.w3.org/TR/trace-context/#parent-id
|
||||
func SpanIDFromHex(h string) (SpanID, error) {
|
||||
s := SpanID{}
|
||||
if len(h) != 16 {
|
||||
return s, ErrInvalidSpanIDLength
|
||||
}
|
||||
|
||||
if err := decodeHex(h, s[:]); err != nil {
|
||||
return s, err
|
||||
}
|
||||
|
||||
if !s.IsValid() {
|
||||
return s, ErrNilSpanID
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func decodeHex(h string, b []byte) error {
|
||||
for _, r := range h {
|
||||
switch {
|
||||
case 'a' <= r && r <= 'f':
|
||||
@ -56,65 +118,45 @@ func TraceIDFromHex(h string) (TraceID, error) {
|
||||
case '0' <= r && r <= '9':
|
||||
continue
|
||||
default:
|
||||
return t, errors.New("trace-id can only contain [0-9a-f] characters, all lowercase")
|
||||
return ErrInvalidHexID
|
||||
}
|
||||
}
|
||||
|
||||
b, err := hex.DecodeString(h)
|
||||
decoded, err := hex.DecodeString(h)
|
||||
if err != nil {
|
||||
return t, err
|
||||
return err
|
||||
}
|
||||
copy(t[:], b)
|
||||
|
||||
if !t.isValid() {
|
||||
return t, errors.New("trace-id can't be all zero")
|
||||
}
|
||||
return t, nil
|
||||
copy(b[:], decoded)
|
||||
return nil
|
||||
}
|
||||
|
||||
type SpanContext struct {
|
||||
TraceID TraceID
|
||||
SpanID uint64
|
||||
SpanID SpanID
|
||||
TraceFlags byte
|
||||
}
|
||||
|
||||
var _ json.Marshaler = (*SpanContext)(nil)
|
||||
|
||||
// EmptySpanContext is meant for internal use to return invalid span context during error conditions.
|
||||
func EmptySpanContext() SpanContext {
|
||||
return SpanContext{}
|
||||
}
|
||||
|
||||
// MarshalJSON implements a custom marshal function to encode SpanContext
|
||||
// in a human readable format with hex encoded TraceID and SpanID.
|
||||
func (sc SpanContext) MarshalJSON() ([]byte, error) {
|
||||
type JSONSpanContext struct {
|
||||
TraceID string
|
||||
SpanID string
|
||||
TraceFlags byte
|
||||
}
|
||||
|
||||
return json.Marshal(JSONSpanContext{
|
||||
TraceID: sc.TraceIDString(),
|
||||
SpanID: sc.SpanIDString(),
|
||||
TraceFlags: sc.TraceFlags,
|
||||
})
|
||||
}
|
||||
|
||||
func (sc SpanContext) IsValid() bool {
|
||||
return sc.HasTraceID() && sc.HasSpanID()
|
||||
}
|
||||
|
||||
func (sc SpanContext) HasTraceID() bool {
|
||||
return sc.TraceID.isValid()
|
||||
return sc.TraceID.IsValid()
|
||||
}
|
||||
|
||||
func (sc SpanContext) HasSpanID() bool {
|
||||
return sc.SpanID != 0
|
||||
return sc.SpanID.IsValid()
|
||||
}
|
||||
|
||||
func (sc SpanContext) SpanIDString() string {
|
||||
return fmt.Sprintf("%.16x", sc.SpanID)
|
||||
return hex.EncodeToString(sc.SpanID[:])
|
||||
|
||||
}
|
||||
|
||||
func (sc SpanContext) TraceIDString() string {
|
||||
|
@ -24,28 +24,28 @@ func TestIsValid(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
name string
|
||||
tid core.TraceID
|
||||
sid uint64
|
||||
sid core.SpanID
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "SpanContext.IsValid() returns true if sc has both an Trace ID and Span ID",
|
||||
tid: core.TraceID([16]byte{1}),
|
||||
sid: uint64(42),
|
||||
tid: [16]byte{1},
|
||||
sid: [8]byte{42},
|
||||
want: true,
|
||||
}, {
|
||||
name: "SpanContext.IsValid() returns false if sc has neither an Trace ID nor Span ID",
|
||||
tid: core.TraceID([16]byte{}),
|
||||
sid: uint64(0),
|
||||
sid: [8]byte{},
|
||||
want: false,
|
||||
}, {
|
||||
name: "SpanContext.IsValid() returns false if sc has a Span ID but not a Trace ID",
|
||||
tid: core.TraceID([16]byte{}),
|
||||
sid: uint64(42),
|
||||
sid: [8]byte{42},
|
||||
want: false,
|
||||
}, {
|
||||
name: "SpanContext.IsValid() returns false if sc has a Trace ID but not a Span ID",
|
||||
tid: core.TraceID([16]byte{1}),
|
||||
sid: uint64(0),
|
||||
sid: [8]byte{},
|
||||
want: false,
|
||||
},
|
||||
} {
|
||||
@ -141,7 +141,7 @@ func TestHasSpanID(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "SpanContext.HasSpanID() returns true if self.SpanID != 0",
|
||||
sc: core.SpanContext{SpanID: uint64(42)},
|
||||
sc: core.SpanContext{SpanID: [8]byte{42}},
|
||||
want: true,
|
||||
}, {
|
||||
name: "SpanContext.HasSpanID() returns false if self.SpanID == 0",
|
||||
@ -167,8 +167,8 @@ func TestSpanIDString(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "SpanContext.SpanIDString returns string representation of self.TraceID values > 0",
|
||||
sc: core.SpanContext{SpanID: uint64(42)},
|
||||
want: `000000000000002a`,
|
||||
sc: core.SpanContext{SpanID: [8]byte{42}},
|
||||
want: `2a00000000000000`,
|
||||
}, {
|
||||
name: "SpanContext.SpanIDString returns string representation of self.TraceID values == 0",
|
||||
sc: core.SpanContext{},
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@ -512,7 +511,7 @@ func (t *BridgeTracer) Inject(sm ot.SpanContext, format interface{}, carrier int
|
||||
return ot.ErrInvalidCarrier
|
||||
}
|
||||
hhcarrier.Set(traceIDHeader, bridgeSC.otelSpanContext.TraceIDString())
|
||||
hhcarrier.Set(spanIDHeader, spanIDToString(bridgeSC.otelSpanContext.SpanID))
|
||||
hhcarrier.Set(spanIDHeader, bridgeSC.otelSpanContext.SpanIDString())
|
||||
hhcarrier.Set(traceFlagsHeader, traceFlagsToString(bridgeSC.otelSpanContext.TraceFlags))
|
||||
bridgeSC.ForeachBaggageItem(func(k, v string) bool {
|
||||
// we assume that keys are already canonicalized
|
||||
@ -522,10 +521,6 @@ func (t *BridgeTracer) Inject(sm ot.SpanContext, format interface{}, carrier int
|
||||
return nil
|
||||
}
|
||||
|
||||
func spanIDToString(spanID uint64) string {
|
||||
return fmt.Sprintf("%.16x", spanID)
|
||||
}
|
||||
|
||||
func traceFlagsToString(opts byte) string {
|
||||
var parts []string
|
||||
if opts&otelcore.TraceFlagsSampled == otelcore.TraceFlagsSampled {
|
||||
@ -557,7 +552,7 @@ func (t *BridgeTracer) Extract(format interface{}, carrier interface{}) (ot.Span
|
||||
}
|
||||
bridgeSC.otelSpanContext.TraceID = traceID
|
||||
case spanIDHeader:
|
||||
spanID, err := spanIDFromString(v)
|
||||
spanID, err := otelcore.SpanIDFromHex(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -581,13 +576,6 @@ func (t *BridgeTracer) Extract(format interface{}, carrier interface{}) (ot.Span
|
||||
return bridgeSC, nil
|
||||
}
|
||||
|
||||
func spanIDFromString(s string) (uint64, error) {
|
||||
if len(s) != 16 {
|
||||
return 0, fmt.Errorf("invalid span ID")
|
||||
}
|
||||
return strconv.ParseUint(s, 16, 64)
|
||||
}
|
||||
|
||||
func stringToTraceFlags(s string) byte {
|
||||
var opts byte
|
||||
for _, part := range strings.Split(s, ",") {
|
||||
|
@ -47,7 +47,7 @@ type MockTracer struct {
|
||||
Resources oteldctx.Map
|
||||
FinishedSpans []*MockSpan
|
||||
SpareTraceIDs []otelcore.TraceID
|
||||
SpareSpanIDs []uint64
|
||||
SpareSpanIDs []otelcore.SpanID
|
||||
SpareContextKeyValues []MockContextKeyValue
|
||||
|
||||
randLock sync.Mutex
|
||||
@ -153,11 +153,11 @@ func (t *MockTracer) getTraceID(ctx context.Context, spanOpts *oteltrace.SpanOpt
|
||||
return t.getRandTraceID()
|
||||
}
|
||||
|
||||
func (t *MockTracer) getParentSpanID(ctx context.Context, spanOpts *oteltrace.SpanOptions) uint64 {
|
||||
func (t *MockTracer) getParentSpanID(ctx context.Context, spanOpts *oteltrace.SpanOptions) otelcore.SpanID {
|
||||
if parent := t.getParentSpanContext(ctx, spanOpts); parent.IsValid() {
|
||||
return parent.SpanID
|
||||
}
|
||||
return 0
|
||||
return otelcore.SpanID{}
|
||||
}
|
||||
|
||||
func (t *MockTracer) getParentSpanContext(ctx context.Context, spanOpts *oteltrace.SpanOptions) otelcore.SpanContext {
|
||||
@ -171,7 +171,7 @@ func (t *MockTracer) getParentSpanContext(ctx context.Context, spanOpts *oteltra
|
||||
return otelcore.EmptySpanContext()
|
||||
}
|
||||
|
||||
func (t *MockTracer) getSpanID() uint64 {
|
||||
func (t *MockTracer) getSpanID() otelcore.SpanID {
|
||||
if len(t.SpareSpanIDs) > 0 {
|
||||
spanID := t.SpareSpanIDs[0]
|
||||
t.SpareSpanIDs = t.SpareSpanIDs[1:]
|
||||
@ -180,13 +180,17 @@ func (t *MockTracer) getSpanID() uint64 {
|
||||
}
|
||||
return spanID
|
||||
}
|
||||
return t.getRandUint64()
|
||||
return t.getRandSpanID()
|
||||
}
|
||||
|
||||
func (t *MockTracer) getRandUint64() uint64 {
|
||||
func (t *MockTracer) getRandSpanID() otelcore.SpanID {
|
||||
t.randLock.Lock()
|
||||
defer t.randLock.Unlock()
|
||||
return t.rand.Uint64()
|
||||
|
||||
sid := otelcore.SpanID{}
|
||||
t.rand.Read(sid[:])
|
||||
|
||||
return sid
|
||||
}
|
||||
|
||||
func (t *MockTracer) getRandTraceID() otelcore.TraceID {
|
||||
@ -220,7 +224,7 @@ type MockSpan struct {
|
||||
Attributes oteldctx.Map
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
ParentSpanID uint64
|
||||
ParentSpanID otelcore.SpanID
|
||||
Events []MockEvent
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ func TestMixedAPIs(t *testing.T) {
|
||||
|
||||
type simpleTest struct {
|
||||
traceID otelcore.TraceID
|
||||
spanIDs []uint64
|
||||
spanIDs []otelcore.SpanID
|
||||
}
|
||||
|
||||
func newSimpleTest() *simpleTest {
|
||||
@ -166,10 +166,10 @@ func (st *simpleTest) noop(t *testing.T, ctx context.Context) {
|
||||
|
||||
type currentActiveSpanTest struct {
|
||||
traceID otelcore.TraceID
|
||||
spanIDs []uint64
|
||||
spanIDs []otelcore.SpanID
|
||||
|
||||
recordedCurrentOtelSpanIDs []uint64
|
||||
recordedActiveOTSpanIDs []uint64
|
||||
recordedCurrentOtelSpanIDs []otelcore.SpanID
|
||||
recordedActiveOTSpanIDs []otelcore.SpanID
|
||||
}
|
||||
|
||||
func newCurrentActiveSpanTest() *currentActiveSpanTest {
|
||||
@ -220,7 +220,7 @@ func (cast *currentActiveSpanTest) recordSpans(t *testing.T, ctx context.Context
|
||||
spanID := oteltrace.CurrentSpan(ctx).SpanContext().SpanID
|
||||
cast.recordedCurrentOtelSpanIDs = append(cast.recordedCurrentOtelSpanIDs, spanID)
|
||||
|
||||
spanID = 0
|
||||
spanID = otelcore.SpanID{}
|
||||
if bridgeSpan, ok := ot.SpanFromContext(ctx).(*bridgeSpan); ok {
|
||||
spanID = bridgeSpan.otelSpan.SpanContext().SpanID
|
||||
}
|
||||
@ -466,23 +466,23 @@ func (tm *tracerMessTest) recordTracers(t *testing.T, ctx context.Context) {
|
||||
|
||||
// helpers
|
||||
|
||||
func checkTraceAndSpans(t *testing.T, tracer *internal.MockTracer, expectedTraceID otelcore.TraceID, expectedSpanIDs []uint64) {
|
||||
func checkTraceAndSpans(t *testing.T, tracer *internal.MockTracer, expectedTraceID otelcore.TraceID, expectedSpanIDs []otelcore.SpanID) {
|
||||
expectedSpanCount := len(expectedSpanIDs)
|
||||
|
||||
// reverse spanIDs, since first span ID belongs to root, that
|
||||
// finishes last
|
||||
spanIDs := make([]uint64, len(expectedSpanIDs))
|
||||
spanIDs := make([]otelcore.SpanID, len(expectedSpanIDs))
|
||||
copy(spanIDs, expectedSpanIDs)
|
||||
reverse(len(spanIDs), func(i, j int) {
|
||||
spanIDs[i], spanIDs[j] = spanIDs[j], spanIDs[i]
|
||||
})
|
||||
// the last finished span has no parent
|
||||
parentSpanIDs := append(spanIDs[1:], 0)
|
||||
parentSpanIDs := append(spanIDs[1:], otelcore.SpanID{})
|
||||
|
||||
sks := map[uint64]oteltrace.SpanKind{
|
||||
3456: oteltrace.SpanKindProducer,
|
||||
2345: oteltrace.SpanKindInternal,
|
||||
1234: oteltrace.SpanKindClient,
|
||||
sks := map[otelcore.SpanID]oteltrace.SpanKind{
|
||||
{125}: oteltrace.SpanKindProducer,
|
||||
{124}: oteltrace.SpanKindInternal,
|
||||
{123}: oteltrace.SpanKindClient,
|
||||
}
|
||||
|
||||
if len(tracer.FinishedSpans) != expectedSpanCount {
|
||||
@ -517,23 +517,16 @@ func simpleTraceID() otelcore.TraceID {
|
||||
return [16]byte{123, 42}
|
||||
}
|
||||
|
||||
func simpleSpanIDs(count int) []uint64 {
|
||||
base := []uint64{
|
||||
1234,
|
||||
2345,
|
||||
3456,
|
||||
4567,
|
||||
5678,
|
||||
6789,
|
||||
func simpleSpanIDs(count int) []otelcore.SpanID {
|
||||
base := []otelcore.SpanID{
|
||||
{123},
|
||||
{124},
|
||||
{125},
|
||||
{126},
|
||||
{127},
|
||||
{128},
|
||||
}
|
||||
if count <= len(base) {
|
||||
return base[:count]
|
||||
}
|
||||
count -= len(base)
|
||||
for i := 0; i < count; i++ {
|
||||
base = append(base, base[i]*10)
|
||||
}
|
||||
return base
|
||||
return base[:count]
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
|
@ -199,7 +199,7 @@ func spanDataToThrift(data *export.SpanData) *gen.Span {
|
||||
refs = append(refs, &gen.SpanRef{
|
||||
TraceIdHigh: int64(binary.BigEndian.Uint64(link.TraceID[0:8])),
|
||||
TraceIdLow: int64(binary.BigEndian.Uint64(link.TraceID[8:16])),
|
||||
SpanId: int64(link.SpanID),
|
||||
SpanId: int64(binary.BigEndian.Uint64(link.SpanID[:])),
|
||||
// TODO(paivagustavo): properly set the reference type when specs are defined
|
||||
// see https://github.com/open-telemetry/opentelemetry-specification/issues/65
|
||||
RefType: gen.SpanRefType_CHILD_OF,
|
||||
@ -209,8 +209,8 @@ func spanDataToThrift(data *export.SpanData) *gen.Span {
|
||||
return &gen.Span{
|
||||
TraceIdHigh: int64(binary.BigEndian.Uint64(data.SpanContext.TraceID[0:8])),
|
||||
TraceIdLow: int64(binary.BigEndian.Uint64(data.SpanContext.TraceID[8:16])),
|
||||
SpanId: int64(data.SpanContext.SpanID),
|
||||
ParentSpanId: int64(data.ParentSpanID),
|
||||
SpanId: int64(binary.BigEndian.Uint64(data.SpanContext.SpanID[:])),
|
||||
ParentSpanId: int64(binary.BigEndian.Uint64(data.ParentSpanID[:])),
|
||||
OperationName: data.Name, // TODO: if span kind is added then add prefix "Sent"/"Recv"
|
||||
Flags: int32(data.SpanContext.TraceFlags),
|
||||
StartTime: data.StartTime.UnixNano() / 1000,
|
||||
|
@ -37,10 +37,10 @@ import (
|
||||
func Test_spanDataToThrift(t *testing.T) {
|
||||
now := time.Now()
|
||||
traceID, _ := core.TraceIDFromHex("0102030405060708090a0b0c0d0e0f10")
|
||||
spanID := uint64(0x0102030405060708)
|
||||
spanID, _ := core.SpanIDFromHex("0102030405060708")
|
||||
|
||||
linkTraceID, _ := core.TraceIDFromHex("0102030405060709090a0b0c0d0e0f11")
|
||||
linkSpanID := uint64(0x0102030405060709)
|
||||
linkSpanID, _ := core.SpanIDFromHex("0102030405060709")
|
||||
|
||||
keyValue := "value"
|
||||
statusCodeValue := int64(2)
|
||||
@ -102,7 +102,7 @@ func Test_spanDataToThrift(t *testing.T) {
|
||||
RefType: gen.SpanRefType_CHILD_OF,
|
||||
TraceIdHigh: int64(binary.BigEndian.Uint64(linkTraceID[0:8])),
|
||||
TraceIdLow: int64(binary.BigEndian.Uint64(linkTraceID[8:16])),
|
||||
SpanId: int64(linkSpanID),
|
||||
SpanId: int64(binary.BigEndian.Uint64(linkSpanID[:])),
|
||||
},
|
||||
},
|
||||
// TODO [rghetia]: check Logs when event is added.
|
||||
|
@ -82,7 +82,7 @@ func protoFromSpanData(s *export.SpanData, projectID string) *tracepb.Span {
|
||||
EndTime: timestampProto(s.EndTime),
|
||||
SameProcessAsParentSpan: &wrapperspb.BoolValue{Value: !s.HasRemoteParent},
|
||||
}
|
||||
if s.ParentSpanID != s.SpanContext.SpanID && s.ParentSpanID != 0 {
|
||||
if s.ParentSpanID != s.SpanContext.SpanID && s.ParentSpanID.IsValid() {
|
||||
sp.ParentSpanId = fmt.Sprintf("%.16x", s.ParentSpanID)
|
||||
}
|
||||
if s.Status != codes.OK {
|
||||
|
@ -41,7 +41,7 @@ func TestExporter_ExportSpan(t *testing.T) {
|
||||
// setup test span
|
||||
now := time.Now()
|
||||
traceID, _ := core.TraceIDFromHex("0102030405060708090a0b0c0d0e0f10")
|
||||
spanID := uint64(0x0102030405060708)
|
||||
spanID, _ := core.SpanIDFromHex("0102030405060708")
|
||||
keyValue := "value"
|
||||
doubleValue := float64(123.456)
|
||||
|
||||
@ -74,7 +74,7 @@ func TestExporter_ExportSpan(t *testing.T) {
|
||||
expectedOutput := `{"SpanContext":{` +
|
||||
`"TraceID":"0102030405060708090a0b0c0d0e0f10",` +
|
||||
`"SpanID":"0102030405060708","TraceFlags":0},` +
|
||||
`"ParentSpanID":0,` +
|
||||
`"ParentSpanID":"0000000000000000",` +
|
||||
`"SpanKind":"internal",` +
|
||||
`"Name":"/foo",` +
|
||||
`"StartTime":` + string(expectedSerializedNow) + "," +
|
||||
|
@ -17,6 +17,7 @@ package trace
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
@ -77,7 +78,8 @@ func (mt *MockTracer) Start(ctx context.Context, name string, o ...apitrace.Span
|
||||
} else {
|
||||
sc = opts.Relation.SpanContext
|
||||
}
|
||||
sc.SpanID = atomic.AddUint64(mt.StartSpanID, 1)
|
||||
|
||||
binary.BigEndian.PutUint64(sc.SpanID[:], atomic.AddUint64(mt.StartSpanID, 1))
|
||||
span = &MockSpan{
|
||||
sc: sc,
|
||||
tracer: mt,
|
||||
|
@ -15,8 +15,6 @@
|
||||
package propagation
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
apipropagation "go.opentelemetry.io/api/propagation"
|
||||
)
|
||||
@ -40,7 +38,7 @@ func (bp binaryPropagator) ToBytes(sc core.SpanContext) []byte {
|
||||
var b [29]byte
|
||||
copy(b[2:18], sc.TraceID[:])
|
||||
b[18] = 1
|
||||
binary.BigEndian.PutUint64(b[19:27], sc.SpanID)
|
||||
copy(b[19:27], sc.SpanID[:])
|
||||
b[27] = 2
|
||||
b[28] = sc.TraceFlags
|
||||
return b[:]
|
||||
@ -60,7 +58,7 @@ func (bp binaryPropagator) FromBytes(b []byte) (sc core.SpanContext) {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
if len(b) >= 9 && b[0] == 1 {
|
||||
sc.SpanID = binary.BigEndian.Uint64(b[1:9])
|
||||
copy(sc.SpanID[:], b[1:9])
|
||||
b = b[9:]
|
||||
}
|
||||
if len(b) >= 2 && b[0] == 2 {
|
||||
|
@ -15,7 +15,6 @@
|
||||
package propagation_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@ -25,11 +24,9 @@ import (
|
||||
)
|
||||
|
||||
func TestExtractSpanContextFromBytes(t *testing.T) {
|
||||
bTraceID, _ := hex.DecodeString("4bf92f3577b34da6a3ce929d0e0e4736")
|
||||
traceID := core.TraceID{}
|
||||
copy(traceID[:], bTraceID)
|
||||
traceID, _ := core.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
|
||||
spanID, _ := core.SpanIDFromHex("00f067aa0ba902b7")
|
||||
|
||||
spanID := uint64(0x00f067aa0ba902b7)
|
||||
propagator := propagation.BinaryPropagator()
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -123,11 +120,9 @@ func TestExtractSpanContextFromBytes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConvertSpanContextToBytes(t *testing.T) {
|
||||
bTraceID, _ := hex.DecodeString("4bf92f3577b34da6a3ce929d0e0e4736")
|
||||
traceID := core.TraceID{}
|
||||
copy(traceID[:], bTraceID)
|
||||
traceID, _ := core.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
|
||||
spanID, _ := core.SpanIDFromHex("00f067aa0ba902b7")
|
||||
|
||||
spanID := uint64(0x00f067aa0ba902b7)
|
||||
propagator := propagation.BinaryPropagator()
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -17,8 +17,6 @@ package propagation
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/api/trace"
|
||||
@ -57,8 +55,6 @@ type HTTPB3Propagator struct {
|
||||
|
||||
var _ apipropagation.TextFormatPropagator = HTTPB3Propagator{}
|
||||
|
||||
var hexStr16ByteRegex = regexp.MustCompile("^[a-f0-9]{16}$")
|
||||
|
||||
func (b3 HTTPB3Propagator) Inject(ctx context.Context, supplier apipropagation.Supplier) {
|
||||
sc := trace.CurrentSpan(ctx).SpanContext()
|
||||
if sc.IsValid() {
|
||||
@ -98,12 +94,12 @@ func (b3 HTTPB3Propagator) GetAllKeys() []string {
|
||||
}
|
||||
|
||||
func (b3 HTTPB3Propagator) extract(supplier apipropagation.Supplier) core.SpanContext {
|
||||
tid, ok := b3.extractTraceID(supplier.Get(B3TraceIDHeader))
|
||||
if !ok {
|
||||
tid, err := core.TraceIDFromHex(supplier.Get(B3TraceIDHeader))
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
sid, ok := b3.extractSpanID(supplier.Get(B3SpanIDHeader))
|
||||
if !ok {
|
||||
sid, err := core.SpanIDFromHex(supplier.Get(B3SpanIDHeader))
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
sampled, ok := b3.extractSampledState(supplier.Get(B3SampledHeader))
|
||||
@ -148,26 +144,27 @@ func (b3 HTTPB3Propagator) extractSingleHeader(supplier apipropagation.Supplier)
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
var ok bool
|
||||
sc.TraceID, ok = b3.extractTraceID(parts[0])
|
||||
if !ok {
|
||||
var err error
|
||||
sc.TraceID, err = core.TraceIDFromHex(parts[0])
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
sc.SpanID, ok = b3.extractSpanID(parts[1])
|
||||
if !ok {
|
||||
sc.SpanID, err = core.SpanIDFromHex(parts[1])
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
|
||||
if l > 2 {
|
||||
var ok bool
|
||||
sc.TraceFlags, ok = b3.extractSampledState(parts[2])
|
||||
if !ok {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
}
|
||||
if l == 4 {
|
||||
_, ok = b3.extractSpanID(parts[3])
|
||||
if !ok {
|
||||
_, err = core.SpanIDFromHex(parts[3])
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
}
|
||||
@ -179,21 +176,6 @@ func (b3 HTTPB3Propagator) extractSingleHeader(supplier apipropagation.Supplier)
|
||||
return sc
|
||||
}
|
||||
|
||||
// extractTraceID parses the value of the X-B3-TraceId b3Header.
|
||||
func (b3 HTTPB3Propagator) extractTraceID(tid string) (traceID core.TraceID, ok bool) {
|
||||
traceID, err := core.TraceIDFromHex(tid)
|
||||
return traceID, err == nil
|
||||
}
|
||||
|
||||
// extractSpanID parses the value of the X-B3-SpanId or X-B3-ParentSpanId headers.
|
||||
func (b3 HTTPB3Propagator) extractSpanID(sid string) (spanID uint64, ok bool) {
|
||||
if hexStr16ByteRegex.MatchString(sid) {
|
||||
spanID, _ = strconv.ParseUint(sid, 16, 64)
|
||||
ok = true
|
||||
}
|
||||
return spanID, ok
|
||||
}
|
||||
|
||||
// extractSampledState parses the value of the X-B3-Sampled b3Header.
|
||||
func (b3 HTTPB3Propagator) extractSampledState(sampled string) (flag byte, ok bool) {
|
||||
switch sampled {
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
@ -119,7 +118,7 @@ func (hp HTTPTraceContextPropagator) extractSpanContext(
|
||||
|
||||
var sc core.SpanContext
|
||||
|
||||
_, err = hex.Decode(sc.TraceID[0:16], []byte(sections[1][0:32]))
|
||||
sc.TraceID, err = core.TraceIDFromHex(sections[1][:32])
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
@ -127,11 +126,10 @@ func (hp HTTPTraceContextPropagator) extractSpanContext(
|
||||
if len(sections[2]) != 16 {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
result, err := strconv.ParseUint(sections[2][0:], 16, 64)
|
||||
sc.SpanID, err = core.SpanIDFromHex(sections[2][:])
|
||||
if err != nil {
|
||||
return core.EmptySpanContext()
|
||||
}
|
||||
sc.SpanID = result
|
||||
|
||||
if len(sections[3]) != 2 {
|
||||
return core.EmptySpanContext()
|
||||
|
@ -2,7 +2,6 @@ package propagation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
@ -25,13 +24,9 @@ func BenchmarkInject(b *testing.B) {
|
||||
|
||||
func injectSubBenchmarks(b *testing.B, fn func(context.Context, *testing.B)) {
|
||||
b.Run("SampledSpanContext", func(b *testing.B) {
|
||||
var (
|
||||
id uint64
|
||||
spanID = uint64(0x00f067aa0ba902b7)
|
||||
traceID = core.TraceID{}
|
||||
)
|
||||
bt, _ := hex.DecodeString("4bf92f3577b34da6a3ce929d0e0e4736")
|
||||
copy(traceID[:], bt)
|
||||
var id uint64
|
||||
spanID, _ := core.SpanIDFromHex("00f067aa0ba902b7")
|
||||
traceID, _ := core.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
|
||||
|
||||
mockTracer := &mocktrace.MockTracer{
|
||||
Sampled: false,
|
||||
|
@ -32,7 +32,7 @@ import (
|
||||
|
||||
var (
|
||||
traceID = mustTraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
|
||||
spanID = uint64(0x00f067aa0ba902b7)
|
||||
spanID = mustSpanIDFromHex("00f067aa0ba902b7")
|
||||
)
|
||||
|
||||
func mustTraceIDFromHex(s string) (t core.TraceID) {
|
||||
@ -40,6 +40,11 @@ func mustTraceIDFromHex(s string) (t core.TraceID) {
|
||||
return
|
||||
}
|
||||
|
||||
func mustSpanIDFromHex(s string) (t core.SpanID) {
|
||||
t, _ = core.SpanIDFromHex(s)
|
||||
return
|
||||
}
|
||||
|
||||
func TestExtractValidTraceContextFromHTTPReq(t *testing.T) {
|
||||
var propagator propagation.HTTPTraceContextPropagator
|
||||
tests := []struct {
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
// SpanData contains all the information collected by a span.
|
||||
type SpanData struct {
|
||||
SpanContext core.SpanContext
|
||||
ParentSpanID uint64
|
||||
ParentSpanID core.SpanID
|
||||
SpanKind apitrace.SpanKind
|
||||
Name string
|
||||
StartTime time.Time
|
||||
|
@ -194,7 +194,7 @@ func generateSpan(t *testing.T, tr apitrace.Tracer, option testOption) {
|
||||
|
||||
func getSpanContext() core.SpanContext {
|
||||
tid, _ := core.TraceIDFromHex("01020304050607080102040810203040")
|
||||
sid := uint64(0x0102040810203040)
|
||||
sid, _ := core.SpanIDFromHex("0102040810203040")
|
||||
return core.SpanContext{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
|
@ -161,8 +161,8 @@ func BenchmarkTraceID_DotString(b *testing.B) {
|
||||
|
||||
func BenchmarkSpanID_DotString(b *testing.B) {
|
||||
traceBenchmark(b, func(b *testing.B) {
|
||||
sc := core.SpanContext{SpanID: 1}
|
||||
want := "0000000000000001"
|
||||
sc := core.SpanContext{SpanID: core.SpanID{1}}
|
||||
want := "1000000000000000"
|
||||
for i := 0; i < b.N; i++ {
|
||||
if got := sc.SpanIDString(); got != want {
|
||||
b.Fatalf("got = %q want = %q", got, want)
|
||||
@ -188,4 +188,4 @@ func getTracer(b *testing.B, name string) apitrace.Tracer {
|
||||
b.Fatalf("Failed to create trace provider for test %s\n", name)
|
||||
}
|
||||
return tp.GetTracer(name)
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ package trace
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"go.opentelemetry.io/sdk/trace/internal"
|
||||
@ -25,31 +24,18 @@ import (
|
||||
|
||||
type defaultIDGenerator struct {
|
||||
sync.Mutex
|
||||
|
||||
// Please keep these as the first fields
|
||||
// so that these 8 byte fields will be aligned on addresses
|
||||
// divisible by 8, on both 32-bit and 64-bit machines when
|
||||
// performing atomic increments and accesses.
|
||||
// See:
|
||||
// * https://github.com/census-instrumentation/opencensus-go/issues/587
|
||||
// * https://github.com/census-instrumentation/opencensus-go/issues/865
|
||||
// * https://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||
nextSpanID uint64
|
||||
spanIDInc uint64
|
||||
|
||||
traceIDAdd [2]uint64
|
||||
traceIDRand *rand.Rand
|
||||
randSource *rand.Rand
|
||||
}
|
||||
|
||||
var _ internal.IDGenerator = &defaultIDGenerator{}
|
||||
|
||||
// NewSpanID returns a non-zero span ID from a randomly-chosen sequence.
|
||||
func (gen *defaultIDGenerator) NewSpanID() uint64 {
|
||||
var id uint64
|
||||
for id == 0 {
|
||||
id = atomic.AddUint64(&gen.nextSpanID, gen.spanIDInc)
|
||||
}
|
||||
return id
|
||||
func (gen *defaultIDGenerator) NewSpanID() core.SpanID {
|
||||
gen.Lock()
|
||||
defer gen.Unlock()
|
||||
sid := core.SpanID{}
|
||||
gen.randSource.Read(sid[:])
|
||||
return sid
|
||||
}
|
||||
|
||||
// NewTraceID returns a non-zero trace ID from a randomly-chosen sequence.
|
||||
@ -57,9 +43,7 @@ func (gen *defaultIDGenerator) NewSpanID() uint64 {
|
||||
func (gen *defaultIDGenerator) NewTraceID() core.TraceID {
|
||||
gen.Lock()
|
||||
defer gen.Unlock()
|
||||
// Construct the trace ID from two outputs of traceIDRand, with a constant
|
||||
// added to each half for additional entropy.
|
||||
tid := core.TraceID{}
|
||||
gen.traceIDRand.Read(tid[:])
|
||||
gen.randSource.Read(tid[:])
|
||||
return tid
|
||||
}
|
||||
|
@ -20,5 +20,5 @@ import "go.opentelemetry.io/api/core"
|
||||
// IDGenerator allows custom generators for TraceId and SpanId.
|
||||
type IDGenerator interface {
|
||||
NewTraceID() core.TraceID
|
||||
NewSpanID() uint64
|
||||
NewSpanID() core.SpanID
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ type Sampler func(SamplingParameters) SamplingDecision
|
||||
type SamplingParameters struct {
|
||||
ParentContext core.SpanContext
|
||||
TraceID core.TraceID
|
||||
SpanID uint64
|
||||
SpanID core.SpanID
|
||||
Name string
|
||||
HasRemoteParent bool
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func TestSimpleSpanProcessorOnEnd(t *testing.T) {
|
||||
tp.RegisterSpanProcessor(ssp)
|
||||
tr := tp.GetTracer("SimpleSpanProcessor")
|
||||
tid, _ := core.TraceIDFromHex("01020304050607080102040810203040")
|
||||
sid := uint64(0x0102040810203040)
|
||||
sid, _ := core.SpanIDFromHex("0102040810203040")
|
||||
sc := core.SpanContext{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
|
@ -178,7 +178,7 @@ func (s *span) SetName(name string) {
|
||||
spanName := s.tracer.spanNameWithPrefix(name)
|
||||
s.data.Name = spanName
|
||||
// SAMPLING
|
||||
noParent := s.data.ParentSpanID == 0
|
||||
noParent := !s.data.ParentSpanID.IsValid()
|
||||
var ctx core.SpanContext
|
||||
if noParent {
|
||||
ctx = core.EmptySpanContext()
|
||||
|
@ -24,14 +24,8 @@ import (
|
||||
|
||||
func defIDGenerator() internal.IDGenerator {
|
||||
gen := &defaultIDGenerator{}
|
||||
// initialize traceID and spanID generators.
|
||||
var rngSeed int64
|
||||
for _, p := range []interface{}{
|
||||
&rngSeed, &gen.traceIDAdd, &gen.nextSpanID, &gen.spanIDInc,
|
||||
} {
|
||||
_ = binary.Read(crand.Reader, binary.LittleEndian, p)
|
||||
}
|
||||
gen.traceIDRand = rand.New(rand.NewSource(rngSeed))
|
||||
gen.spanIDInc |= 1
|
||||
_ = binary.Read(crand.Reader, binary.LittleEndian, &rngSeed)
|
||||
gen.randSource = rand.New(rand.NewSource(rngSeed))
|
||||
return gen
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +35,12 @@ import (
|
||||
|
||||
var (
|
||||
tid core.TraceID
|
||||
sid = uint64(0x0102040810203040)
|
||||
sid core.SpanID
|
||||
)
|
||||
|
||||
func init() {
|
||||
tid, _ = core.TraceIDFromHex("01020304050607080102040810203040")
|
||||
sid, _ = core.SpanIDFromHex("0102040810203040")
|
||||
}
|
||||
|
||||
func TestTracerFollowsExpectedAPIBehaviour(t *testing.T) {
|
||||
@ -394,8 +395,8 @@ func TestAddLinks(t *testing.T) {
|
||||
k1v1 := key.New("key1").String("value1")
|
||||
k2v2 := key.New("key2").String("value2")
|
||||
|
||||
sc1 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: 0x3}
|
||||
sc2 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: 0x3}
|
||||
sc1 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: core.SpanID{3}}
|
||||
sc2 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: core.SpanID{3}}
|
||||
|
||||
link1 := apitrace.Link{SpanContext: sc1, Attributes: []core.KeyValue{k1v1}}
|
||||
link2 := apitrace.Link{SpanContext: sc2, Attributes: []core.KeyValue{k2v2}}
|
||||
@ -435,8 +436,8 @@ func TestLinks(t *testing.T) {
|
||||
k2v2 := key.New("key2").String("value2")
|
||||
k3v3 := key.New("key3").String("value3")
|
||||
|
||||
sc1 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: 0x3}
|
||||
sc2 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: 0x3}
|
||||
sc1 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: core.SpanID{3}}
|
||||
sc2 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: core.SpanID{3}}
|
||||
|
||||
span.Link(sc1, key.New("key1").String("value1"))
|
||||
span.Link(sc2,
|
||||
@ -471,9 +472,9 @@ func TestLinksOverLimit(t *testing.T) {
|
||||
te := &testExporter{}
|
||||
cfg := Config{MaxLinksPerSpan: 2}
|
||||
|
||||
sc1 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: 0x3}
|
||||
sc2 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: 0x3}
|
||||
sc3 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: 0x3}
|
||||
sc1 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: core.SpanID{3}}
|
||||
sc2 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: core.SpanID{3}}
|
||||
sc3 := core.SpanContext{TraceID: core.TraceID([16]byte{1, 1}), SpanID: core.SpanID{3}}
|
||||
|
||||
tp, _ := NewProvider(WithConfig(cfg), WithSyncer(te))
|
||||
span := startSpan(tp, "LinksOverLimit")
|
||||
@ -632,10 +633,10 @@ func endSpan(te *testExporter, span apitrace.Span) (*export.SpanData, error) {
|
||||
return nil, fmt.Errorf("got exported spans %#v, want one span", te.spans)
|
||||
}
|
||||
got := te.spans[0]
|
||||
if got.SpanContext.SpanID == 0 {
|
||||
if !got.SpanContext.SpanID.IsValid() {
|
||||
return nil, fmt.Errorf("exporting span: expected nonzero SpanID")
|
||||
}
|
||||
got.SpanContext.SpanID = 0
|
||||
got.SpanContext.SpanID = core.SpanID{}
|
||||
if !checkTime(&got.StartTime) {
|
||||
return nil, fmt.Errorf("exporting span: expected nonzero StartTime")
|
||||
}
|
||||
@ -755,6 +756,7 @@ func TestExecutionTracerTaskEnd(t *testing.T) {
|
||||
spans = append(spans, s) // never sample
|
||||
|
||||
tID, _ := core.TraceIDFromHex("0102030405060708090a0b0c0d0e0f")
|
||||
sID, _ := core.SpanIDFromHex("0001020304050607")
|
||||
|
||||
_, apiSpan = tr.Start(
|
||||
context.Background(),
|
||||
@ -762,7 +764,7 @@ func TestExecutionTracerTaskEnd(t *testing.T) {
|
||||
apitrace.ChildOf(
|
||||
core.SpanContext{
|
||||
TraceID: tID,
|
||||
SpanID: uint64(0x0001020304050607),
|
||||
SpanID: sID,
|
||||
TraceFlags: 0,
|
||||
},
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user