mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-12-12 10:04:29 +02:00
ec300b28ad
* Update Span API event methods Remove the context argument from the event methods. It is unused and can be added back in as a passed option if needed in the future. Update AddEvent to accept a required name and a set of options. These options are the new EventOption type that can be used to configure a SpanConfig Timestamp and Attributes. Remove the AddEventWithTimestamp method as it is redundant to calling AddEvent with a WithTimestamp option. Update RecordError to also accept the EventOptions. * Add changes to CHANGELOG * Add LifeCycleOption Use the LifeCycleOption to encapsulate the options passed to a span for life cycle events.
614 lines
15 KiB
Go
614 lines
15 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"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/codes"
|
|
"go.opentelemetry.io/otel/internal/matchers"
|
|
ottest "go.opentelemetry.io/otel/internal/testing"
|
|
"go.opentelemetry.io/otel/label"
|
|
"go.opentelemetry.io/otel/oteltest"
|
|
)
|
|
|
|
func TestSpan(t *testing.T) {
|
|
t.Run("#Tracer", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("returns the tracer used to start the span", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, subject := tracer.Start(context.Background(), "test")
|
|
|
|
e.Expect(subject.Tracer()).ToEqual(tracer)
|
|
})
|
|
})
|
|
|
|
t.Run("#End", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("ends the span", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
e.Expect(subject.Ended()).ToBeFalse()
|
|
|
|
_, ok = subject.EndTime()
|
|
e.Expect(ok).ToBeFalse()
|
|
|
|
start := time.Now()
|
|
|
|
subject.End()
|
|
|
|
end := time.Now()
|
|
|
|
e.Expect(subject.Ended()).ToBeTrue()
|
|
|
|
endTime, ok := subject.EndTime()
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
e.Expect(endTime).ToBeTemporally(matchers.AfterOrSameTime, start)
|
|
e.Expect(endTime).ToBeTemporally(matchers.BeforeOrSameTime, end)
|
|
})
|
|
|
|
t.Run("only takes effect the first time it is called", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
subject.End()
|
|
|
|
expectedEndTime, ok := subject.EndTime()
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
subject.End()
|
|
|
|
endTime, ok := subject.EndTime()
|
|
e.Expect(ok).ToBeTrue()
|
|
e.Expect(endTime).ToEqual(expectedEndTime)
|
|
})
|
|
|
|
t.Run("uses the time from WithTimestamp", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
expectedEndTime := time.Now().AddDate(5, 0, 0)
|
|
subject.End(otel.WithTimestamp(expectedEndTime))
|
|
|
|
e.Expect(subject.Ended()).ToBeTrue()
|
|
|
|
endTime, ok := subject.EndTime()
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
e.Expect(endTime).ToEqual(expectedEndTime)
|
|
})
|
|
})
|
|
|
|
t.Run("#RecordError", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("records an error", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
scenarios := []struct {
|
|
err error
|
|
typ string
|
|
msg string
|
|
}{
|
|
{
|
|
err: ottest.NewTestError("test error"),
|
|
typ: "go.opentelemetry.io/otel/internal/testing.TestError",
|
|
msg: "test error",
|
|
},
|
|
{
|
|
err: errors.New("test error 2"),
|
|
typ: "*errors.errorString",
|
|
msg: "test error 2",
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
testTime := time.Now()
|
|
subject.RecordError(s.err, otel.WithTimestamp(testTime))
|
|
|
|
expectedEvents := []oteltest.Event{{
|
|
Timestamp: testTime,
|
|
Name: "error",
|
|
Attributes: map[label.Key]label.Value{
|
|
label.Key("error.type"): label.StringValue(s.typ),
|
|
label.Key("error.message"): label.StringValue(s.msg),
|
|
},
|
|
}}
|
|
e.Expect(subject.Events()).ToEqual(expectedEvents)
|
|
e.Expect(subject.StatusCode()).ToEqual(codes.Error)
|
|
e.Expect(subject.StatusMessage()).ToEqual("")
|
|
}
|
|
})
|
|
|
|
t.Run("sets span status if provided", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
errMsg := "test error message"
|
|
testErr := ottest.NewTestError(errMsg)
|
|
testTime := time.Now()
|
|
subject.RecordError(testErr, otel.WithTimestamp(testTime))
|
|
|
|
expectedEvents := []oteltest.Event{{
|
|
Timestamp: testTime,
|
|
Name: "error",
|
|
Attributes: map[label.Key]label.Value{
|
|
label.Key("error.type"): label.StringValue("go.opentelemetry.io/otel/internal/testing.TestError"),
|
|
label.Key("error.message"): label.StringValue(errMsg),
|
|
},
|
|
}}
|
|
e.Expect(subject.Events()).ToEqual(expectedEvents)
|
|
e.Expect(subject.StatusCode()).ToEqual(codes.Error)
|
|
})
|
|
|
|
t.Run("cannot be set after the span has ended", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
subject.End()
|
|
subject.RecordError(errors.New("ignored error"))
|
|
|
|
e.Expect(len(subject.Events())).ToEqual(0)
|
|
})
|
|
|
|
t.Run("has no effect with nil error", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
subject.RecordError(nil)
|
|
|
|
e.Expect(len(subject.Events())).ToEqual(0)
|
|
})
|
|
})
|
|
|
|
t.Run("#IsRecording", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("returns true", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, subject := tracer.Start(context.Background(), "test")
|
|
|
|
e.Expect(subject.IsRecording()).ToBeTrue()
|
|
})
|
|
})
|
|
|
|
t.Run("#SpanContext", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("returns a valid SpanContext", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, subject := tracer.Start(context.Background(), "test")
|
|
|
|
e.Expect(subject.SpanContext().IsValid()).ToBeTrue()
|
|
})
|
|
|
|
t.Run("returns a consistent value", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, subject := tracer.Start(context.Background(), "test")
|
|
|
|
e.Expect(subject.SpanContext()).ToEqual(subject.SpanContext())
|
|
})
|
|
})
|
|
|
|
t.Run("#Name", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("returns the most recently set name on the span", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
originalName := "test"
|
|
_, span := tracer.Start(context.Background(), originalName)
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
e.Expect(subject.Name()).ToEqual(originalName)
|
|
|
|
subject.SetName("in-between")
|
|
|
|
newName := "new name"
|
|
|
|
subject.SetName(newName)
|
|
|
|
e.Expect(subject.Name()).ToEqual(newName)
|
|
})
|
|
|
|
t.Run("cannot be changed after the span has been ended", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
originalName := "test"
|
|
_, span := tracer.Start(context.Background(), originalName)
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
subject.End()
|
|
subject.SetName("new name")
|
|
|
|
e.Expect(subject.Name()).ToEqual(originalName)
|
|
})
|
|
})
|
|
|
|
t.Run("#Attributes", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("returns an empty map by default", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
e.Expect(subject.Attributes()).ToEqual(map[label.Key]label.Value{})
|
|
})
|
|
|
|
t.Run("returns the most recently set attributes", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
attr1 := label.String("key1", "value1")
|
|
attr2 := label.String("key2", "value2")
|
|
attr3 := label.String("key3", "value3")
|
|
unexpectedAttr := attr2.Key.String("unexpected")
|
|
|
|
subject.SetAttributes(attr1, unexpectedAttr, attr3)
|
|
subject.SetAttributes(attr2)
|
|
|
|
attributes := subject.Attributes()
|
|
|
|
e.Expect(attributes[attr1.Key]).ToEqual(attr1.Value)
|
|
e.Expect(attributes[attr2.Key]).ToEqual(attr2.Value)
|
|
e.Expect(attributes[attr3.Key]).ToEqual(attr3.Value)
|
|
})
|
|
|
|
t.Run("cannot be changed after the span has been ended", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
expectedAttr := label.String("key", "value")
|
|
subject.SetAttributes(expectedAttr)
|
|
subject.End()
|
|
|
|
unexpectedAttr := expectedAttr.Key.String("unexpected")
|
|
subject.SetAttributes(unexpectedAttr)
|
|
subject.End()
|
|
|
|
attributes := subject.Attributes()
|
|
e.Expect(attributes[expectedAttr.Key]).ToEqual(expectedAttr.Value)
|
|
})
|
|
|
|
t.Run("can be used concurrently with setter", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(2)
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
subject.SetAttributes(label.String("key", "value"))
|
|
}()
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
subject.Attributes()
|
|
}()
|
|
|
|
wg.Wait()
|
|
})
|
|
})
|
|
|
|
t.Run("#Links", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("returns an empty map by default", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
e.Expect(len(subject.Links())).ToEqual(0)
|
|
})
|
|
})
|
|
|
|
t.Run("#Events", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("returns an empty slice by default", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
e.Expect(len(subject.Events())).ToEqual(0)
|
|
})
|
|
|
|
t.Run("returns all of the added events", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
event1Name := "event1"
|
|
event1Attributes := []label.KeyValue{
|
|
label.String("event1Attr1", "foo"),
|
|
label.String("event1Attr2", "bar"),
|
|
}
|
|
|
|
event1Start := time.Now()
|
|
subject.AddEvent(event1Name, otel.WithAttributes(event1Attributes...))
|
|
event1End := time.Now()
|
|
|
|
event2Timestamp := time.Now().AddDate(5, 0, 0)
|
|
event2Name := "event1"
|
|
event2Attributes := []label.KeyValue{
|
|
label.String("event2Attr", "abc"),
|
|
}
|
|
|
|
subject.AddEvent(event2Name, otel.WithTimestamp(event2Timestamp), otel.WithAttributes(event2Attributes...))
|
|
|
|
events := subject.Events()
|
|
|
|
e.Expect(len(events)).ToEqual(2)
|
|
|
|
event1 := events[0]
|
|
|
|
e.Expect(event1.Timestamp).ToBeTemporally(matchers.AfterOrSameTime, event1Start)
|
|
e.Expect(event1.Timestamp).ToBeTemporally(matchers.BeforeOrSameTime, event1End)
|
|
e.Expect(event1.Name).ToEqual(event1Name)
|
|
|
|
for _, attr := range event1Attributes {
|
|
e.Expect(event1.Attributes[attr.Key]).ToEqual(attr.Value)
|
|
}
|
|
|
|
event2 := events[1]
|
|
|
|
e.Expect(event2.Timestamp).ToEqual(event2Timestamp)
|
|
e.Expect(event2.Name).ToEqual(event2Name)
|
|
|
|
for _, attr := range event2Attributes {
|
|
e.Expect(event2.Attributes[attr.Key]).ToEqual(attr.Value)
|
|
}
|
|
})
|
|
|
|
t.Run("cannot be changed after the span has been ended", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
subject.AddEvent("test")
|
|
|
|
e.Expect(len(subject.Events())).ToEqual(1)
|
|
|
|
expectedEvent := subject.Events()[0]
|
|
|
|
subject.End()
|
|
subject.AddEvent("should not occur")
|
|
|
|
e.Expect(len(subject.Events())).ToEqual(1)
|
|
e.Expect(subject.Events()[0]).ToEqual(expectedEvent)
|
|
})
|
|
})
|
|
|
|
t.Run("#Status", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("defaults to OK", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
e.Expect(subject.StatusCode()).ToEqual(codes.Unset)
|
|
|
|
subject.End()
|
|
|
|
e.Expect(subject.StatusCode()).ToEqual(codes.Unset)
|
|
})
|
|
|
|
statuses := []codes.Code{
|
|
codes.Unset,
|
|
codes.Error,
|
|
codes.Ok,
|
|
}
|
|
|
|
for _, status := range statuses {
|
|
t.Run("returns the most recently set status on the span", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
subject.SetStatus(codes.Ok, "Ok")
|
|
subject.SetStatus(status, "Yo!")
|
|
|
|
e.Expect(subject.StatusCode()).ToEqual(status)
|
|
e.Expect(subject.StatusMessage()).ToEqual("Yo!")
|
|
})
|
|
|
|
t.Run("cannot be changed after the span has been ended", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test")
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
|
|
originalStatus := codes.Ok
|
|
|
|
subject.SetStatus(originalStatus, "OK")
|
|
subject.End()
|
|
subject.SetStatus(status, fmt.Sprint(status))
|
|
|
|
e.Expect(subject.StatusCode()).ToEqual(originalStatus)
|
|
e.Expect(subject.StatusMessage()).ToEqual("OK")
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("#SpanKind", func(t *testing.T) {
|
|
tp := oteltest.NewTracerProvider()
|
|
t.Run("returns the value given at start", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
e := matchers.NewExpecter(t)
|
|
|
|
tracer := tp.Tracer(t.Name())
|
|
_, span := tracer.Start(context.Background(), "test",
|
|
otel.WithSpanKind(otel.SpanKindConsumer))
|
|
|
|
subject, ok := span.(*oteltest.Span)
|
|
e.Expect(ok).ToBeTrue()
|
|
subject.End()
|
|
|
|
e.Expect(subject.SpanKind()).ToEqual(otel.SpanKindConsumer)
|
|
})
|
|
})
|
|
}
|