Skip to content

Commit

Permalink
major refactor work. compiles but does not work.
Browse files Browse the repository at this point in the history
  • Loading branch information
knusbaum committed Oct 30, 2023
1 parent f845595 commit 8e216e8
Show file tree
Hide file tree
Showing 41 changed files with 3,100 additions and 4,107 deletions.
237 changes: 117 additions & 120 deletions ddtrace/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ package ddtrace

import (
"context"
"encoding/binary"
"encoding/hex"
"testing"

"github.com/DataDog/dd-trace-go/v2/internal"
Expand Down Expand Up @@ -38,126 +36,125 @@ func TestSpanFromContext(t *testing.T) {
assert := assert.New(t)
span, ok := SpanFromContext(context.Background())
assert.False(ok)
_, ok = span.(*NoopSpan)
assert.True(ok)
assert.Nil(span)
span, ok = SpanFromContext(nil)
assert.False(ok)
_, ok = span.(*NoopSpan)
assert.True(ok)
assert.Nil(span)
})
}

func TestStartSpanFromContext(t *testing.T) {
_, _, _, stop := startTestTracer(t)
defer stop()

parent := &Span{context: &spanContext{spanID: 123, traceID: traceIDFrom64Bits(456)}}
parent2 := &Span{context: &spanContext{spanID: 789, traceID: traceIDFrom64Bits(456)}}
pctx := ContextWithSpan(context.Background(), parent)
child, ctx := StartSpanFromContext(
pctx,
"http.request",
ServiceName("gin"),
ResourceName("/"),
ChildOf(parent2.Context()), // we do this to assert that the span in pctx takes priority.
)
assert := assert.New(t)

got, ok := child.(*Span)
assert.True(ok)
gotctx, ok := SpanFromContext(ctx)
assert.True(ok)
assert.Equal(gotctx, got)
_, ok = gotctx.(*NoopSpan)
assert.False(ok)

assert.Equal(uint64(456), got.TraceID)
assert.Equal(uint64(123), got.ParentID)
assert.Equal("http.request", got.Name)
assert.Equal("gin", got.Service)
assert.Equal("/", got.Resource)
}

func TestStartSpanFromContextRace(t *testing.T) {
_, _, _, stop := startTestTracer(t)
defer stop()

// Start 100 goroutines that create child spans with StartSpanFromContext in parallel,
// with a shared options slice. The child spans should get parented to the correct spans
const contextKey = "key"
const numContexts = 100
options := make([]StartSpanOption, 0, 3)
outputValues := make(chan uint64, numContexts)
var expectedTraceIDs []uint64
for i := 0; i < numContexts; i++ {
parent, childCtx := StartSpanFromContext(context.Background(), "parent")
expectedTraceIDs = append(expectedTraceIDs, parent.Context().TraceID())
go func() {
span, _ := StartSpanFromContext(childCtx, "testoperation", options...)
defer span.Finish()
outputValues <- span.Context().TraceID()
}()
parent.Finish()
}

// collect the outputs
var outputs []uint64
for i := 0; i < numContexts; i++ {
outputs = append(outputs, <-outputValues)
}
assert.Len(t, outputs, numContexts)
assert.ElementsMatch(t, outputs, expectedTraceIDs)
}

func Test128(t *testing.T) {
_, _, _, stop := startTestTracer(t)
defer stop()

span, _ := StartSpanFromContext(context.Background(), "http.request")
assert.NotZero(t, span.Context().TraceID())
w3cCtx, ok := span.Context().(SpanContextW3C)
if !ok {
assert.Fail(t, "couldn't cast to SpanContextW3C")
}
id128 := w3cCtx.TraceID128()
assert.Len(t, id128, 32) // ensure there are enough leading zeros
idBytes, err := hex.DecodeString(id128)
assert.NoError(t, err)
assert.Equal(t, uint64(0), binary.BigEndian.Uint64(idBytes[:8])) // high 64 bits should be 0
assert.Equal(t, span.Context().TraceID(), binary.BigEndian.Uint64(idBytes[8:]))

// Enable 128 bit trace ids
t.Setenv("DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED", "true")
span128, _ := StartSpanFromContext(context.Background(), "http.request")
assert.NotZero(t, span128.Context().TraceID())
w3cCtx, ok = span128.Context().(SpanContextW3C)
if !ok {
assert.Fail(t, "couldn't cast to SpanContextW3C")
}
id128bit := w3cCtx.TraceID128()
assert.NotEmpty(t, id128bit)
assert.Len(t, id128bit, 32)
// Ensure that the lower order bits match the span's 64-bit trace id
b, err := hex.DecodeString(id128bit)
assert.NoError(t, err)
assert.Equal(t, span128.Context().TraceID(), binary.BigEndian.Uint64(b[8:]))
}

