1
0
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:
Gustavo Silva Paiva 2019-10-28 14:05:06 -03:00 committed by rghetia
parent d9c4aa5cee
commit 4e545e2ab8
26 changed files with 194 additions and 212 deletions

View File

@ -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 {

View File

@ -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{},

View File

@ -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, ",") {

View File

@ -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
}

View File

@ -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 {

View File

@ -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,

View File

@ -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.

View File

@ -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 {

View File

@ -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) + "," +

View File

@ -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,

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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()

View File

@ -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,

View File

@ -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 {

View File

@ -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

View File

@ -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,

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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,

View File

@ -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()

View File

@ -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
}
}

View File

@ -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,
},
),