mirror of
https://github.com/dutchcoders/transfer.sh.git
synced 2024-12-29 05:40:18 +01:00
897 lines
24 KiB
Go
897 lines
24 KiB
Go
// Copyright 2017, OpenCensus 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 trace
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"go.opencensus.io/trace/tracestate"
|
|
)
|
|
|
|
var (
|
|
tid = TraceID{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 4, 8, 16, 32, 64, 128}
|
|
sid = SpanID{1, 2, 4, 8, 16, 32, 64, 128}
|
|
testTracestate, _ = tracestate.New(nil, tracestate.Entry{Key: "foo", Value: "bar"})
|
|
)
|
|
|
|
func init() {
|
|
// no random sampling, but sample children of sampled spans.
|
|
ApplyConfig(Config{DefaultSampler: ProbabilitySampler(0)})
|
|
}
|
|
|
|
func TestStrings(t *testing.T) {
|
|
if got, want := tid.String(), "01020304050607080102040810204080"; got != want {
|
|
t.Errorf("TraceID.String: got %q want %q", got, want)
|
|
}
|
|
if got, want := sid.String(), "0102040810204080"; got != want {
|
|
t.Errorf("SpanID.String: got %q want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFromContext(t *testing.T) {
|
|
want := &Span{}
|
|
ctx := NewContext(context.Background(), want)
|
|
got := FromContext(ctx)
|
|
if got != want {
|
|
t.Errorf("got Span pointer %p want %p", got, want)
|
|
}
|
|
}
|
|
|
|
type foo int
|
|
|
|
func (f foo) String() string {
|
|
return "foo"
|
|
}
|
|
|
|
// checkChild tests that c has fields set appropriately, given that it is a child span of p.
|
|
func checkChild(p SpanContext, c *Span) error {
|
|
if c == nil {
|
|
return fmt.Errorf("got nil child span, want non-nil")
|
|
}
|
|
if got, want := c.spanContext.TraceID, p.TraceID; got != want {
|
|
return fmt.Errorf("got child trace ID %s, want %s", got, want)
|
|
}
|
|
if childID, parentID := c.spanContext.SpanID, p.SpanID; childID == parentID {
|
|
return fmt.Errorf("got child span ID %s, parent span ID %s; want unequal IDs", childID, parentID)
|
|
}
|
|
if got, want := c.spanContext.TraceOptions, p.TraceOptions; got != want {
|
|
return fmt.Errorf("got child trace options %d, want %d", got, want)
|
|
}
|
|
if got, want := c.spanContext.Tracestate, p.Tracestate; got != want {
|
|
return fmt.Errorf("got child tracestate %v, want %v", got, want)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func TestStartSpan(t *testing.T) {
|
|
ctx, _ := StartSpan(context.Background(), "StartSpan")
|
|
if FromContext(ctx).data != nil {
|
|
t.Error("StartSpan: new span is recording events")
|
|
}
|
|
}
|
|
|
|
func TestSampling(t *testing.T) {
|
|
for _, test := range []struct {
|
|
remoteParent bool
|
|
localParent bool
|
|
parentTraceOptions TraceOptions
|
|
sampler Sampler
|
|
wantTraceOptions TraceOptions
|
|
}{
|
|
{true, false, 0, nil, 0},
|
|
{true, false, 1, nil, 1},
|
|
{true, false, 0, NeverSample(), 0},
|
|
{true, false, 1, NeverSample(), 0},
|
|
{true, false, 0, AlwaysSample(), 1},
|
|
{true, false, 1, AlwaysSample(), 1},
|
|
{false, true, 0, NeverSample(), 0},
|
|
{false, true, 1, NeverSample(), 0},
|
|
{false, true, 0, AlwaysSample(), 1},
|
|
{false, true, 1, AlwaysSample(), 1},
|
|
{false, false, 0, nil, 0},
|
|
{false, false, 0, NeverSample(), 0},
|
|
{false, false, 0, AlwaysSample(), 1},
|
|
} {
|
|
var ctx context.Context
|
|
if test.remoteParent {
|
|
sc := SpanContext{
|
|
TraceID: tid,
|
|
SpanID: sid,
|
|
TraceOptions: test.parentTraceOptions,
|
|
}
|
|
ctx, _ = StartSpanWithRemoteParent(context.Background(), "foo", sc, WithSampler(test.sampler))
|
|
} else if test.localParent {
|
|
sampler := NeverSample()
|
|
if test.parentTraceOptions == 1 {
|
|
sampler = AlwaysSample()
|
|
}
|
|
ctx2, _ := StartSpan(context.Background(), "foo", WithSampler(sampler))
|
|
ctx, _ = StartSpan(ctx2, "foo", WithSampler(test.sampler))
|
|
} else {
|
|
ctx, _ = StartSpan(context.Background(), "foo", WithSampler(test.sampler))
|
|
}
|
|
sc := FromContext(ctx).SpanContext()
|
|
if (sc == SpanContext{}) {
|
|
t.Errorf("case %#v: starting new span: no span in context", test)
|
|
continue
|
|
}
|
|
if sc.SpanID == (SpanID{}) {
|
|
t.Errorf("case %#v: starting new span: got zero SpanID, want nonzero", test)
|
|
}
|
|
if sc.TraceOptions != test.wantTraceOptions {
|
|
t.Errorf("case %#v: starting new span: got TraceOptions %x, want %x", test, sc.TraceOptions, test.wantTraceOptions)
|
|
}
|
|
}
|
|
|
|
// Test that for children of local spans, the default sampler has no effect.
|
|
for _, test := range []struct {
|
|
parentTraceOptions TraceOptions
|
|
wantTraceOptions TraceOptions
|
|
}{
|
|
{0, 0},
|
|
{0, 0},
|
|
{1, 1},
|
|
{1, 1},
|
|
} {
|
|
for _, defaultSampler := range []Sampler{
|
|
NeverSample(),
|
|
AlwaysSample(),
|
|
ProbabilitySampler(0),
|
|
} {
|
|
ApplyConfig(Config{DefaultSampler: defaultSampler})
|
|
sampler := NeverSample()
|
|
if test.parentTraceOptions == 1 {
|
|
sampler = AlwaysSample()
|
|
}
|
|
ctx2, _ := StartSpan(context.Background(), "foo", WithSampler(sampler))
|
|
ctx, _ := StartSpan(ctx2, "foo")
|
|
sc := FromContext(ctx).SpanContext()
|
|
if (sc == SpanContext{}) {
|
|
t.Errorf("case %#v: starting new child of local span: no span in context", test)
|
|
continue
|
|
}
|
|
if sc.SpanID == (SpanID{}) {
|
|
t.Errorf("case %#v: starting new child of local span: got zero SpanID, want nonzero", test)
|
|
}
|
|
if sc.TraceOptions != test.wantTraceOptions {
|
|
t.Errorf("case %#v: starting new child of local span: got TraceOptions %x, want %x", test, sc.TraceOptions, test.wantTraceOptions)
|
|
}
|
|
}
|
|
}
|
|
ApplyConfig(Config{DefaultSampler: ProbabilitySampler(0)}) // reset the default sampler.
|
|
}
|
|
|
|
func TestProbabilitySampler(t *testing.T) {
|
|
exported := 0
|
|
for i := 0; i < 1000; i++ {
|
|
_, span := StartSpan(context.Background(), "foo", WithSampler(ProbabilitySampler(0.3)))
|
|
if span.SpanContext().IsSampled() {
|
|
exported++
|
|
}
|
|
}
|
|
if exported < 200 || exported > 400 {
|
|
t.Errorf("got %f%% exported spans, want approximately 30%%", float64(exported)*0.1)
|
|
}
|
|
}
|
|
|
|
func TestStartSpanWithRemoteParent(t *testing.T) {
|
|
sc := SpanContext{
|
|
TraceID: tid,
|
|
SpanID: sid,
|
|
TraceOptions: 0x0,
|
|
}
|
|
ctx, _ := StartSpanWithRemoteParent(context.Background(), "startSpanWithRemoteParent", sc)
|
|
if err := checkChild(sc, FromContext(ctx)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
ctx, _ = StartSpanWithRemoteParent(context.Background(), "startSpanWithRemoteParent", sc)
|
|
if err := checkChild(sc, FromContext(ctx)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
sc = SpanContext{
|
|
TraceID: tid,
|
|
SpanID: sid,
|
|
TraceOptions: 0x1,
|
|
Tracestate: testTracestate,
|
|
}
|
|
ctx, _ = StartSpanWithRemoteParent(context.Background(), "startSpanWithRemoteParent", sc)
|
|
if err := checkChild(sc, FromContext(ctx)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
ctx, _ = StartSpanWithRemoteParent(context.Background(), "startSpanWithRemoteParent", sc)
|
|
if err := checkChild(sc, FromContext(ctx)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
ctx2, _ := StartSpan(ctx, "StartSpan")
|
|
parent := FromContext(ctx).SpanContext()
|
|
if err := checkChild(parent, FromContext(ctx2)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
// startSpan returns a context with a new Span that is recording events and will be exported.
|
|
func startSpan(o StartOptions) *Span {
|
|
_, span := StartSpanWithRemoteParent(context.Background(), "span0",
|
|
SpanContext{
|
|
TraceID: tid,
|
|
SpanID: sid,
|
|
TraceOptions: 1,
|
|
},
|
|
WithSampler(o.Sampler),
|
|
WithSpanKind(o.SpanKind),
|
|
)
|
|
return span
|
|
}
|
|
|
|
type testExporter struct {
|
|
spans []*SpanData
|
|
}
|
|
|
|
func (t *testExporter) ExportSpan(s *SpanData) {
|
|
t.spans = append(t.spans, s)
|
|
}
|
|
|
|
// endSpan ends the Span in the context and returns the exported SpanData.
|
|
//
|
|
// It also does some tests on the Span, and tests and clears some fields in the SpanData.
|
|
func endSpan(span *Span) (*SpanData, error) {
|
|
|
|
if !span.IsRecordingEvents() {
|
|
return nil, fmt.Errorf("IsRecordingEvents: got false, want true")
|
|
}
|
|
if !span.SpanContext().IsSampled() {
|
|
return nil, fmt.Errorf("IsSampled: got false, want true")
|
|
}
|
|
var te testExporter
|
|
RegisterExporter(&te)
|
|
span.End()
|
|
UnregisterExporter(&te)
|
|
if len(te.spans) != 1 {
|
|
return nil, fmt.Errorf("got exported spans %#v, want one span", te.spans)
|
|
}
|
|
got := te.spans[0]
|
|
if got.SpanContext.SpanID == (SpanID{}) {
|
|
return nil, fmt.Errorf("exporting span: expected nonzero SpanID")
|
|
}
|
|
got.SpanContext.SpanID = SpanID{}
|
|
if !checkTime(&got.StartTime) {
|
|
return nil, fmt.Errorf("exporting span: expected nonzero StartTime")
|
|
}
|
|
if !checkTime(&got.EndTime) {
|
|
return nil, fmt.Errorf("exporting span: expected nonzero EndTime")
|
|
}
|
|
return got, nil
|
|
}
|
|
|
|
// checkTime checks that a nonzero time was set in x, then clears it.
|
|
func checkTime(x *time.Time) bool {
|
|
if x.IsZero() {
|
|
return false
|
|
}
|
|
*x = time.Time{}
|
|
return true
|
|
}
|
|
|
|
func TestSpanKind(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
startOptions StartOptions
|
|
want *SpanData
|
|
}{
|
|
{
|
|
name: "zero StartOptions",
|
|
startOptions: StartOptions{},
|
|
want: &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
SpanKind: SpanKindUnspecified,
|
|
HasRemoteParent: true,
|
|
},
|
|
},
|
|
{
|
|
name: "client span",
|
|
startOptions: StartOptions{
|
|
SpanKind: SpanKindClient,
|
|
},
|
|
want: &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
SpanKind: SpanKindClient,
|
|
HasRemoteParent: true,
|
|
},
|
|
},
|
|
{
|
|
name: "server span",
|
|
startOptions: StartOptions{
|
|
SpanKind: SpanKindServer,
|
|
},
|
|
want: &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
SpanKind: SpanKindServer,
|
|
HasRemoteParent: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
span := startSpan(tt.startOptions)
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("exporting span: got %#v want %#v", got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSetSpanAttributes(t *testing.T) {
|
|
span := startSpan(StartOptions{})
|
|
span.AddAttributes(StringAttribute("key1", "value1"))
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
want := &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
Attributes: map[string]interface{}{"key1": "value1"},
|
|
HasRemoteParent: true,
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("exporting span: got %#v want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestSetSpanAttributesOverLimit(t *testing.T) {
|
|
cfg := Config{MaxAttributesPerSpan: 2}
|
|
ApplyConfig(cfg)
|
|
|
|
span := startSpan(StartOptions{})
|
|
span.AddAttributes(StringAttribute("key1", "value1"))
|
|
span.AddAttributes(StringAttribute("key2", "value2"))
|
|
span.AddAttributes(StringAttribute("key1", "value3")) // Replace key1.
|
|
span.AddAttributes(StringAttribute("key4", "value4")) // Remove key2 and add key4
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
want := &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
Attributes: map[string]interface{}{"key1": "value3", "key4": "value4"},
|
|
HasRemoteParent: true,
|
|
DroppedAttributeCount: 1,
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("exporting span: got %#v want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestAnnotations(t *testing.T) {
|
|
span := startSpan(StartOptions{})
|
|
span.Annotatef([]Attribute{StringAttribute("key1", "value1")}, "%f", 1.5)
|
|
span.Annotate([]Attribute{StringAttribute("key2", "value2")}, "Annotate")
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for i := range got.Annotations {
|
|
if !checkTime(&got.Annotations[i].Time) {
|
|
t.Error("exporting span: expected nonzero Annotation Time")
|
|
}
|
|
}
|
|
|
|
want := &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
Annotations: []Annotation{
|
|
{Message: "1.500000", Attributes: map[string]interface{}{"key1": "value1"}},
|
|
{Message: "Annotate", Attributes: map[string]interface{}{"key2": "value2"}},
|
|
},
|
|
HasRemoteParent: true,
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("exporting span: got %#v want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestAnnotationsOverLimit(t *testing.T) {
|
|
cfg := Config{MaxAnnotationEventsPerSpan: 2}
|
|
ApplyConfig(cfg)
|
|
span := startSpan(StartOptions{})
|
|
span.Annotatef([]Attribute{StringAttribute("key4", "value4")}, "%d", 1)
|
|
span.Annotate([]Attribute{StringAttribute("key3", "value3")}, "Annotate oldest")
|
|
span.Annotatef([]Attribute{StringAttribute("key1", "value1")}, "%f", 1.5)
|
|
span.Annotate([]Attribute{StringAttribute("key2", "value2")}, "Annotate")
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for i := range got.Annotations {
|
|
if !checkTime(&got.Annotations[i].Time) {
|
|
t.Error("exporting span: expected nonzero Annotation Time")
|
|
}
|
|
}
|
|
|
|
want := &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
Annotations: []Annotation{
|
|
{Message: "1.500000", Attributes: map[string]interface{}{"key1": "value1"}},
|
|
{Message: "Annotate", Attributes: map[string]interface{}{"key2": "value2"}},
|
|
},
|
|
DroppedAnnotationCount: 2,
|
|
HasRemoteParent: true,
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("exporting span: got %#v want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestMessageEvents(t *testing.T) {
|
|
span := startSpan(StartOptions{})
|
|
span.AddMessageReceiveEvent(3, 400, 300)
|
|
span.AddMessageSendEvent(1, 200, 100)
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for i := range got.MessageEvents {
|
|
if !checkTime(&got.MessageEvents[i].Time) {
|
|
t.Error("exporting span: expected nonzero MessageEvent Time")
|
|
}
|
|
}
|
|
|
|
want := &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
MessageEvents: []MessageEvent{
|
|
{EventType: 2, MessageID: 0x3, UncompressedByteSize: 0x190, CompressedByteSize: 0x12c},
|
|
{EventType: 1, MessageID: 0x1, UncompressedByteSize: 0xc8, CompressedByteSize: 0x64},
|
|
},
|
|
HasRemoteParent: true,
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("exporting span: got %#v want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestMessageEventsOverLimit(t *testing.T) {
|
|
cfg := Config{MaxMessageEventsPerSpan: 2}
|
|
ApplyConfig(cfg)
|
|
span := startSpan(StartOptions{})
|
|
span.AddMessageReceiveEvent(5, 300, 120)
|
|
span.AddMessageSendEvent(4, 100, 50)
|
|
span.AddMessageReceiveEvent(3, 400, 300)
|
|
span.AddMessageSendEvent(1, 200, 100)
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for i := range got.MessageEvents {
|
|
if !checkTime(&got.MessageEvents[i].Time) {
|
|
t.Error("exporting span: expected nonzero MessageEvent Time")
|
|
}
|
|
}
|
|
|
|
want := &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
MessageEvents: []MessageEvent{
|
|
{EventType: 2, MessageID: 0x3, UncompressedByteSize: 0x190, CompressedByteSize: 0x12c},
|
|
{EventType: 1, MessageID: 0x1, UncompressedByteSize: 0xc8, CompressedByteSize: 0x64},
|
|
},
|
|
DroppedMessageEventCount: 2,
|
|
HasRemoteParent: true,
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("exporting span: got %#v want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestSetSpanName(t *testing.T) {
|
|
want := "SpanName-1"
|
|
span := startSpan(StartOptions{})
|
|
span.SetName(want)
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if got.Name != want {
|
|
t.Errorf("span.Name=%q; want %q", got.Name, want)
|
|
}
|
|
}
|
|
|
|
func TestSetSpanNameUnsampledSpan(t *testing.T) {
|
|
var nilSpanData *SpanData
|
|
span := startSpan(StartOptions{Sampler: NeverSample()})
|
|
span.SetName("NoopName")
|
|
|
|
if want, got := nilSpanData, span.data; want != got {
|
|
t.Errorf("span.data=%+v; want %+v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestSetSpanNameAfterSpanEnd(t *testing.T) {
|
|
want := "SpanName-2"
|
|
span := startSpan(StartOptions{})
|
|
span.SetName(want)
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// updating name after span.End
|
|
span.SetName("NoopName")
|
|
|
|
// exported span should not be updated by previous call to SetName
|
|
if got.Name != want {
|
|
t.Errorf("span.Name=%q; want %q", got.Name, want)
|
|
}
|
|
|
|
// span should not be exported again
|
|
var te testExporter
|
|
RegisterExporter(&te)
|
|
span.End()
|
|
UnregisterExporter(&te)
|
|
if len(te.spans) != 0 {
|
|
t.Errorf("got exported spans %#v, wanted no spans", te.spans)
|
|
}
|
|
}
|
|
|
|
func TestSetSpanStatus(t *testing.T) {
|
|
span := startSpan(StartOptions{})
|
|
span.SetStatus(Status{Code: int32(1), Message: "request failed"})
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
want := &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
Status: Status{Code: 1, Message: "request failed"},
|
|
HasRemoteParent: true,
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("exporting span: got %#v want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestAddLink(t *testing.T) {
|
|
span := startSpan(StartOptions{})
|
|
span.AddLink(Link{
|
|
TraceID: tid,
|
|
SpanID: sid,
|
|
Type: LinkTypeParent,
|
|
Attributes: map[string]interface{}{"key5": "value5"},
|
|
})
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
want := &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
Links: []Link{{
|
|
TraceID: tid,
|
|
SpanID: sid,
|
|
Type: 2,
|
|
Attributes: map[string]interface{}{"key5": "value5"},
|
|
}},
|
|
HasRemoteParent: true,
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("exporting span: got %#v want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestAddLinkOverLimit(t *testing.T) {
|
|
cfg := Config{MaxLinksPerSpan: 1}
|
|
ApplyConfig(cfg)
|
|
span := startSpan(StartOptions{})
|
|
span.AddLink(Link{
|
|
TraceID: tid,
|
|
SpanID: sid,
|
|
Type: LinkTypeParent,
|
|
Attributes: map[string]interface{}{"key4": "value4"},
|
|
})
|
|
span.AddLink(Link{
|
|
TraceID: tid,
|
|
SpanID: sid,
|
|
Type: LinkTypeParent,
|
|
Attributes: map[string]interface{}{"key5": "value5"},
|
|
})
|
|
got, err := endSpan(span)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
want := &SpanData{
|
|
SpanContext: SpanContext{
|
|
TraceID: tid,
|
|
SpanID: SpanID{},
|
|
TraceOptions: 0x1,
|
|
},
|
|
ParentSpanID: sid,
|
|
Name: "span0",
|
|
Links: []Link{{
|
|
TraceID: tid,
|
|
SpanID: sid,
|
|
Type: 2,
|
|
Attributes: map[string]interface{}{"key5": "value5"},
|
|
}},
|
|
DroppedLinkCount: 1,
|
|
HasRemoteParent: true,
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("exporting span: got %#v want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestUnregisterExporter(t *testing.T) {
|
|
var te testExporter
|
|
RegisterExporter(&te)
|
|
UnregisterExporter(&te)
|
|
|
|
ctx := startSpan(StartOptions{})
|
|
endSpan(ctx)
|
|
if len(te.spans) != 0 {
|
|
t.Error("unregistered Exporter was called")
|
|
}
|
|
}
|
|
|
|
func TestBucket(t *testing.T) {
|
|
// make a bucket of size 5 and add 10 spans
|
|
b := makeBucket(5)
|
|
for i := 1; i <= 10; i++ {
|
|
b.nextTime = time.Time{} // reset the time so that the next span is accepted.
|
|
// add a span, with i stored in the TraceID so we can test for it later.
|
|
b.add(&SpanData{SpanContext: SpanContext{TraceID: TraceID{byte(i)}}, EndTime: time.Now()})
|
|
if i <= 5 {
|
|
if b.size() != i {
|
|
t.Fatalf("got bucket size %d, want %d %#v\n", b.size(), i, b)
|
|
}
|
|
for j := 0; j < i; j++ {
|
|
if b.span(j).TraceID[0] != byte(j+1) {
|
|
t.Errorf("got span index %d, want %d\n", b.span(j).TraceID[0], j+1)
|
|
}
|
|
}
|
|
} else {
|
|
if b.size() != 5 {
|
|
t.Fatalf("got bucket size %d, want 5\n", b.size())
|
|
}
|
|
for j := 0; j < 5; j++ {
|
|
want := i - 4 + j
|
|
if b.span(j).TraceID[0] != byte(want) {
|
|
t.Errorf("got span index %d, want %d\n", b.span(j).TraceID[0], want)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// expand the bucket
|
|
b.resize(20)
|
|
if b.size() != 5 {
|
|
t.Fatalf("after resizing upwards: got bucket size %d, want 5\n", b.size())
|
|
}
|
|
for i := 0; i < 5; i++ {
|
|
want := 6 + i
|
|
if b.span(i).TraceID[0] != byte(want) {
|
|
t.Errorf("after resizing upwards: got span index %d, want %d\n", b.span(i).TraceID[0], want)
|
|
}
|
|
}
|
|
// shrink the bucket
|
|
b.resize(3)
|
|
if b.size() != 3 {
|
|
t.Fatalf("after resizing downwards: got bucket size %d, want 3\n", b.size())
|
|
}
|
|
for i := 0; i < 3; i++ {
|
|
want := 8 + i
|
|
if b.span(i).TraceID[0] != byte(want) {
|
|
t.Errorf("after resizing downwards: got span index %d, want %d\n", b.span(i).TraceID[0], want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type exporter map[string]*SpanData
|
|
|
|
func (e exporter) ExportSpan(s *SpanData) {
|
|
e[s.Name] = s
|
|
}
|
|
|
|
func Test_Issue328_EndSpanTwice(t *testing.T) {
|
|
spans := make(exporter)
|
|
RegisterExporter(&spans)
|
|
defer UnregisterExporter(&spans)
|
|
ctx := context.Background()
|
|
ctx, span := StartSpan(ctx, "span-1", WithSampler(AlwaysSample()))
|
|
span.End()
|
|
span.End()
|
|
UnregisterExporter(&spans)
|
|
if len(spans) != 1 {
|
|
t.Fatalf("expected only a single span, got %#v", spans)
|
|
}
|
|
}
|
|
|
|
func TestStartSpanAfterEnd(t *testing.T) {
|
|
spans := make(exporter)
|
|
RegisterExporter(&spans)
|
|
defer UnregisterExporter(&spans)
|
|
ctx, span0 := StartSpan(context.Background(), "parent", WithSampler(AlwaysSample()))
|
|
ctx1, span1 := StartSpan(ctx, "span-1", WithSampler(AlwaysSample()))
|
|
span1.End()
|
|
// Start a new span with the context containing span-1
|
|
// even though span-1 is ended, we still add this as a new child of span-1
|
|
_, span2 := StartSpan(ctx1, "span-2", WithSampler(AlwaysSample()))
|
|
span2.End()
|
|
span0.End()
|
|
UnregisterExporter(&spans)
|
|
if got, want := len(spans), 3; got != want {
|
|
t.Fatalf("len(%#v) = %d; want %d", spans, got, want)
|
|
}
|
|
if got, want := spans["span-1"].TraceID, spans["parent"].TraceID; got != want {
|
|
t.Errorf("span-1.TraceID=%q; want %q", got, want)
|
|
}
|
|
if got, want := spans["span-2"].TraceID, spans["parent"].TraceID; got != want {
|
|
t.Errorf("span-2.TraceID=%q; want %q", got, want)
|
|
}
|
|
if got, want := spans["span-1"].ParentSpanID, spans["parent"].SpanID; got != want {
|
|
t.Errorf("span-1.ParentSpanID=%q; want %q (parent.SpanID)", got, want)
|
|
}
|
|
if got, want := spans["span-2"].ParentSpanID, spans["span-1"].SpanID; got != want {
|
|
t.Errorf("span-2.ParentSpanID=%q; want %q (span1.SpanID)", got, want)
|
|
}
|
|
}
|
|
|
|
func TestChildSpanCount(t *testing.T) {
|
|
spans := make(exporter)
|
|
RegisterExporter(&spans)
|
|
defer UnregisterExporter(&spans)
|
|
ctx, span0 := StartSpan(context.Background(), "parent", WithSampler(AlwaysSample()))
|
|
ctx1, span1 := StartSpan(ctx, "span-1", WithSampler(AlwaysSample()))
|
|
_, span2 := StartSpan(ctx1, "span-2", WithSampler(AlwaysSample()))
|
|
span2.End()
|
|
span1.End()
|
|
|
|
_, span3 := StartSpan(ctx, "span-3", WithSampler(AlwaysSample()))
|
|
span3.End()
|
|
span0.End()
|
|
UnregisterExporter(&spans)
|
|
if got, want := len(spans), 4; got != want {
|
|
t.Fatalf("len(%#v) = %d; want %d", spans, got, want)
|
|
}
|
|
if got, want := spans["span-3"].ChildSpanCount, 0; got != want {
|
|
t.Errorf("span-3.ChildSpanCount=%q; want %q", got, want)
|
|
}
|
|
if got, want := spans["span-2"].ChildSpanCount, 0; got != want {
|
|
t.Errorf("span-2.ChildSpanCount=%q; want %q", got, want)
|
|
}
|
|
if got, want := spans["span-1"].ChildSpanCount, 1; got != want {
|
|
t.Errorf("span-1.ChildSpanCount=%q; want %q", got, want)
|
|
}
|
|
if got, want := spans["parent"].ChildSpanCount, 2; got != want {
|
|
t.Errorf("parent.ChildSpanCount=%q; want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestNilSpanEnd(t *testing.T) {
|
|
var span *Span
|
|
span.End()
|
|
}
|
|
|
|
func TestExecutionTracerTaskEnd(t *testing.T) {
|
|
var n uint64
|
|
executionTracerTaskEnd := func() {
|
|
atomic.AddUint64(&n, 1)
|
|
}
|
|
|
|
var spans []*Span
|
|
_, span := StartSpan(context.Background(), "foo", WithSampler(NeverSample()))
|
|
span.executionTracerTaskEnd = executionTracerTaskEnd
|
|
spans = append(spans, span) // never sample
|
|
|
|
_, span = StartSpanWithRemoteParent(context.Background(), "foo", SpanContext{
|
|
TraceID: TraceID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
|
SpanID: SpanID{0, 1, 2, 3, 4, 5, 6, 7},
|
|
TraceOptions: 0,
|
|
})
|
|
span.executionTracerTaskEnd = executionTracerTaskEnd
|
|
spans = append(spans, span) // parent not sampled
|
|
|
|
_, span = StartSpan(context.Background(), "foo", WithSampler(AlwaysSample()))
|
|
span.executionTracerTaskEnd = executionTracerTaskEnd
|
|
spans = append(spans, span) // always sample
|
|
|
|
for _, span := range spans {
|
|
span.End()
|
|
}
|
|
if got, want := n, uint64(len(spans)); got != want {
|
|
t.Fatalf("Execution tracer task ended for %v spans; want %v", got, want)
|
|
}
|
|
}
|