func TestStartSpanFromNilContext(t *testing.T) {
_, _, _, stop := startTestTracer(t)
defer stop()

child, ctx := StartSpanFromContext(nil, "http.request")
assert := assert.New(t)
// ensure the returned context works
assert.Nil(ctx.Value("not_found_key"))

internalSpan, ok := child.(*Span)
assert.True(ok)
assert.Equal("http.request", internalSpan.Name)

// the returned context includes the span
ctxSpan, ok := SpanFromContext(ctx)
assert.True(ok)
assert.Equal(child, ctxSpan)
}
// TODO(kjn v2): Fix span context
// func TestStartSpanFromContext(t *testing.T) {
// _, _, _, stop := startTestTracer(t)
// defer stop()
//
// parent := &Span{context: &spanContext{spanID: 123, traceID: traceIDFrom64Bits(456)}}
// parent2 := &Span{context: &spanContext{spanID: 789, traceID: traceIDFrom64Bits(456)}}
// pctx := ContextWithSpan(context.Background(), parent)
// child, ctx := StartSpanFromContext(
// pctx,
// "http.request",
// ServiceName("gin"),
// ResourceName("/"),
// ChildOf(parent2.Context()), // we do this to assert that the span in pctx takes priority.
// )
// assert := assert.New(t)
//
// got, ok := child.(*Span)
// assert.True(ok)
// gotctx, ok := SpanFromContext(ctx)
// assert.True(ok)
// assert.Equal(gotctx, got)
// _, ok = gotctx.(*NoopSpan)
// assert.False(ok)
//
// assert.Equal(uint64(456), got.TraceID)
// assert.Equal(uint64(123), got.ParentID)
// assert.Equal("http.request", got.Name)
// assert.Equal("gin", got.Service)
// assert.Equal("/", got.Resource)
// }
//
// func TestStartSpanFromContextRace(t *testing.T) {
// _, _, _, stop := startTestTracer(t)
// defer stop()
//
// // Start 100 goroutines that create child spans with StartSpanFromContext in parallel,
// // with a shared options slice. The child spans should get parented to the correct spans
// const contextKey = "key"
// const numContexts = 100
// options := make([]StartSpanOption, 0, 3)
// outputValues := make(chan uint64, numContexts)
// var expectedTraceIDs []uint64
// for i := 0; i < numContexts; i++ {
// parent, childCtx := StartSpanFromContext(context.Background(), "parent")
// expectedTraceIDs = append(expectedTraceIDs, parent.Context().TraceID())
// go func() {
// span, _ := StartSpanFromContext(childCtx, "testoperation", options...)
// defer span.Finish()
// outputValues <- span.Context().TraceID()
// }()
// parent.Finish()
// }
//
// // collect the outputs
// var outputs []uint64
// for i := 0; i < numContexts; i++ {
// outputs = append(outputs, <-outputValues)
// }
// assert.Len(t, outputs, numContexts)
// assert.ElementsMatch(t, outputs, expectedTraceIDs)
// }
//
// func Test128(t *testing.T) {
// _, _, _, stop := startTestTracer(t)
// defer stop()
//
// span, _ := StartSpanFromContext(context.Background(), "http.request")
// assert.NotZero(t, span.Context().TraceID())
// w3cCtx, ok := span.Context().(SpanContextW3C)
// if !ok {
// assert.Fail(t, "couldn't cast to SpanContextW3C")
// }
// id128 := w3cCtx.TraceID128()
// assert.Len(t, id128, 32) // ensure there are enough leading zeros
// idBytes, err := hex.DecodeString(id128)
// assert.NoError(t, err)
// assert.Equal(t, uint64(0), binary.BigEndian.Uint64(idBytes[:8])) // high 64 bits should be 0
// assert.Equal(t, span.Context().TraceID(), binary.BigEndian.Uint64(idBytes[8:]))
//
// // Enable 128 bit trace ids
// t.Setenv("DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED", "true")
// span128, _ := StartSpanFromContext(context.Background(), "http.request")
// assert.NotZero(t, span128.Context().TraceID())
// w3cCtx, ok = span128.Context().(SpanContextW3C)
// if !ok {
// assert.Fail(t, "couldn't cast to SpanContextW3C")
// }
// id128bit := w3cCtx.TraceID128()
// assert.NotEmpty(t, id128bit)
// assert.Len(t, id128bit, 32)
// // Ensure that the lower order bits match the span's 64-bit trace id
// b, err := hex.DecodeString(id128bit)
// assert.NoError(t, err)
// assert.Equal(t, span128.Context().TraceID(), binary.BigEndian.Uint64(b[8:]))
// }
//
// func TestStartSpanFromNilContext(t *testing.T) {
// _, _, _, stop := startTestTracer(t)
// defer stop()
//
// child, ctx := StartSpanFromContext(nil, "http.request")
// assert := assert.New(t)
// // ensure the returned context works
// assert.Nil(ctx.Value("not_found_key"))
//
// internalSpan, ok := child.(*Span)
// assert.True(ok)
// assert.Equal("http.request", internalSpan.Name)
//
// // the returned context includes the span
// ctxSpan, ok := SpanFromContext(ctx)
// assert.True(ok)
// assert.Equal(child, ctxSpan)
// }
66 changes: 35 additions & 31 deletions ddtrace/ddtrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ type SpanContextW3C interface {
// within the "tracer" package.
type Tracer interface {
// StartSpan starts a span with the given operation name and options.
StartSpan(operationName string, opts ...StartSpanOption) Span
StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (Span, context.Context)
StartSpan(operationName string, opts ...StartSpanOption) *Span
StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (*Span, context.Context)

// Extract extracts a span context from a given carrier. Note that baggage item
// keys will always be lower-cased to maintain consistency. It is impossible to
Expand All @@ -52,33 +52,37 @@ type Tracer interface {

// Stop stops the tracer. Calls to Stop should be idempotent.
Stop()
}

// Span represents a chunk of computation time. Spans have names, durations,
// timestamps and other metadata. A Tracer is used to create hierarchies of
// spans in a request, buffer and submit them to the server.
type Span interface {
// SetTag sets a key/value pair as metadata on the span.
SetTag(key string, value interface{})

// SetOperationName sets the operation name for this span. An operation name should be
// a representative name for a group of spans (e.g. "grpc.server" or "http.request").
SetOperationName(operationName string)

// BaggageItem returns the baggage item held by the given key.
BaggageItem(key string) string

// SetBaggageItem sets a new baggage item at the given key. The baggage
// item should propagate to all descendant spans, both in- and cross-process.
SetBaggageItem(key, val string)

// Finish finishes the current span with the given options. Finish calls should be idempotent.
Finish(opts ...FinishOption)

// Context returns the SpanContext of this Span.
Context() SpanContext
// Temporary hacks for v2 conversion
CanComputeStats() bool
PushChunk(*Chunk) // We will keep this, or some version of it to allow submitting traces to a tracer.
}

// // Span represents a chunk of computation time. Spans have names, durations,
// // timestamps and other metadata. A Tracer is used to create hierarchies of
// // spans in a request, buffer and submit them to the server.
// type Span interface {
// // SetTag sets a key/value pair as metadata on the span.
// SetTag(key string, value interface{})
//
// // SetOperationName sets the operation name for this span. An operation name should be
// // a representative name for a group of spans (e.g. "grpc.server" or "http.request").
// SetOperationName(operationName string)
//
// // BaggageItem returns the baggage item held by the given key.
// BaggageItem(key string) string
//
// // SetBaggageItem sets a new baggage item at the given key. The baggage
// // item should propagate to all descendant spans, both in- and cross-process.
// SetBaggageItem(key, val string)
//
// // Finish finishes the current span with the given options. Finish calls should be idempotent.
// Finish(opts ...FinishOption)
//
// // Context returns the SpanContext of this Span.
// Context() SpanContext
// }

// SpanContext represents a span state that can propagate to descendant spans
// and across process boundaries. It contains all the information needed to
// spawn a direct descendant of the span that it belongs to. It can be used
Expand Down Expand Up @@ -160,25 +164,25 @@ func UseLogger(l Logger) {
}

// ContextWithSpan returns a copy of the given context which includes the span s.
func ContextWithSpan(ctx context.Context, s Span) context.Context {
func ContextWithSpan(ctx context.Context, s *Span) context.Context {
return context.WithValue(ctx, internal.ActiveSpanKey, s)
}

// StartSpan starts a new span with the given operation name and set of options.
// If the tracer is not started, calling this function is a no-op.
func StartSpan(operationName string, opts ...StartSpanOption) Span {
func StartSpan(operationName string, opts ...StartSpanOption) *Span {
return GetGlobalTracer().StartSpan(operationName, opts...)
}

// SpanFromContext returns the span contained in the given context. A second return
// value indicates if a span was found in the context. If no span is found, a no-op
// span is returned.
func SpanFromContext(ctx context.Context) (Span, bool) {
func SpanFromContext(ctx context.Context) (*Span, bool) {
if ctx == nil {
return nil, false
}
v := ctx.Value(internal.ActiveSpanKey)
if s, ok := v.(Span); ok {
if s, ok := v.(*Span); ok {
return s, true
}
return nil, false
Expand All @@ -187,7 +191,7 @@ func SpanFromContext(ctx context.Context) (Span, bool) {
// StartSpanFromContext returns a new span with the given operation name and options. If a span
// is found in the context, it will be used as the parent of the resulting span. If the ChildOf
// option is passed, it will only be used as the parent if there is no span found in `ctx`.
func StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (Span, context.Context) {
func StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (*Span, context.Context) {
return GetGlobalTracer().StartSpanFromContext(ctx, operationName, opts...)
}

Expand Down
Loading

0 comments on commit 8e216e8

Please sign in to comment.