1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2024-12-26 21:05:00 +02:00
opentelemetry-go/oteltest/tracer_test.go
Tyler Yahn 604b05cbcf
Store current Span instead of local and remote SpanContext in context.Context (#1731)
* Store Span instead of local/remote SpanContext in Context

Now that the SpanContext has a remote identifier, storing a Span's
SpanContext two separate ways in a Context (one for local another for
remote) is unnecessary and adds complication throughout the project when
determining heredity of a Span. This moves to storing the Span directly
in the Context uniformly (for both local and remote) as current Span. In
the process, it updates the getter/setter functionality the `trace`
package provides and replaces the distributed heredity logic throughout
the project with just using the current Span as the parent if it exists.

* Update trace/context.go

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>

* Assert propagators context remote state

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
2021-03-29 08:55:44 -07:00

292 lines
7.9 KiB
Go

// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package oteltest_test
import (
"context"
"fmt"
"sync"
"sync/atomic"
"testing"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/internal/matchers"
"go.opentelemetry.io/otel/oteltest"
"go.opentelemetry.io/otel/trace"
)
func TestTracer(t *testing.T) {
tp := oteltest.NewTracerProvider()
oteltest.NewHarness(t).TestTracer(func() func() trace.Tracer {
tp := oteltest.NewTracerProvider()
var i uint64
return func() trace.Tracer {
return tp.Tracer(fmt.Sprintf("tracer %d", atomic.AddUint64(&i, 1)))
}
}())
t.Run("#Start", func(t *testing.T) {
testTracedSpan(t, func(tracer trace.Tracer, name string) (trace.Span, error) {
_, span := tracer.Start(context.Background(), name)
return span, nil
})
t.Run("uses the start time from WithTimestamp", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
expectedStartTime := time.Now().AddDate(5, 0, 0)
subject := tp.Tracer(t.Name())
_, span := subject.Start(context.Background(), "test", trace.WithTimestamp(expectedStartTime))
testSpan, ok := span.(*oteltest.Span)
e.Expect(ok).ToBeTrue()
e.Expect(testSpan.StartTime()).ToEqual(expectedStartTime)
})
t.Run("uses the attributes from WithAttributes", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
attr1 := attribute.String("a", "1")
attr2 := attribute.String("b", "2")
subject := tp.Tracer(t.Name())
_, span := subject.Start(context.Background(), "test", trace.WithAttributes(attr1, attr2))
testSpan, ok := span.(*oteltest.Span)
e.Expect(ok).ToBeTrue()
attributes := testSpan.Attributes()
e.Expect(attributes[attr1.Key]).ToEqual(attr1.Value)
e.Expect(attributes[attr2.Key]).ToEqual(attr2.Value)
})
t.Run("uses the current span from context as parent", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
subject := tp.Tracer(t.Name())
parent, parentSpan := subject.Start(context.Background(), "parent")
parentSpanContext := parentSpan.SpanContext()
_, span := subject.Start(parent, "child")
testSpan, ok := span.(*oteltest.Span)
e.Expect(ok).ToBeTrue()
childSpanContext := testSpan.SpanContext()
e.Expect(childSpanContext.TraceID()).ToEqual(parentSpanContext.TraceID())
e.Expect(childSpanContext.SpanID()).NotToEqual(parentSpanContext.SpanID())
e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID())
})
t.Run("uses the current span from context as parent, even if it is remote", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
subject := tp.Tracer(t.Name())
ctx, _ := subject.Start(context.Background(), "local grandparent")
_, s := subject.Start(ctx, "remote parent")
ctx = trace.ContextWithRemoteSpanContext(ctx, s.SpanContext())
parentSpanContext := trace.SpanContextFromContext(ctx)
_, span := subject.Start(ctx, "child")
testSpan, ok := span.(*oteltest.Span)
e.Expect(ok).ToBeTrue()
childSpanContext := testSpan.SpanContext()
e.Expect(childSpanContext.TraceID()).ToEqual(parentSpanContext.TraceID())
e.Expect(childSpanContext.SpanID()).NotToEqual(parentSpanContext.SpanID())
e.Expect(testSpan.ParentSpanID()).ToEqual(parentSpanContext.SpanID())
})
t.Run("creates new root when context does not have a current span", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
subject := tp.Tracer(t.Name())
_, napSpan := subject.Start(context.Background(), "not-a-parent")
napSpanContext := napSpan.SpanContext()
_, span := subject.Start(context.Background(), "child")
testSpan, ok := span.(*oteltest.Span)
e.Expect(ok).ToBeTrue()
childSpanContext := testSpan.SpanContext()
e.Expect(childSpanContext.TraceID()).NotToEqual(napSpanContext.TraceID())
e.Expect(childSpanContext.SpanID()).NotToEqual(napSpanContext.SpanID())
e.Expect(testSpan.ParentSpanID().IsValid()).ToBeFalse()
})
t.Run("creates new root when requested, even if context has current span", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
subject := tp.Tracer(t.Name())
ctx, napSpan := subject.Start(context.Background(), "not-a-parent")
napSpanContext := napSpan.SpanContext()
_, span := subject.Start(ctx, "child", trace.WithNewRoot())
testSpan, ok := span.(*oteltest.Span)
e.Expect(ok).ToBeTrue()
childSpanContext := testSpan.SpanContext()
e.Expect(childSpanContext.TraceID()).NotToEqual(napSpanContext.TraceID())
e.Expect(childSpanContext.SpanID()).NotToEqual(napSpanContext.SpanID())
e.Expect(testSpan.ParentSpanID().IsValid()).ToBeFalse()
})
t.Run("uses the links provided through WithLinks", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
subject := tp.Tracer(t.Name())
_, span := subject.Start(context.Background(), "link1")
link1 := trace.Link{
SpanContext: span.SpanContext(),
Attributes: []attribute.KeyValue{
attribute.String("a", "1"),
},
}
_, span = subject.Start(context.Background(), "link2")
link2 := trace.Link{
SpanContext: span.SpanContext(),
Attributes: []attribute.KeyValue{
attribute.String("b", "2"),
},
}
_, span = subject.Start(context.Background(), "test", trace.WithLinks(link1, link2))
testSpan, ok := span.(*oteltest.Span)
e.Expect(ok).ToBeTrue()
links := testSpan.Links()
e.Expect(links[0].Attributes).ToEqual(link1.Attributes)
e.Expect(links[1].Attributes).ToEqual(link2.Attributes)
})
})
}
func testTracedSpan(t *testing.T, fn func(tracer trace.Tracer, name string) (trace.Span, error)) {
tp := oteltest.NewTracerProvider()
t.Run("starts a span with the expected name", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
subject := tp.Tracer(t.Name())
expectedName := "test name"
span, err := fn(subject, expectedName)
e.Expect(err).ToBeNil()
testSpan, ok := span.(*oteltest.Span)
e.Expect(ok).ToBeTrue()
e.Expect(testSpan.Name()).ToEqual(expectedName)
})
t.Run("uses the current time as the start time", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
subject := tp.Tracer(t.Name())
start := time.Now()
span, err := fn(subject, "test")
end := time.Now()
e.Expect(err).ToBeNil()
testSpan, ok := span.(*oteltest.Span)
e.Expect(ok).ToBeTrue()
e.Expect(testSpan.StartTime()).ToBeTemporally(matchers.AfterOrSameTime, start)
e.Expect(testSpan.StartTime()).ToBeTemporally(matchers.BeforeOrSameTime, end)
})
t.Run("calls SpanRecorder.OnStart", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
sr := new(oteltest.SpanRecorder)
subject := oteltest.NewTracerProvider(oteltest.WithSpanRecorder(sr)).Tracer(t.Name())
subject.Start(context.Background(), "span1")
e.Expect(len(sr.Started())).ToEqual(1)
span, err := fn(subject, "span2")
e.Expect(err).ToBeNil()
spans := sr.Started()
e.Expect(len(spans)).ToEqual(2)
e.Expect(spans[1]).ToEqual(span)
})
t.Run("can be run concurrently with another call", func(t *testing.T) {
t.Parallel()
e := matchers.NewExpecter(t)
sr := new(oteltest.SpanRecorder)
subject := oteltest.NewTracerProvider(oteltest.WithSpanRecorder(sr)).Tracer(t.Name())
numSpans := 2
var wg sync.WaitGroup
wg.Add(numSpans)
for i := 0; i < numSpans; i++ {
go func() {
_, err := fn(subject, "test")
e.Expect(err).ToBeNil()
wg.Done()
}()
}
wg.Wait()
e.Expect(len(sr.Started())).ToEqual(numSpans)
})
}