2020-03-24 07:41:10 +02:00
// Copyright The OpenTelemetry Authors
2019-08-02 22:52:55 +02:00
//
// 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"
2020-02-28 23:44:53 +02:00
"errors"
2019-08-02 22:52:55 +02:00
"fmt"
2019-10-30 01:53:50 +02:00
"math"
2021-03-18 19:48:13 +02:00
"strconv"
2019-08-26 20:53:12 +02:00
"strings"
2020-09-09 19:19:03 +02:00
"sync"
2019-08-02 22:52:55 +02:00
"sync/atomic"
"testing"
"time"
"github.com/google/go-cmp/cmp"
2020-08-08 21:10:36 +02:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2019-08-26 18:41:15 +02:00
2021-11-13 18:35:04 +02:00
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
2021-01-12 18:56:16 +02:00
ottest "go.opentelemetry.io/otel/internal/internaltest"
2020-06-10 07:15:53 +02:00
"go.opentelemetry.io/otel/sdk/instrumentation"
2020-03-13 22:07:36 +02:00
"go.opentelemetry.io/otel/sdk/resource"
2022-07-13 15:55:43 +02:00
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
2021-11-13 18:35:04 +02:00
"go.opentelemetry.io/otel/trace"
2019-08-02 22:52:55 +02:00
)
2021-04-21 20:53:12 +02:00
const envVar = "OTEL_RESOURCE_ATTRIBUTES"
2020-12-11 07:28:41 +02:00
type storingHandler struct {
errs [ ] error
}
func ( s * storingHandler ) Handle ( err error ) {
s . errs = append ( s . errs , err )
}
func ( s * storingHandler ) Reset ( ) {
s . errs = nil
}
2019-08-02 22:52:55 +02:00
var (
2020-11-07 00:13:31 +02:00
tid trace . TraceID
sid trace . SpanID
2021-03-30 17:59:54 +02:00
sc trace . SpanContext
2019-08-02 22:52:55 +02:00
2021-06-08 19:10:01 +02:00
handler = & storingHandler { }
2020-12-11 07:28:41 +02:00
)
2020-06-02 22:33:19 +02:00
2019-10-23 08:01:33 +02:00
func init ( ) {
2020-11-07 00:13:31 +02:00
tid , _ = trace . TraceIDFromHex ( "01020304050607080102040810203040" )
sid , _ = trace . SpanIDFromHex ( "0102040810203040" )
2021-03-30 17:59:54 +02:00
sc = trace . NewSpanContext ( trace . SpanContextConfig {
TraceID : tid ,
SpanID : sid ,
TraceFlags : 0x1 ,
} )
2020-06-02 22:33:19 +02:00
2020-12-11 07:28:41 +02:00
otel . SetErrorHandler ( handler )
2019-10-23 08:01:33 +02:00
}
2019-10-17 20:13:57 +02:00
func TestTracerFollowsExpectedAPIBehaviour ( t * testing . T ) {
2021-07-26 17:40:36 +02:00
harness := ottest . NewHarness ( t )
2019-10-17 20:13:57 +02:00
2021-03-03 22:09:58 +02:00
harness . TestTracerProvider ( func ( ) trace . TracerProvider {
2021-03-18 18:34:47 +02:00
return NewTracerProvider ( WithSampler ( TraceIDRatioBased ( 0 ) ) )
2021-03-03 22:09:58 +02:00
} )
2021-03-18 18:34:47 +02:00
tp := NewTracerProvider ( WithSampler ( TraceIDRatioBased ( 0 ) ) )
2021-03-03 22:09:58 +02:00
harness . TestTracer ( func ( ) trace . Tracer {
return tp . Tracer ( "" )
} )
2019-10-17 20:13:57 +02:00
}
2019-08-02 22:52:55 +02:00
type testExporter struct {
2020-09-09 19:19:03 +02:00
mu sync . RWMutex
idx map [ string ] int
2021-05-05 01:45:13 +02:00
spans [ ] * snapshot
2019-08-02 22:52:55 +02:00
}
2020-09-09 19:19:03 +02:00
func NewTestExporter ( ) * testExporter {
return & testExporter { idx : make ( map [ string ] int ) }
}
2021-05-05 01:45:13 +02:00
func ( te * testExporter ) ExportSpans ( _ context . Context , spans [ ] ReadOnlySpan ) error {
2020-09-09 19:19:03 +02:00
te . mu . Lock ( )
defer te . mu . Unlock ( )
i := len ( te . spans )
2021-05-05 01:45:13 +02:00
for _ , s := range spans {
te . idx [ s . Name ( ) ] = i
te . spans = append ( te . spans , s . ( * snapshot ) )
2020-09-09 19:19:03 +02:00
i ++
}
return nil
}
2021-05-05 01:45:13 +02:00
func ( te * testExporter ) Spans ( ) [ ] * snapshot {
2020-09-09 19:19:03 +02:00
te . mu . RLock ( )
defer te . mu . RUnlock ( )
2021-05-05 01:45:13 +02:00
cp := make ( [ ] * snapshot , len ( te . spans ) )
2020-09-09 19:19:03 +02:00
copy ( cp , te . spans )
return cp
}
2021-05-05 01:45:13 +02:00
func ( te * testExporter ) GetSpan ( name string ) ( * snapshot , bool ) {
2020-09-09 19:19:03 +02:00
te . mu . RLock ( )
defer te . mu . RUnlock ( )
i , ok := te . idx [ name ]
if ! ok {
return nil , false
}
return te . spans [ i ] , true
}
func ( te * testExporter ) Len ( ) int {
te . mu . RLock ( )
defer te . mu . RUnlock ( )
return len ( te . spans )
}
func ( te * testExporter ) Shutdown ( context . Context ) error {
te . Reset ( )
return nil
}
func ( te * testExporter ) Reset ( ) {
te . mu . Lock ( )
defer te . mu . Unlock ( )
te . idx = make ( map [ string ] int )
te . spans = te . spans [ : 0 ]
2019-08-02 22:52:55 +02:00
}
2020-03-10 17:25:11 +02:00
type testSampler struct {
callCount int
prefix string
t * testing . T
}
func ( ts * testSampler ) ShouldSample ( p SamplingParameters ) SamplingResult {
ts . callCount ++
ts . t . Logf ( "called sampler for name %q" , p . Name )
2020-09-22 19:34:43 +02:00
decision := Drop
2020-03-10 17:25:11 +02:00
if strings . HasPrefix ( p . Name , ts . prefix ) {
2020-09-22 19:34:43 +02:00
decision = RecordAndSample
2020-03-10 17:25:11 +02:00
}
2021-02-18 19:59:37 +02:00
return SamplingResult { Decision : decision , Attributes : [ ] attribute . KeyValue { attribute . Int ( "callCount" , ts . callCount ) } }
2020-03-10 17:25:11 +02:00
}
func ( ts testSampler ) Description ( ) string {
return "testSampler"
}
2019-08-26 20:53:12 +02:00
func TestSetName ( t * testing . T ) {
2021-02-18 19:49:10 +02:00
tp := NewTracerProvider ( )
2019-10-22 22:19:11 +02:00
2019-08-26 20:53:12 +02:00
type testCase struct {
2021-02-18 19:49:10 +02:00
name string
newName string
2019-08-26 20:53:12 +02:00
}
for idx , tt := range [ ] testCase {
{ // 0
2021-02-18 19:49:10 +02:00
name : "foobar" ,
newName : "foobaz" ,
2019-08-26 20:53:12 +02:00
} ,
{ // 1
2021-02-18 19:49:10 +02:00
name : "foobar" ,
newName : "barbaz" ,
2019-08-26 20:53:12 +02:00
} ,
{ // 2
2021-02-18 19:49:10 +02:00
name : "barbar" ,
newName : "barbaz" ,
2019-08-26 20:53:12 +02:00
} ,
{ // 3
2021-02-18 19:49:10 +02:00
name : "barbar" ,
newName : "foobar" ,
2019-08-26 20:53:12 +02:00
} ,
} {
2021-02-18 19:49:10 +02:00
sp := startNamedSpan ( tp , "SetName" , tt . name )
2021-09-02 23:30:12 +02:00
if sdkspan , ok := sp . ( * recordingSpan ) ; ok {
2021-02-18 19:49:10 +02:00
if sdkspan . Name ( ) != tt . name {
t . Errorf ( "%d: invalid name at span creation, expected %v, got %v" , idx , tt . name , sdkspan . Name ( ) )
}
} else {
t . Errorf ( "%d: unable to coerce span to SDK span, is type %T" , idx , sp )
2019-08-26 20:53:12 +02:00
}
2021-02-18 19:49:10 +02:00
sp . SetName ( tt . newName )
2021-09-02 23:30:12 +02:00
if sdkspan , ok := sp . ( * recordingSpan ) ; ok {
2021-02-18 19:49:10 +02:00
if sdkspan . Name ( ) != tt . newName {
t . Errorf ( "%d: span name not changed, expected %v, got %v" , idx , tt . newName , sdkspan . Name ( ) )
}
} else {
t . Errorf ( "%d: unable to coerce span to SDK span, is type %T" , idx , sp )
2019-08-26 20:53:12 +02:00
}
2021-02-18 19:49:10 +02:00
sp . End ( )
2019-08-26 20:53:12 +02:00
}
}
2021-03-30 22:26:42 +02:00
func TestSpanIsRecording ( t * testing . T ) {
t . Run ( "while Span active" , func ( t * testing . T ) {
for name , tc := range map [ string ] struct {
sampler Sampler
want bool
} {
"Always sample, recording on" : { sampler : AlwaysSample ( ) , want : true } ,
"Never sample recording off" : { sampler : NeverSample ( ) , want : false } ,
} {
tp := NewTracerProvider ( WithSampler ( tc . sampler ) )
_ , span := tp . Tracer ( name ) . Start ( context . Background ( ) , "StartSpan" )
got := span . IsRecording ( )
2022-05-19 22:15:07 +02:00
span . End ( )
2021-03-30 22:26:42 +02:00
assert . Equal ( t , got , tc . want , name )
}
} )
t . Run ( "after Span end" , func ( t * testing . T ) {
for name , tc := range map [ string ] Sampler {
"Always Sample" : AlwaysSample ( ) ,
"Never Sample" : NeverSample ( ) ,
} {
tp := NewTracerProvider ( WithSampler ( tc ) )
_ , span := tp . Tracer ( name ) . Start ( context . Background ( ) , "StartSpan" )
span . End ( )
got := span . IsRecording ( )
assert . False ( t , got , name )
}
} )
2019-08-02 22:52:55 +02:00
}
2019-10-30 01:53:50 +02:00
func TestSampling ( t * testing . T ) {
2020-12-10 03:30:32 +02:00
idg := defaultIDGenerator ( )
2019-11-08 21:11:59 +02:00
const total = 10000
2019-10-30 01:53:50 +02:00
for name , tc := range map [ string ] struct {
sampler Sampler
expect float64
parent bool
sampledParent bool
} {
// Span w/o a parent
2020-09-03 16:28:01 +02:00
"NeverSample" : { sampler : NeverSample ( ) , expect : 0 } ,
"AlwaysSample" : { sampler : AlwaysSample ( ) , expect : 1.0 } ,
"TraceIdRatioBased_-1" : { sampler : TraceIDRatioBased ( - 1.0 ) , expect : 0 } ,
"TraceIdRatioBased_.25" : { sampler : TraceIDRatioBased ( 0.25 ) , expect : .25 } ,
"TraceIdRatioBased_.50" : { sampler : TraceIDRatioBased ( 0.50 ) , expect : .5 } ,
"TraceIdRatioBased_.75" : { sampler : TraceIDRatioBased ( 0.75 ) , expect : .75 } ,
"TraceIdRatioBased_2.0" : { sampler : TraceIDRatioBased ( 2.0 ) , expect : 1 } ,
2020-09-10 21:39:04 +02:00
// Spans w/o a parent and using ParentBased(DelegateSampler()) Sampler, receive DelegateSampler's sampling decision
"ParentNeverSample" : { sampler : ParentBased ( NeverSample ( ) ) , expect : 0 } ,
"ParentAlwaysSample" : { sampler : ParentBased ( AlwaysSample ( ) ) , expect : 1 } ,
"ParentTraceIdRatioBased_.50" : { sampler : ParentBased ( TraceIDRatioBased ( 0.50 ) ) , expect : .5 } ,
2020-09-03 16:28:01 +02:00
// An unadorned TraceIDRatioBased sampler ignores parent spans
"UnsampledParentSpanWithTraceIdRatioBased_.25" : { sampler : TraceIDRatioBased ( 0.25 ) , expect : .25 , parent : true } ,
"SampledParentSpanWithTraceIdRatioBased_.25" : { sampler : TraceIDRatioBased ( 0.25 ) , expect : .25 , parent : true , sampledParent : true } ,
"UnsampledParentSpanWithTraceIdRatioBased_.50" : { sampler : TraceIDRatioBased ( 0.50 ) , expect : .5 , parent : true } ,
"SampledParentSpanWithTraceIdRatioBased_.50" : { sampler : TraceIDRatioBased ( 0.50 ) , expect : .5 , parent : true , sampledParent : true } ,
"UnsampledParentSpanWithTraceIdRatioBased_.75" : { sampler : TraceIDRatioBased ( 0.75 ) , expect : .75 , parent : true } ,
"SampledParentSpanWithTraceIdRatioBased_.75" : { sampler : TraceIDRatioBased ( 0.75 ) , expect : .75 , parent : true , sampledParent : true } ,
// Spans with a sampled parent but using NeverSample Sampler, are not sampled
2019-11-08 21:11:59 +02:00
"SampledParentSpanWithNeverSample" : { sampler : NeverSample ( ) , expect : 0 , parent : true , sampledParent : true } ,
2020-09-03 16:28:01 +02:00
2020-09-10 21:39:04 +02:00
// Spans with a sampled parent and using ParentBased(DelegateSampler()) Sampler, inherit the parent span's sampling status
"SampledParentSpanWithParentNeverSample" : { sampler : ParentBased ( NeverSample ( ) ) , expect : 1 , parent : true , sampledParent : true } ,
"UnsampledParentSpanWithParentNeverSampler" : { sampler : ParentBased ( NeverSample ( ) ) , expect : 0 , parent : true , sampledParent : false } ,
"SampledParentSpanWithParentAlwaysSampler" : { sampler : ParentBased ( AlwaysSample ( ) ) , expect : 1 , parent : true , sampledParent : true } ,
"UnsampledParentSpanWithParentAlwaysSampler" : { sampler : ParentBased ( AlwaysSample ( ) ) , expect : 0 , parent : true , sampledParent : false } ,
"SampledParentSpanWithParentTraceIdRatioBased_.50" : { sampler : ParentBased ( TraceIDRatioBased ( 0.50 ) ) , expect : 1 , parent : true , sampledParent : true } ,
"UnsampledParentSpanWithParentTraceIdRatioBased_.50" : { sampler : ParentBased ( TraceIDRatioBased ( 0.50 ) ) , expect : 0 , parent : true , sampledParent : false } ,
2019-10-30 01:53:50 +02:00
} {
tc := tc
t . Run ( name , func ( t * testing . T ) {
t . Parallel ( )
2021-03-18 18:34:47 +02:00
p := NewTracerProvider ( WithSampler ( tc . sampler ) )
2019-11-25 19:46:07 +02:00
tr := p . Tracer ( "test" )
2019-10-30 01:53:50 +02:00
var sampled int
for i := 0 ; i < total ; i ++ {
2020-02-04 18:55:03 +02:00
ctx := context . Background ( )
2019-10-30 01:53:50 +02:00
if tc . parent {
2020-12-10 03:30:32 +02:00
tid , sid := idg . NewIDs ( ctx )
2021-03-09 18:17:29 +02:00
psc := trace . NewSpanContext ( trace . SpanContextConfig {
2020-12-10 03:30:32 +02:00
TraceID : tid ,
SpanID : sid ,
2021-03-09 18:17:29 +02:00
} )
2019-10-30 01:53:50 +02:00
if tc . sampledParent {
2021-03-09 18:17:29 +02:00
psc = psc . WithTraceFlags ( trace . FlagsSampled )
2019-10-30 01:53:50 +02:00
}
2020-11-07 00:13:31 +02:00
ctx = trace . ContextWithRemoteSpanContext ( ctx , psc )
2019-10-30 01:53:50 +02:00
}
2020-02-04 18:55:03 +02:00
_ , span := tr . Start ( ctx , "test" )
2019-10-30 01:53:50 +02:00
if span . SpanContext ( ) . IsSampled ( ) {
sampled ++
}
}
2019-11-08 21:11:59 +02:00
tolerance := 0.0
2019-10-30 01:53:50 +02:00
got := float64 ( sampled ) / float64 ( total )
2019-11-08 21:11:59 +02:00
if tc . expect > 0 && tc . expect < 1 {
// See https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval
const z = 4.75342 // This should succeed 99.9999% of the time
tolerance = z * math . Sqrt ( got * ( 1 - got ) / total )
}
2019-10-30 01:53:50 +02:00
diff := math . Abs ( got - tc . expect )
2019-11-08 21:11:59 +02:00
if diff > tolerance {
t . Errorf ( "got %f (diff: %f), expected %f (w/tolerance: %f)" , got , diff , tc . expect , tolerance )
2019-10-30 01:53:50 +02:00
}
} )
}
}
2019-08-02 22:52:55 +02:00
2020-02-04 18:55:03 +02:00
func TestStartSpanWithParent ( t * testing . T ) {
2020-09-24 00:16:13 +02:00
tp := NewTracerProvider ( )
2020-02-04 18:55:03 +02:00
tr := tp . Tracer ( "SpanWithParent" )
ctx := context . Background ( )
2019-10-22 22:19:11 +02:00
2021-03-30 17:59:54 +02:00
_ , s1 := tr . Start ( trace . ContextWithRemoteSpanContext ( ctx , sc ) , "span1-unsampled-parent1" )
if err := checkChild ( t , sc , s1 ) ; err != nil {
2019-08-02 22:52:55 +02:00
t . Error ( err )
}
2021-03-30 17:59:54 +02:00
_ , s2 := tr . Start ( trace . ContextWithRemoteSpanContext ( ctx , sc ) , "span2-unsampled-parent1" )
if err := checkChild ( t , sc , s2 ) ; err != nil {
2019-08-02 22:52:55 +02:00
t . Error ( err )
}
2021-07-26 17:12:53 +02:00
ts , err := trace . ParseTraceState ( "k=v" )
2020-12-21 23:11:48 +02:00
if err != nil {
t . Error ( err )
}
2021-03-30 17:59:54 +02:00
sc2 := sc . WithTraceState ( ts )
2020-11-07 00:13:31 +02:00
_ , s3 := tr . Start ( trace . ContextWithRemoteSpanContext ( ctx , sc2 ) , "span3-sampled-parent2" )
2020-12-21 23:11:48 +02:00
if err := checkChild ( t , sc2 , s3 ) ; err != nil {
2019-08-02 22:52:55 +02:00
t . Error ( err )
}
2020-11-07 00:13:31 +02:00
ctx2 , s4 := tr . Start ( trace . ContextWithRemoteSpanContext ( ctx , sc2 ) , "span4-sampled-parent2" )
2020-12-21 23:11:48 +02:00
if err := checkChild ( t , sc2 , s4 ) ; err != nil {
2019-08-02 22:52:55 +02:00
t . Error ( err )
}
s4Sc := s4 . SpanContext ( )
2020-02-04 18:55:03 +02:00
_ , s5 := tr . Start ( ctx2 , "span5-implicit-childof-span4" )
2020-12-21 23:11:48 +02:00
if err := checkChild ( t , s4Sc , s5 ) ; err != nil {
2019-08-02 22:52:55 +02:00
t . Error ( err )
}
}
2022-08-26 15:53:33 +02:00
// Test we get a successful span as a new root if a nil context is sent in, as opposed to a panic.
// See https://github.com/open-telemetry/opentelemetry-go/issues/3109
func TestStartSpanWithNilContext ( t * testing . T ) {
tp := NewTracerProvider ( )
tr := tp . Tracer ( "NoPanic" )
// nolint:staticcheck // no nil context, but that's the point of the test.
assert . NotPanics ( t , func ( ) { tr . Start ( nil , "should-not-panic" ) } )
}
2021-06-28 18:17:12 +02:00
func TestStartSpanNewRootNotSampled ( t * testing . T ) {
alwaysSampleTp := NewTracerProvider ( )
sampledTr := alwaysSampleTp . Tracer ( "AlwaysSampled" )
neverSampleTp := NewTracerProvider ( WithSampler ( ParentBased ( NeverSample ( ) ) ) )
neverSampledTr := neverSampleTp . Tracer ( "ParentBasedNeverSample" )
ctx := context . Background ( )
ctx , s1 := sampledTr . Start ( trace . ContextWithRemoteSpanContext ( ctx , sc ) , "span1-sampled" )
if err := checkChild ( t , sc , s1 ) ; err != nil {
t . Error ( err )
}
_ , s2 := neverSampledTr . Start ( ctx , "span2-no-newroot" )
if ! s2 . SpanContext ( ) . IsSampled ( ) {
t . Error ( fmt . Errorf ( "got child span is not sampled, want child span with sampler: ParentBased(NeverSample()) to be sampled" ) )
}
// Adding WithNewRoot causes child spans to not sample based on parent context
_ , s3 := neverSampledTr . Start ( ctx , "span3-newroot" , trace . WithNewRoot ( ) )
if s3 . SpanContext ( ) . IsSampled ( ) {
t . Error ( fmt . Errorf ( "got child span is sampled, want child span WithNewRoot() and with sampler: ParentBased(NeverSample()) to not be sampled" ) )
}
}
2019-11-04 21:04:38 +02:00
func TestSetSpanAttributesOnStart ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2019-11-14 20:03:23 +02:00
span := startSpan ( tp ,
"StartSpanAttribute" ,
2021-02-18 19:59:37 +02:00
trace . WithAttributes ( attribute . String ( "key1" , "value1" ) ) ,
trace . WithAttributes ( attribute . String ( "key2" , "value2" ) ) ,
2019-11-14 20:03:23 +02:00
)
2019-11-04 21:04:38 +02:00
got , err := endSpan ( te , span )
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2019-11-04 21:04:38 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
attributes : [ ] attribute . KeyValue {
2021-02-18 19:59:37 +02:00
attribute . String ( "key1" , "value1" ) ,
attribute . String ( "key2" , "value2" ) ,
2019-11-04 21:04:38 +02:00
} ,
2022-07-06 20:55:46 +02:00
spanKind : trace . SpanKindInternal ,
instrumentationScope : instrumentation . Scope { Name : "StartSpanAttribute" } ,
2019-11-04 21:04:38 +02:00
}
if diff := cmpDiff ( got , want ) ; diff != "" {
t . Errorf ( "SetSpanAttributesOnStart: -got +want %s" , diff )
}
}
2020-10-10 00:48:34 +02:00
func TestSamplerAttributesLocalChildSpan ( t * testing . T ) {
sampler := & testSampler { prefix : "span" , t : t }
te := NewTestExporter ( )
2021-03-18 18:34:47 +02:00
tp := NewTracerProvider ( WithSampler ( sampler ) , WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2020-10-10 00:48:34 +02:00
ctx := context . Background ( )
2022-05-19 22:15:07 +02:00
ctx , span := startLocalSpan ( ctx , tp , "SpanOne" , "span0" )
_ , spanTwo := startLocalSpan ( ctx , tp , "SpanTwo" , "span1" )
2020-10-10 00:48:34 +02:00
spanTwo . End ( )
span . End ( )
got := te . Spans ( )
2021-03-30 17:59:54 +02:00
require . Len ( t , got , 2 )
// FILO order above means spanTwo <-> gotSpan0 and span <-> gotSpan1.
gotSpan0 , gotSpan1 := got [ 0 ] , got [ 1 ]
// Ensure sampler is called for local child spans by verifying the
// attributes set by the sampler are set on the child span.
2021-05-05 01:45:13 +02:00
assert . Equal ( t , [ ] attribute . KeyValue { attribute . Int ( "callCount" , 2 ) } , gotSpan0 . Attributes ( ) )
assert . Equal ( t , [ ] attribute . KeyValue { attribute . Int ( "callCount" , 1 ) } , gotSpan1 . Attributes ( ) )
2020-10-10 00:48:34 +02:00
}
2022-02-07 22:58:05 +02:00
func TestSpanSetAttributes ( t * testing . T ) {
attrs := [ ... ] attribute . KeyValue {
attribute . String ( "key1" , "value1" ) ,
2021-02-18 19:59:37 +02:00
attribute . String ( "key2" , "value2" ) ,
2022-02-07 22:58:05 +02:00
attribute . String ( "key3" , "value3" ) ,
attribute . String ( "key4" , "value4" ) ,
attribute . String ( "key1" , "value5" ) ,
attribute . String ( "key2" , "value6" ) ,
attribute . String ( "key3" , "value7" ) ,
2019-08-02 22:52:55 +02:00
}
2022-02-07 22:58:05 +02:00
invalid := attribute . KeyValue { }
2019-08-02 22:52:55 +02:00
2022-02-07 22:58:05 +02:00
tests := [ ] struct {
name string
input [ ] [ ] attribute . KeyValue
wantAttrs [ ] attribute . KeyValue
wantDropped int
} {
{
name : "array" ,
input : [ ] [ ] attribute . KeyValue { attrs [ : 3 ] } ,
wantAttrs : attrs [ : 3 ] ,
} ,
{
name : "single_value:array" ,
input : [ ] [ ] attribute . KeyValue { attrs [ : 1 ] , attrs [ 1 : 3 ] } ,
wantAttrs : attrs [ : 3 ] ,
} ,
{
name : "array:single_value" ,
input : [ ] [ ] attribute . KeyValue { attrs [ : 2 ] , attrs [ 2 : 3 ] } ,
wantAttrs : attrs [ : 3 ] ,
} ,
{
name : "single_values" ,
input : [ ] [ ] attribute . KeyValue { attrs [ : 1 ] , attrs [ 1 : 2 ] , attrs [ 2 : 3 ] } ,
wantAttrs : attrs [ : 3 ] ,
2019-09-25 22:22:33 +02:00
} ,
2019-08-02 22:52:55 +02:00
2022-02-07 22:58:05 +02:00
// The tracing specification states:
//
// For each unique attribute key, addition of which would result in
// exceeding the limit, SDK MUST discard that key/value pair
//
// Therefore, adding attributes after the capacity is reached should
// result in those attributes being dropped.
2021-03-08 19:24:29 +02:00
2022-02-07 22:58:05 +02:00
{
name : "drop_last_added" ,
input : [ ] [ ] attribute . KeyValue { attrs [ : 3 ] , attrs [ 3 : 4 ] , attrs [ 3 : 4 ] } ,
wantAttrs : attrs [ : 3 ] ,
wantDropped : 2 ,
} ,
2021-03-08 19:24:29 +02:00
2022-02-07 22:58:05 +02:00
// The tracing specification states:
//
// Setting an attribute with the same key as an existing attribute
// SHOULD overwrite the existing attribute's value.
//
// Therefore, attributes are updated regardless of capacity state.
{
name : "single_value_update" ,
input : [ ] [ ] attribute . KeyValue { attrs [ : 1 ] , attrs [ : 3 ] } ,
wantAttrs : attrs [ : 3 ] ,
} ,
{
name : "all_update" ,
input : [ ] [ ] attribute . KeyValue { attrs [ : 3 ] , attrs [ 4 : 7 ] } ,
wantAttrs : attrs [ 4 : 7 ] ,
} ,
{
name : "all_update/multi" ,
input : [ ] [ ] attribute . KeyValue { attrs [ : 3 ] , attrs [ 4 : 7 ] , attrs [ : 3 ] } ,
wantAttrs : attrs [ : 3 ] ,
} ,
{
name : "deduplicate/under_capacity" ,
input : [ ] [ ] attribute . KeyValue { attrs [ : 1 ] , attrs [ : 1 ] , attrs [ : 1 ] } ,
wantAttrs : attrs [ : 1 ] ,
} ,
{
name : "deduplicate/over_capacity" ,
input : [ ] [ ] attribute . KeyValue { attrs [ : 1 ] , attrs [ : 1 ] , attrs [ : 1 ] , attrs [ : 3 ] } ,
wantAttrs : attrs [ : 3 ] ,
} ,
{
name : "deduplicate/added" ,
input : [ ] [ ] attribute . KeyValue {
attrs [ : 2 ] ,
{ attrs [ 2 ] , attrs [ 2 ] , attrs [ 2 ] } ,
} ,
wantAttrs : attrs [ : 3 ] ,
} ,
{
name : "deduplicate/added_at_cappacity" ,
input : [ ] [ ] attribute . KeyValue {
attrs [ : 3 ] ,
{ attrs [ 2 ] , attrs [ 2 ] , attrs [ 2 ] } ,
} ,
wantAttrs : attrs [ : 3 ] ,
} ,
{
name : "invalid" ,
input : [ ] [ ] attribute . KeyValue {
{ invalid } ,
} ,
wantDropped : 1 ,
} ,
{
name : "invalid_with_valid" ,
input : [ ] [ ] attribute . KeyValue {
{ invalid , attrs [ 0 ] } ,
} ,
wantAttrs : attrs [ : 1 ] ,
wantDropped : 1 ,
} ,
{
name : "invalid_over_capacity" ,
input : [ ] [ ] attribute . KeyValue {
{ invalid , invalid , invalid , invalid , attrs [ 0 ] } ,
} ,
wantAttrs : attrs [ : 1 ] ,
wantDropped : 4 ,
} ,
{
name : "valid:invalid/under_capacity" ,
input : [ ] [ ] attribute . KeyValue {
attrs [ : 1 ] ,
{ invalid } ,
} ,
wantAttrs : attrs [ : 1 ] ,
wantDropped : 1 ,
} ,
{
name : "valid:invalid/over_capacity" ,
input : [ ] [ ] attribute . KeyValue {
attrs [ : 1 ] ,
{ invalid , invalid , invalid , invalid } ,
} ,
wantAttrs : attrs [ : 1 ] ,
wantDropped : 4 ,
} ,
{
name : "valid_at_capacity:invalid" ,
input : [ ] [ ] attribute . KeyValue {
attrs [ : 3 ] ,
{ invalid , invalid , invalid , invalid } ,
} ,
wantAttrs : attrs [ : 3 ] ,
wantDropped : 4 ,
2021-03-08 19:24:29 +02:00
} ,
}
2022-02-07 22:58:05 +02:00
const (
capacity = 3
instName = "TestSpanAttributeCapacity"
spanName = "test span"
)
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
te := NewTestExporter ( )
2022-03-03 17:56:07 +02:00
sl := NewSpanLimits ( )
sl . AttributeCountLimit = capacity
tp := NewTracerProvider ( WithSyncer ( te ) , WithSpanLimits ( sl ) )
2022-02-07 22:58:05 +02:00
_ , span := tp . Tracer ( instName ) . Start ( context . Background ( ) , spanName )
for _ , a := range test . input {
span . SetAttributes ( a ... )
}
span . End ( )
require . Implements ( t , ( * ReadOnlySpan ) ( nil ) , span )
roSpan := span . ( ReadOnlySpan )
// Ensure the span itself is valid.
assert . ElementsMatch ( t , test . wantAttrs , roSpan . Attributes ( ) , "exected attributes" )
assert . Equal ( t , test . wantDropped , roSpan . DroppedAttributes ( ) , "dropped attributes" )
snap , ok := te . GetSpan ( spanName )
require . Truef ( t , ok , "span %s not exported" , spanName )
// Ensure the exported span snapshot is valid.
assert . ElementsMatch ( t , test . wantAttrs , snap . Attributes ( ) , "exected attributes" )
assert . Equal ( t , test . wantDropped , snap . DroppedAttributes ( ) , "dropped attributes" )
} )
2021-03-08 19:24:29 +02:00
}
}
2019-08-02 22:52:55 +02:00
func TestEvents ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2019-10-22 22:19:11 +02:00
span := startSpan ( tp , "Events" )
2021-02-18 19:59:37 +02:00
k1v1 := attribute . String ( "key1" , "value1" )
k2v2 := attribute . Bool ( "key2" , true )
k3v3 := attribute . Int64 ( "key3" , 3 )
2019-08-02 22:52:55 +02:00
2021-02-18 19:59:37 +02:00
span . AddEvent ( "foo" , trace . WithAttributes ( attribute . String ( "key1" , "value1" ) ) )
2020-11-07 00:13:31 +02:00
span . AddEvent ( "bar" , trace . WithAttributes (
2021-02-18 19:59:37 +02:00
attribute . Bool ( "key2" , true ) ,
attribute . Int64 ( "key3" , 3 ) ,
2020-10-16 17:09:27 +02:00
) )
2019-10-22 22:19:11 +02:00
got , err := endSpan ( te , span )
2019-08-02 22:52:55 +02:00
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
for i := range got . Events ( ) {
if ! checkTime ( & got . Events ( ) [ i ] . Time ) {
2019-09-09 23:59:39 +02:00
t . Error ( "exporting span: expected nonzero Event Time" )
2019-08-02 22:52:55 +02:00
}
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2019-09-25 23:37:36 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
events : [ ] Event {
2021-02-18 19:59:37 +02:00
{ Name : "foo" , Attributes : [ ] attribute . KeyValue { k1v1 } } ,
{ Name : "bar" , Attributes : [ ] attribute . KeyValue { k2v2 , k3v3 } } ,
2019-08-02 22:52:55 +02:00
} ,
2022-07-06 20:55:46 +02:00
spanKind : trace . SpanKindInternal ,
instrumentationScope : instrumentation . Scope { Name : "Events" } ,
2019-08-02 22:52:55 +02:00
}
2019-10-31 00:01:19 +02:00
if diff := cmpDiff ( got , want ) ; diff != "" {
2019-08-02 22:52:55 +02:00
t . Errorf ( "Message Events: -got +want %s" , diff )
}
}
func TestEventsOverLimit ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2022-03-03 17:56:07 +02:00
sl := NewSpanLimits ( )
sl . EventCountLimit = 2
tp := NewTracerProvider ( WithSpanLimits ( sl ) , WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2019-10-22 22:19:11 +02:00
span := startSpan ( tp , "EventsOverLimit" )
2021-02-18 19:59:37 +02:00
k1v1 := attribute . String ( "key1" , "value1" )
k2v2 := attribute . Bool ( "key2" , false )
k3v3 := attribute . String ( "key3" , "value3" )
2019-08-02 22:52:55 +02:00
2021-02-18 19:59:37 +02:00
span . AddEvent ( "fooDrop" , trace . WithAttributes ( attribute . String ( "key1" , "value1" ) ) )
2020-11-07 00:13:31 +02:00
span . AddEvent ( "barDrop" , trace . WithAttributes (
2021-02-18 19:59:37 +02:00
attribute . Bool ( "key2" , true ) ,
attribute . String ( "key3" , "value3" ) ,
2020-10-16 17:09:27 +02:00
) )
2021-02-18 19:59:37 +02:00
span . AddEvent ( "foo" , trace . WithAttributes ( attribute . String ( "key1" , "value1" ) ) )
2020-11-07 00:13:31 +02:00
span . AddEvent ( "bar" , trace . WithAttributes (
2021-02-18 19:59:37 +02:00
attribute . Bool ( "key2" , false ) ,
attribute . String ( "key3" , "value3" ) ,
2020-10-16 17:09:27 +02:00
) )
2019-10-22 22:19:11 +02:00
got , err := endSpan ( te , span )
2019-08-02 22:52:55 +02:00
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
for i := range got . Events ( ) {
if ! checkTime ( & got . Events ( ) [ i ] . Time ) {
2019-09-09 23:59:39 +02:00
t . Error ( "exporting span: expected nonzero Event Time" )
2019-08-02 22:52:55 +02:00
}
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2019-09-25 23:37:36 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
events : [ ] Event {
2021-02-18 19:59:37 +02:00
{ Name : "foo" , Attributes : [ ] attribute . KeyValue { k1v1 } } ,
{ Name : "bar" , Attributes : [ ] attribute . KeyValue { k2v2 , k3v3 } } ,
2019-08-02 22:52:55 +02:00
} ,
2022-07-06 20:55:46 +02:00
droppedEventCount : 2 ,
spanKind : trace . SpanKindInternal ,
instrumentationScope : instrumentation . Scope { Name : "EventsOverLimit" } ,
2019-08-02 22:52:55 +02:00
}
2019-10-31 00:01:19 +02:00
if diff := cmpDiff ( got , want ) ; diff != "" {
2019-08-02 22:52:55 +02:00
t . Errorf ( "Message Event over limit: -got +want %s" , diff )
}
}
2019-09-21 09:26:20 +02:00
func TestLinks ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2019-10-22 22:19:11 +02:00
2021-02-18 19:59:37 +02:00
k1v1 := attribute . String ( "key1" , "value1" )
k2v2 := attribute . String ( "key2" , "value2" )
k3v3 := attribute . String ( "key3" , "value3" )
2019-09-21 09:26:20 +02:00
2021-03-09 18:17:29 +02:00
sc1 := trace . NewSpanContext ( trace . SpanContextConfig { TraceID : trace . TraceID ( [ 16 ] byte { 1 , 1 } ) , SpanID : trace . SpanID { 3 } } )
sc2 := trace . NewSpanContext ( trace . SpanContextConfig { TraceID : trace . TraceID ( [ 16 ] byte { 1 , 1 } ) , SpanID : trace . SpanID { 3 } } )
2019-09-21 09:26:20 +02:00
2021-07-26 17:06:41 +02:00
l1 := trace . Link { SpanContext : sc1 , Attributes : [ ] attribute . KeyValue { k1v1 } }
l2 := trace . Link { SpanContext : sc2 , Attributes : [ ] attribute . KeyValue { k2v2 , k3v3 } }
links := [ ] trace . Link { l1 , l2 }
2020-11-07 00:13:31 +02:00
span := startSpan ( tp , "Links" , trace . WithLinks ( links ... ) )
2019-11-26 23:03:07 +02:00
2019-10-22 22:19:11 +02:00
got , err := endSpan ( te , span )
2019-09-21 09:26:20 +02:00
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2019-09-25 23:37:36 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2022-07-06 20:55:46 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
links : [ ] Link { { l1 . SpanContext , l1 . Attributes , 0 } , { l2 . SpanContext , l2 . Attributes , 0 } } ,
spanKind : trace . SpanKindInternal ,
instrumentationScope : instrumentation . Scope { Name : "Links" } ,
2019-09-21 09:26:20 +02:00
}
2019-10-31 00:01:19 +02:00
if diff := cmpDiff ( got , want ) ; diff != "" {
2019-09-21 09:26:20 +02:00
t . Errorf ( "Link: -got +want %s" , diff )
}
2021-10-12 16:45:10 +02:00
sc1 = trace . NewSpanContext ( trace . SpanContextConfig { TraceID : trace . TraceID ( [ 16 ] byte { 1 , 1 } ) , SpanID : trace . SpanID { 3 } } )
span1 := startSpan ( tp , "name" , trace . WithLinks ( [ ] trace . Link {
{ SpanContext : trace . SpanContext { } } ,
{ SpanContext : sc1 } ,
} ... ) )
sdkspan , _ := span1 . ( * recordingSpan )
require . Len ( t , sdkspan . Links ( ) , 1 )
2019-09-21 09:26:20 +02:00
}
func TestLinksOverLimit ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2019-10-22 22:19:11 +02:00
2021-03-09 18:17:29 +02:00
sc1 := trace . NewSpanContext ( trace . SpanContextConfig { TraceID : trace . TraceID ( [ 16 ] byte { 1 , 1 } ) , SpanID : trace . SpanID { 3 } } )
sc2 := trace . NewSpanContext ( trace . SpanContextConfig { TraceID : trace . TraceID ( [ 16 ] byte { 1 , 1 } ) , SpanID : trace . SpanID { 3 } } )
sc3 := trace . NewSpanContext ( trace . SpanContextConfig { TraceID : trace . TraceID ( [ 16 ] byte { 1 , 1 } ) , SpanID : trace . SpanID { 3 } } )
2019-10-23 08:01:33 +02:00
2022-03-03 17:56:07 +02:00
sl := NewSpanLimits ( )
sl . LinkCountLimit = 2
tp := NewTracerProvider ( WithSpanLimits ( sl ) , WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2019-11-26 23:03:07 +02:00
span := startSpan ( tp , "LinksOverLimit" ,
2020-11-07 00:13:31 +02:00
trace . WithLinks (
2021-02-18 19:59:37 +02:00
trace . Link { SpanContext : sc1 , Attributes : [ ] attribute . KeyValue { attribute . String ( "key1" , "value1" ) } } ,
trace . Link { SpanContext : sc2 , Attributes : [ ] attribute . KeyValue { attribute . String ( "key2" , "value2" ) } } ,
trace . Link { SpanContext : sc3 , Attributes : [ ] attribute . KeyValue { attribute . String ( "key3" , "value3" ) } } ,
2020-09-03 16:34:36 +02:00
) ,
2019-11-26 23:03:07 +02:00
)
2019-09-21 09:26:20 +02:00
2021-02-18 19:59:37 +02:00
k2v2 := attribute . String ( "key2" , "value2" )
k3v3 := attribute . String ( "key3" , "value3" )
2019-09-21 09:26:20 +02:00
2019-10-22 22:19:11 +02:00
got , err := endSpan ( te , span )
2019-09-21 09:26:20 +02:00
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2019-09-25 23:37:36 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
2021-07-26 17:06:41 +02:00
links : [ ] Link {
{ SpanContext : sc2 , Attributes : [ ] attribute . KeyValue { k2v2 } , DroppedAttributeCount : 0 } ,
{ SpanContext : sc3 , Attributes : [ ] attribute . KeyValue { k3v3 } , DroppedAttributeCount : 0 } ,
2019-09-21 09:26:20 +02:00
} ,
2022-07-06 20:55:46 +02:00
droppedLinkCount : 1 ,
spanKind : trace . SpanKindInternal ,
instrumentationScope : instrumentation . Scope { Name : "LinksOverLimit" } ,
2019-09-21 09:26:20 +02:00
}
2019-10-31 00:01:19 +02:00
if diff := cmpDiff ( got , want ) ; diff != "" {
2019-09-21 09:26:20 +02:00
t . Errorf ( "Link over limit: -got +want %s" , diff )
}
}
2019-08-02 22:52:55 +02:00
func TestSetSpanName ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2020-02-04 18:55:03 +02:00
ctx := context . Background ( )
2019-10-22 22:19:11 +02:00
2020-01-14 14:54:48 +02:00
want := "SpanName-1"
2021-03-30 17:59:54 +02:00
ctx = trace . ContextWithRemoteSpanContext ( ctx , sc )
2020-02-04 18:55:03 +02:00
_ , span := tp . Tracer ( "SetSpanName" ) . Start ( ctx , "SpanName-1" )
2019-10-22 22:19:11 +02:00
got , err := endSpan ( te , span )
2019-08-02 22:52:55 +02:00
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
if got . Name ( ) != want {
t . Errorf ( "span.Name: got %q; want %q" , got . Name ( ) , want )
2019-08-02 22:52:55 +02:00
}
}
func TestSetSpanStatus ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2019-10-22 22:19:11 +02:00
span := startSpan ( tp , "SpanStatus" )
2020-10-05 20:36:03 +02:00
span . SetStatus ( codes . Error , "Error" )
2019-10-22 22:19:11 +02:00
got , err := endSpan ( te , span )
2019-08-02 22:52:55 +02:00
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2019-09-25 23:37:36 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
spanKind : trace . SpanKindInternal ,
status : Status {
2021-05-03 21:00:54 +02:00
Code : codes . Error ,
Description : "Error" ,
} ,
2022-07-06 20:55:46 +02:00
instrumentationScope : instrumentation . Scope { Name : "SpanStatus" } ,
2019-08-02 22:52:55 +02:00
}
2019-10-31 00:01:19 +02:00
if diff := cmpDiff ( got , want ) ; diff != "" {
2019-08-02 22:52:55 +02:00
t . Errorf ( "SetSpanStatus: -got +want %s" , diff )
}
}
2021-03-08 19:40:38 +02:00
func TestSetSpanStatusWithoutMessageWhenStatusIsNotError ( t * testing . T ) {
te := NewTestExporter ( )
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
span := startSpan ( tp , "SpanStatus" )
span . SetStatus ( codes . Ok , "This message will be ignored" )
got , err := endSpan ( te , span )
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2021-03-08 19:40:38 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
spanKind : trace . SpanKindInternal ,
status : Status {
2021-05-03 21:00:54 +02:00
Code : codes . Ok ,
Description : "" ,
} ,
2022-07-06 20:55:46 +02:00
instrumentationScope : instrumentation . Scope { Name : "SpanStatus" } ,
2021-03-08 19:40:38 +02:00
}
if diff := cmpDiff ( got , want ) ; diff != "" {
t . Errorf ( "SetSpanStatus: -got +want %s" , diff )
}
}
2019-10-31 00:01:19 +02:00
func cmpDiff ( x , y interface { } ) string {
2020-03-13 22:07:36 +02:00
return cmp . Diff ( x , y ,
2021-05-05 01:45:13 +02:00
cmp . AllowUnexported ( snapshot { } ) ,
2021-02-18 19:59:37 +02:00
cmp . AllowUnexported ( attribute . Value { } ) ,
2021-04-29 19:29:48 +02:00
cmp . AllowUnexported ( Event { } ) ,
2020-12-21 23:11:48 +02:00
cmp . AllowUnexported ( trace . TraceState { } ) )
2019-10-31 00:01:19 +02:00
}
2019-08-02 22:52:55 +02:00
// checkChild is test utility function that tests that c has fields set appropriately,
// given that it is a child span of p.
2020-12-21 23:11:48 +02:00
func checkChild ( t * testing . T , p trace . SpanContext , apiSpan trace . Span ) error {
2021-09-02 23:30:12 +02:00
s := apiSpan . ( * recordingSpan )
2019-08-02 22:52:55 +02:00
if s == nil {
return fmt . Errorf ( "got nil child span, want non-nil" )
}
2021-03-09 18:17:29 +02:00
if got , want := s . spanContext . TraceID ( ) . String ( ) , p . TraceID ( ) . String ( ) ; got != want {
2019-08-02 22:52:55 +02:00
return fmt . Errorf ( "got child trace ID %s, want %s" , got , want )
}
2021-03-09 18:17:29 +02:00
if childID , parentID := s . spanContext . SpanID ( ) . String ( ) , p . SpanID ( ) . String ( ) ; childID == parentID {
2019-08-02 22:52:55 +02:00
return fmt . Errorf ( "got child span ID %s, parent span ID %s; want unequal IDs" , childID , parentID )
}
2021-03-09 18:17:29 +02:00
if got , want := s . spanContext . TraceFlags ( ) , p . TraceFlags ( ) ; got != want {
2019-08-02 22:52:55 +02:00
return fmt . Errorf ( "got child trace options %d, want %d" , got , want )
}
2021-03-09 18:17:29 +02:00
got , want := s . spanContext . TraceState ( ) , p . TraceState ( )
2020-12-21 23:11:48 +02:00
assert . Equal ( t , want , got )
2019-08-02 22:52:55 +02:00
return nil
}
2019-08-26 20:53:12 +02:00
// startSpan starts a span with a name "span0". See startNamedSpan for
// details.
2021-05-27 16:53:56 +02:00
func startSpan ( tp * TracerProvider , trName string , args ... trace . SpanStartOption ) trace . Span {
2019-11-04 21:04:38 +02:00
return startNamedSpan ( tp , trName , "span0" , args ... )
2019-08-26 20:53:12 +02:00
}
// startNamed Span is a test utility func that starts a span with a
2020-02-04 18:55:03 +02:00
// passed name and with remote span context as parent. The remote span
// context contains TraceFlags with sampled bit set. This allows the
// span to be automatically sampled.
2021-05-27 16:53:56 +02:00
func startNamedSpan ( tp * TracerProvider , trName , name string , args ... trace . SpanStartOption ) trace . Span {
2019-11-25 19:46:07 +02:00
_ , span := tp . Tracer ( trName ) . Start (
2021-03-30 17:59:54 +02:00
trace . ContextWithRemoteSpanContext ( context . Background ( ) , sc ) ,
2019-08-26 20:53:12 +02:00
name ,
2019-11-04 21:04:38 +02:00
args ... ,
2019-08-02 22:52:55 +02:00
)
return span
}
2020-10-10 00:48:34 +02:00
// startLocalSpan is a test utility func that starts a span with a
// passed name and with the passed context. The context is returned
// along with the span so this parent can be used to create child
// spans.
2022-05-19 22:15:07 +02:00
func startLocalSpan ( ctx context . Context , tp * TracerProvider , trName , name string , args ... trace . SpanStartOption ) ( context . Context , trace . Span ) {
2020-10-10 00:48:34 +02:00
ctx , span := tp . Tracer ( trName ) . Start (
ctx ,
name ,
args ... ,
)
return ctx , span
}
2019-08-02 22:52:55 +02:00
// endSpan is a test utility function that ends the span in the context and
2021-05-05 01:45:13 +02:00
// returns the exported span.
2019-08-02 22:52:55 +02:00
// It requires that span be sampled using one of these methods
2020-02-04 18:55:03 +02:00
// 1. Passing parent span context in context
2019-08-02 22:52:55 +02:00
// 2. Use WithSampler(AlwaysSample())
// 3. Configuring AlwaysSample() as default sampler
//
// It also does some basic tests on the span.
2021-05-05 01:45:13 +02:00
// It also clears spanID in the to make the comparison easier.
func endSpan ( te * testExporter , span trace . Span ) ( * snapshot , error ) {
2019-10-11 03:07:35 +02:00
if ! span . IsRecording ( ) {
2022-05-19 22:15:07 +02:00
return nil , fmt . Errorf ( "method IsRecording: got false, want true" )
2019-08-02 22:52:55 +02:00
}
if ! span . SpanContext ( ) . IsSampled ( ) {
2022-05-19 22:15:07 +02:00
return nil , fmt . Errorf ( "method IsSampled: got false, want true" )
2019-08-02 22:52:55 +02:00
}
2019-09-27 19:48:10 +02:00
span . End ( )
2020-09-09 19:19:03 +02:00
if te . Len ( ) != 1 {
return nil , fmt . Errorf ( "got %d exported spans, want one span" , te . Len ( ) )
2019-08-02 22:52:55 +02:00
}
2020-09-09 19:19:03 +02:00
got := te . Spans ( ) [ 0 ]
2021-05-05 01:45:13 +02:00
if ! got . SpanContext ( ) . SpanID ( ) . IsValid ( ) {
2019-08-02 22:52:55 +02:00
return nil , fmt . Errorf ( "exporting span: expected nonzero SpanID" )
}
2021-05-05 01:45:13 +02:00
got . spanContext = got . SpanContext ( ) . WithSpanID ( trace . SpanID { } )
if ! checkTime ( & got . startTime ) {
2019-08-02 22:52:55 +02:00
return nil , fmt . Errorf ( "exporting span: expected nonzero StartTime" )
}
2021-05-05 01:45:13 +02:00
if ! checkTime ( & got . endTime ) {
2019-08-02 22:52:55 +02:00
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 TestEndSpanTwice ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2020-09-24 00:16:13 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) )
2019-10-08 20:56:58 +02:00
2020-12-11 07:15:44 +02:00
st := time . Now ( )
et1 := st . Add ( 100 * time . Millisecond )
et2 := st . Add ( 200 * time . Millisecond )
span := startSpan ( tp , "EndSpanTwice" , trace . WithTimestamp ( st ) )
span . End ( trace . WithTimestamp ( et1 ) )
span . End ( trace . WithTimestamp ( et2 ) )
2020-09-09 19:19:03 +02:00
if te . Len ( ) != 1 {
t . Fatalf ( "expected only a single span, got %#v" , te . Spans ( ) )
2019-08-02 22:52:55 +02:00
}
2020-12-11 07:15:44 +02:00
ro := span . ( ReadOnlySpan )
if ro . EndTime ( ) != et1 {
t . Fatalf ( "2nd call to End() should not modify end time" )
}
2019-08-02 22:52:55 +02:00
}
func TestStartSpanAfterEnd ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-03-18 18:34:47 +02:00
tp := NewTracerProvider ( WithSampler ( AlwaysSample ( ) ) , WithSyncer ( te ) )
2020-02-04 18:55:03 +02:00
ctx := context . Background ( )
2019-10-08 20:56:58 +02:00
2019-11-25 19:46:07 +02:00
tr := tp . Tracer ( "SpanAfterEnd" )
2021-03-30 17:59:54 +02:00
ctx , span0 := tr . Start ( trace . ContextWithRemoteSpanContext ( ctx , sc ) , "parent" )
2019-10-22 22:19:11 +02:00
ctx1 , span1 := tr . Start ( ctx , "span-1" )
2019-09-27 19:48:10 +02:00
span1 . End ( )
2019-08-02 22:52:55 +02:00
// 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
2019-10-22 22:19:11 +02:00
_ , span2 := tr . Start ( ctx1 , "span-2" )
2019-09-27 19:48:10 +02:00
span2 . End ( )
span0 . End ( )
2020-09-09 19:19:03 +02:00
if got , want := te . Len ( ) , 3 ; got != want {
t . Fatalf ( "len(%#v) = %d; want %d" , te . Spans ( ) , got , want )
2019-08-02 22:52:55 +02:00
}
2020-01-14 14:54:48 +02:00
2020-09-09 19:19:03 +02:00
gotParent , ok := te . GetSpan ( "parent" )
2020-01-14 14:54:48 +02:00
if ! ok {
t . Fatal ( "parent not recorded" )
}
2020-09-09 19:19:03 +02:00
gotSpan1 , ok := te . GetSpan ( "span-1" )
2020-01-14 14:54:48 +02:00
if ! ok {
t . Fatal ( "span-1 not recorded" )
}
2020-09-09 19:19:03 +02:00
gotSpan2 , ok := te . GetSpan ( "span-2" )
2020-01-14 14:54:48 +02:00
if ! ok {
t . Fatal ( "span-2 not recorded" )
}
2021-05-05 01:45:13 +02:00
if got , want := gotSpan1 . SpanContext ( ) . TraceID ( ) , gotParent . SpanContext ( ) . TraceID ( ) ; got != want {
2019-08-02 22:52:55 +02:00
t . Errorf ( "span-1.TraceID=%q; want %q" , got , want )
}
2021-05-05 01:45:13 +02:00
if got , want := gotSpan2 . SpanContext ( ) . TraceID ( ) , gotParent . SpanContext ( ) . TraceID ( ) ; got != want {
2019-08-02 22:52:55 +02:00
t . Errorf ( "span-2.TraceID=%q; want %q" , got , want )
}
2021-05-05 01:45:13 +02:00
if got , want := gotSpan1 . Parent ( ) . SpanID ( ) , gotParent . SpanContext ( ) . SpanID ( ) ; got != want {
2019-08-02 22:52:55 +02:00
t . Errorf ( "span-1.ParentSpanID=%q; want %q (parent.SpanID)" , got , want )
}
2021-05-05 01:45:13 +02:00
if got , want := gotSpan2 . Parent ( ) . SpanID ( ) , gotSpan1 . SpanContext ( ) . SpanID ( ) ; got != want {
2019-08-02 22:52:55 +02:00
t . Errorf ( "span-2.ParentSpanID=%q; want %q (span1.SpanID)" , got , want )
}
}
func TestChildSpanCount ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-03-18 18:34:47 +02:00
tp := NewTracerProvider ( WithSampler ( AlwaysSample ( ) ) , WithSyncer ( te ) )
2019-10-08 20:56:58 +02:00
2019-11-25 19:46:07 +02:00
tr := tp . Tracer ( "ChidSpanCount" )
2019-10-22 22:19:11 +02:00
ctx , span0 := tr . Start ( context . Background ( ) , "parent" )
ctx1 , span1 := tr . Start ( ctx , "span-1" )
_ , span2 := tr . Start ( ctx1 , "span-2" )
2019-09-27 19:48:10 +02:00
span2 . End ( )
span1 . End ( )
2019-08-02 22:52:55 +02:00
2019-10-22 22:19:11 +02:00
_ , span3 := tr . Start ( ctx , "span-3" )
2019-09-27 19:48:10 +02:00
span3 . End ( )
span0 . End ( )
2020-09-09 19:19:03 +02:00
if got , want := te . Len ( ) , 4 ; got != want {
t . Fatalf ( "len(%#v) = %d; want %d" , te . Spans ( ) , got , want )
2019-08-02 22:52:55 +02:00
}
2020-01-14 14:54:48 +02:00
2020-09-09 19:19:03 +02:00
gotParent , ok := te . GetSpan ( "parent" )
2020-01-14 14:54:48 +02:00
if ! ok {
t . Fatal ( "parent not recorded" )
}
2020-09-09 19:19:03 +02:00
gotSpan1 , ok := te . GetSpan ( "span-1" )
2020-01-14 14:54:48 +02:00
if ! ok {
t . Fatal ( "span-1 not recorded" )
}
2020-09-09 19:19:03 +02:00
gotSpan2 , ok := te . GetSpan ( "span-2" )
2020-01-14 14:54:48 +02:00
if ! ok {
t . Fatal ( "span-2 not recorded" )
}
2020-09-09 19:19:03 +02:00
gotSpan3 , ok := te . GetSpan ( "span-3" )
2020-01-14 14:54:48 +02:00
if ! ok {
t . Fatal ( "span-3 not recorded" )
}
2021-05-05 01:45:13 +02:00
if got , want := gotSpan3 . ChildSpanCount ( ) , 0 ; got != want {
2020-09-09 19:19:03 +02:00
t . Errorf ( "span-3.ChildSpanCount=%d; want %d" , got , want )
2019-08-02 22:52:55 +02:00
}
2021-05-05 01:45:13 +02:00
if got , want := gotSpan2 . ChildSpanCount ( ) , 0 ; got != want {
2020-09-09 19:19:03 +02:00
t . Errorf ( "span-2.ChildSpanCount=%d; want %d" , got , want )
2019-08-02 22:52:55 +02:00
}
2021-05-05 01:45:13 +02:00
if got , want := gotSpan1 . ChildSpanCount ( ) , 1 ; got != want {
2020-09-09 19:19:03 +02:00
t . Errorf ( "span-1.ChildSpanCount=%d; want %d" , got , want )
2019-08-02 22:52:55 +02:00
}
2021-05-05 01:45:13 +02:00
if got , want := gotParent . ChildSpanCount ( ) , 2 ; got != want {
2020-09-09 19:19:03 +02:00
t . Errorf ( "parent.ChildSpanCount=%d; want %d" , got , want )
2019-08-02 22:52:55 +02:00
}
}
2019-09-27 19:48:10 +02:00
func TestNilSpanEnd ( t * testing . T ) {
2021-09-02 23:30:12 +02:00
var span * recordingSpan
2019-09-27 19:48:10 +02:00
span . End ( )
2019-08-02 22:52:55 +02:00
}
2021-09-02 23:30:12 +02:00
func TestNonRecordingSpanDoesNotTrackRuntimeTracerTask ( t * testing . T ) {
2021-03-18 18:34:47 +02:00
tp := NewTracerProvider ( WithSampler ( NeverSample ( ) ) )
2021-09-02 23:30:12 +02:00
tr := tp . Tracer ( "TestNonRecordingSpanDoesNotTrackRuntimeTracerTask" )
_ , apiSpan := tr . Start ( context . Background ( ) , "foo" )
if _ , ok := apiSpan . ( runtimeTracer ) ; ok {
t . Fatalf ( "non recording span implements runtime trace task tracking" )
}
}
func TestRecordingSpanRuntimeTracerTaskEnd ( t * testing . T ) {
tp := NewTracerProvider ( WithSampler ( AlwaysSample ( ) ) )
tr := tp . Tracer ( "TestRecordingSpanRuntimeTracerTaskEnd" )
2019-10-22 22:19:11 +02:00
2021-09-02 23:30:12 +02:00
var n uint64
2019-08-02 22:52:55 +02:00
executionTracerTaskEnd := func ( ) {
atomic . AddUint64 ( & n , 1 )
}
2019-10-22 22:19:11 +02:00
_ , apiSpan := tr . Start ( context . Background ( ) , "foo" )
2021-09-02 23:30:12 +02:00
s , ok := apiSpan . ( * recordingSpan )
if ! ok {
t . Fatal ( "recording span not returned from always sampled Tracer" )
}
2019-08-02 22:52:55 +02:00
s . executionTracerTaskEnd = executionTracerTaskEnd
2021-09-02 23:30:12 +02:00
s . End ( )
2019-08-02 22:52:55 +02:00
2021-09-02 23:30:12 +02:00
if n != 1 {
t . Error ( "recording span did not end runtime trace task" )
2019-08-02 22:52:55 +02:00
}
}
2019-09-03 20:03:51 +02:00
func TestCustomStartEndTime ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-03-18 18:34:47 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithSampler ( AlwaysSample ( ) ) )
2019-10-22 22:19:11 +02:00
2019-09-03 20:03:51 +02:00
startTime := time . Date ( 2019 , time . August , 27 , 14 , 42 , 0 , 0 , time . UTC )
endTime := startTime . Add ( time . Second * 20 )
2019-11-25 19:46:07 +02:00
_ , span := tp . Tracer ( "Custom Start and End time" ) . Start (
2019-09-03 20:03:51 +02:00
context . Background ( ) ,
"testspan" ,
2020-11-07 00:13:31 +02:00
trace . WithTimestamp ( startTime ) ,
2019-09-03 20:03:51 +02:00
)
2020-11-07 00:13:31 +02:00
span . End ( trace . WithTimestamp ( endTime ) )
2019-10-08 20:56:58 +02:00
2020-09-09 19:19:03 +02:00
if te . Len ( ) != 1 {
t . Fatalf ( "got %d exported spans, want one span" , te . Len ( ) )
2019-09-03 20:03:51 +02:00
}
2020-09-09 19:19:03 +02:00
got := te . Spans ( ) [ 0 ]
2022-05-19 22:15:07 +02:00
if ! got . StartTime ( ) . Equal ( startTime ) {
2021-05-05 01:45:13 +02:00
t . Errorf ( "expected start time to be %s, got %s" , startTime , got . StartTime ( ) )
2019-09-03 20:03:51 +02:00
}
2022-05-19 22:15:07 +02:00
if ! got . EndTime ( ) . Equal ( endTime ) {
2021-05-05 01:45:13 +02:00
t . Errorf ( "expected end time to be %s, got %s" , endTime , got . EndTime ( ) )
2019-09-03 20:03:51 +02:00
}
}
2019-10-24 01:25:14 +02:00
2020-02-28 23:44:53 +02:00
func TestRecordError ( t * testing . T ) {
scenarios := [ ] struct {
err error
typ string
msg string
} {
{
err : ottest . NewTestError ( "test error" ) ,
2021-01-12 18:56:16 +02:00
typ : "go.opentelemetry.io/otel/internal/internaltest.TestError" ,
2020-02-28 23:44:53 +02:00
msg : "test error" ,
} ,
{
err : errors . New ( "test error 2" ) ,
typ : "*errors.errorString" ,
msg : "test error 2" ,
} ,
}
for _ , s := range scenarios {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2020-02-28 23:44:53 +02:00
span := startSpan ( tp , "RecordError" )
errTime := time . Now ( )
2020-11-07 00:13:31 +02:00
span . RecordError ( s . err , trace . WithTimestamp ( errTime ) )
2020-02-28 23:44:53 +02:00
got , err := endSpan ( te , span )
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2020-02-28 23:44:53 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
status : Status { Code : codes . Unset } ,
spanKind : trace . SpanKindInternal ,
events : [ ] Event {
2020-02-28 23:44:53 +02:00
{
2021-04-01 22:07:46 +02:00
Name : semconv . ExceptionEventName ,
2020-02-28 23:44:53 +02:00
Time : errTime ,
2021-02-18 19:59:37 +02:00
Attributes : [ ] attribute . KeyValue {
2021-04-01 22:07:46 +02:00
semconv . ExceptionTypeKey . String ( s . typ ) ,
semconv . ExceptionMessageKey . String ( s . msg ) ,
2020-02-28 23:44:53 +02:00
} ,
} ,
} ,
2022-07-06 20:55:46 +02:00
instrumentationScope : instrumentation . Scope { Name : "RecordError" } ,
2020-02-28 23:44:53 +02:00
}
if diff := cmpDiff ( got , want ) ; diff != "" {
t . Errorf ( "SpanErrorOptions: -got +want %s" , diff )
}
}
}
2021-08-13 23:44:18 +02:00
func TestRecordErrorWithStackTrace ( t * testing . T ) {
err := ottest . NewTestError ( "test error" )
typ := "go.opentelemetry.io/otel/internal/internaltest.TestError"
msg := "test error"
te := NewTestExporter ( )
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
span := startSpan ( tp , "RecordError" )
errTime := time . Now ( )
span . RecordError ( err , trace . WithTimestamp ( errTime ) , trace . WithStackTrace ( true ) )
got , err := endSpan ( te , span )
if err != nil {
t . Fatal ( err )
}
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
TraceID : tid ,
TraceFlags : 0x1 ,
} ) ,
parent : sc . WithRemote ( true ) ,
name : "span0" ,
status : Status { Code : codes . Unset } ,
spanKind : trace . SpanKindInternal ,
events : [ ] Event {
{
Name : semconv . ExceptionEventName ,
Time : errTime ,
Attributes : [ ] attribute . KeyValue {
semconv . ExceptionTypeKey . String ( typ ) ,
semconv . ExceptionMessageKey . String ( msg ) ,
} ,
} ,
} ,
2022-07-06 20:55:46 +02:00
instrumentationScope : instrumentation . Scope { Name : "RecordError" } ,
2021-08-13 23:44:18 +02:00
}
assert . Equal ( t , got . spanContext , want . spanContext )
assert . Equal ( t , got . parent , want . parent )
assert . Equal ( t , got . name , want . name )
assert . Equal ( t , got . status , want . status )
assert . Equal ( t , got . spanKind , want . spanKind )
assert . Equal ( t , got . events [ 0 ] . Attributes [ 0 ] . Value . AsString ( ) , want . events [ 0 ] . Attributes [ 0 ] . Value . AsString ( ) )
assert . Equal ( t , got . events [ 0 ] . Attributes [ 1 ] . Value . AsString ( ) , want . events [ 0 ] . Attributes [ 1 ] . Value . AsString ( ) )
gotStackTraceFunctionName := strings . Split ( got . events [ 0 ] . Attributes [ 2 ] . Value . AsString ( ) , "\n" )
2021-09-02 23:30:12 +02:00
assert . Truef ( t , strings . HasPrefix ( gotStackTraceFunctionName [ 1 ] , "go.opentelemetry.io/otel/sdk/trace.recordStackTrace" ) , "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.recordStackTrace" , gotStackTraceFunctionName [ 1 ] )
assert . Truef ( t , strings . HasPrefix ( gotStackTraceFunctionName [ 3 ] , "go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).RecordError" ) , "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).RecordError" , gotStackTraceFunctionName [ 3 ] )
2021-08-13 23:44:18 +02:00
}
2020-02-28 23:44:53 +02:00
func TestRecordErrorNil ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2020-02-28 23:44:53 +02:00
span := startSpan ( tp , "RecordErrorNil" )
2020-10-16 17:09:27 +02:00
span . RecordError ( nil )
2020-02-28 23:44:53 +02:00
got , err := endSpan ( te , span )
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2020-02-28 23:44:53 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
spanKind : trace . SpanKindInternal ,
status : Status {
2021-05-03 21:00:54 +02:00
Code : codes . Unset ,
Description : "" ,
} ,
2022-07-06 20:55:46 +02:00
instrumentationScope : instrumentation . Scope { Name : "RecordErrorNil" } ,
2020-02-28 23:44:53 +02:00
}
if diff := cmpDiff ( got , want ) ; diff != "" {
t . Errorf ( "SpanErrorOptions: -got +want %s" , diff )
}
}
2019-10-24 01:25:14 +02:00
func TestWithSpanKind ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-03-18 18:34:47 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithSampler ( AlwaysSample ( ) ) , WithResource ( resource . Empty ( ) ) )
2019-11-25 19:46:07 +02:00
tr := tp . Tracer ( "withSpanKind" )
2019-10-24 01:25:14 +02:00
_ , span := tr . Start ( context . Background ( ) , "WithoutSpanKind" )
2020-09-09 19:19:03 +02:00
spanData , err := endSpan ( te , span )
2019-10-24 01:25:14 +02:00
if err != nil {
t . Error ( err . Error ( ) )
}
2021-05-05 01:45:13 +02:00
if spanData . SpanKind ( ) != trace . SpanKindInternal {
t . Errorf ( "Default value of Spankind should be Internal: got %+v, want %+v\n" , spanData . SpanKind ( ) , trace . SpanKindInternal )
2019-10-24 01:25:14 +02:00
}
2020-11-07 00:13:31 +02:00
sks := [ ] trace . SpanKind {
trace . SpanKindInternal ,
trace . SpanKindServer ,
trace . SpanKindClient ,
trace . SpanKindProducer ,
trace . SpanKindConsumer ,
2019-10-24 01:25:14 +02:00
}
for _ , sk := range sks {
2020-09-09 19:19:03 +02:00
te . Reset ( )
2019-10-24 01:25:14 +02:00
2020-11-07 00:13:31 +02:00
_ , span := tr . Start ( context . Background ( ) , fmt . Sprintf ( "SpanKind-%v" , sk ) , trace . WithSpanKind ( sk ) )
2020-09-09 19:19:03 +02:00
spanData , err := endSpan ( te , span )
2019-10-24 01:25:14 +02:00
if err != nil {
t . Error ( err . Error ( ) )
}
2021-05-05 01:45:13 +02:00
if spanData . SpanKind ( ) != sk {
t . Errorf ( "WithSpanKind check: got %+v, want %+v\n" , spanData . SpanKind ( ) , sks )
2019-10-24 01:25:14 +02:00
}
}
}
2020-03-13 22:07:36 +02:00
2021-06-08 18:46:42 +02:00
func mergeResource ( t * testing . T , r1 , r2 * resource . Resource ) * resource . Resource {
r , err := resource . Merge ( r1 , r2 )
assert . NoError ( t , err )
return r
}
2020-03-13 22:07:36 +02:00
func TestWithResource ( t * testing . T ) {
2021-04-21 20:53:12 +02:00
store , err := ottest . SetEnvVariables ( map [ string ] string {
envVar : "key=value,rk5=7" ,
} )
require . NoError ( t , err )
defer func ( ) { require . NoError ( t , store . Restore ( ) ) } ( )
2021-02-15 22:28:37 +02:00
cases := [ ] struct {
name string
options [ ] TracerProviderOption
want * resource . Resource
msg string
} {
{
name : "explicitly empty resource" ,
options : [ ] TracerProviderOption { WithResource ( resource . Empty ( ) ) } ,
2021-04-21 20:53:12 +02:00
want : resource . Environment ( ) ,
2020-03-13 22:07:36 +02:00
} ,
2021-02-15 22:28:37 +02:00
{
name : "uses default if no resource option" ,
options : [ ] TracerProviderOption { } ,
want : resource . Default ( ) ,
} ,
{
name : "explicit resource" ,
2021-06-08 18:46:42 +02:00
options : [ ] TracerProviderOption { WithResource ( resource . NewSchemaless ( attribute . String ( "rk1" , "rv1" ) , attribute . Int64 ( "rk2" , 5 ) ) ) } ,
want : mergeResource ( t , resource . Environment ( ) , resource . NewSchemaless ( attribute . String ( "rk1" , "rv1" ) , attribute . Int64 ( "rk2" , 5 ) ) ) ,
2021-02-15 22:28:37 +02:00
} ,
{
name : "last resource wins" ,
options : [ ] TracerProviderOption {
2021-06-08 18:46:42 +02:00
WithResource ( resource . NewSchemaless ( attribute . String ( "rk1" , "vk1" ) , attribute . Int64 ( "rk2" , 5 ) ) ) ,
WithResource ( resource . NewSchemaless ( attribute . String ( "rk3" , "rv3" ) , attribute . Int64 ( "rk4" , 10 ) ) ) } ,
want : mergeResource ( t , resource . Environment ( ) , resource . NewSchemaless ( attribute . String ( "rk3" , "rv3" ) , attribute . Int64 ( "rk4" , 10 ) ) ) ,
2021-04-21 20:53:12 +02:00
} ,
{
name : "overlapping attributes with environment resource" ,
2021-06-08 18:46:42 +02:00
options : [ ] TracerProviderOption { WithResource ( resource . NewSchemaless ( attribute . String ( "rk1" , "rv1" ) , attribute . Int64 ( "rk5" , 10 ) ) ) } ,
want : mergeResource ( t , resource . Environment ( ) , resource . NewSchemaless ( attribute . String ( "rk1" , "rv1" ) , attribute . Int64 ( "rk5" , 10 ) ) ) ,
2020-03-13 22:07:36 +02:00
} ,
2020-06-10 07:15:53 +02:00
}
2021-02-15 22:28:37 +02:00
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
te := NewTestExporter ( )
2021-03-18 18:34:47 +02:00
defaultOptions := [ ] TracerProviderOption { WithSyncer ( te ) , WithSampler ( AlwaysSample ( ) ) }
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( append ( defaultOptions , tc . options ... ) ... )
span := startSpan ( tp , "WithResource" )
2021-02-18 19:59:37 +02:00
span . SetAttributes ( attribute . String ( "key1" , "value1" ) )
2021-02-15 22:28:37 +02:00
got , err := endSpan ( te , span )
if err != nil {
t . Error ( err . Error ( ) )
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2021-02-15 22:28:37 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
attributes : [ ] attribute . KeyValue {
2021-02-18 19:59:37 +02:00
attribute . String ( "key1" , "value1" ) ,
2021-02-15 22:28:37 +02:00
} ,
2022-07-06 20:55:46 +02:00
spanKind : trace . SpanKindInternal ,
resource : tc . want ,
instrumentationScope : instrumentation . Scope { Name : "WithResource" } ,
2021-02-15 22:28:37 +02:00
}
if diff := cmpDiff ( got , want ) ; diff != "" {
t . Errorf ( "WithResource:\n -got +want %s" , diff )
}
} )
2020-06-10 07:15:53 +02:00
}
}
2021-05-27 21:22:38 +02:00
func TestWithInstrumentationVersionAndSchema ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2020-06-10 07:15:53 +02:00
ctx := context . Background ( )
2021-03-30 17:59:54 +02:00
ctx = trace . ContextWithRemoteSpanContext ( ctx , sc )
2020-06-10 07:15:53 +02:00
_ , span := tp . Tracer (
"WithInstrumentationVersion" ,
2020-11-07 00:13:31 +02:00
trace . WithInstrumentationVersion ( "v0.1.0" ) ,
2021-05-27 21:22:38 +02:00
trace . WithSchemaURL ( "https://opentelemetry.io/schemas/1.2.0" ) ,
2021-03-09 19:45:09 +02:00
) . Start ( ctx , "span0" )
2020-09-09 19:19:03 +02:00
got , err := endSpan ( te , span )
2020-06-10 07:15:53 +02:00
if err != nil {
t . Error ( err . Error ( ) )
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2020-06-10 07:15:53 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
spanKind : trace . SpanKindInternal ,
2022-07-06 20:55:46 +02:00
instrumentationScope : instrumentation . Scope {
2021-05-27 21:22:38 +02:00
Name : "WithInstrumentationVersion" ,
Version : "v0.1.0" ,
SchemaURL : "https://opentelemetry.io/schemas/1.2.0" ,
2020-06-10 07:15:53 +02:00
} ,
2020-03-13 22:07:36 +02:00
}
if diff := cmpDiff ( got , want ) ; diff != "" {
t . Errorf ( "WithResource:\n -got +want %s" , diff )
}
}
2020-08-08 21:10:36 +02:00
func TestSpanCapturesPanic ( t * testing . T ) {
2020-09-09 19:19:03 +02:00
te := NewTestExporter ( )
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2020-08-08 21:10:36 +02:00
_ , span := tp . Tracer ( "CatchPanic" ) . Start (
context . Background ( ) ,
"span" ,
)
f := func ( ) {
defer span . End ( )
panic ( errors . New ( "error message" ) )
}
require . PanicsWithError ( t , "error message" , f )
2020-09-09 19:19:03 +02:00
spans := te . Spans ( )
require . Len ( t , spans , 1 )
2021-05-05 01:45:13 +02:00
require . Len ( t , spans [ 0 ] . Events ( ) , 1 )
assert . Equal ( t , spans [ 0 ] . Events ( ) [ 0 ] . Name , semconv . ExceptionEventName )
assert . Equal ( t , spans [ 0 ] . Events ( ) [ 0 ] . Attributes , [ ] attribute . KeyValue {
2021-04-01 22:07:46 +02:00
semconv . ExceptionTypeKey . String ( "*errors.errorString" ) ,
semconv . ExceptionMessageKey . String ( "error message" ) ,
2020-08-08 21:10:36 +02:00
} )
}
2020-12-11 07:15:44 +02:00
2021-08-13 23:44:18 +02:00
func TestSpanCapturesPanicWithStackTrace ( t * testing . T ) {
te := NewTestExporter ( )
tp := NewTracerProvider ( WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
_ , span := tp . Tracer ( "CatchPanic" ) . Start (
context . Background ( ) ,
"span" ,
)
f := func ( ) {
defer span . End ( trace . WithStackTrace ( true ) )
panic ( errors . New ( "error message" ) )
}
require . PanicsWithError ( t , "error message" , f )
spans := te . Spans ( )
require . Len ( t , spans , 1 )
require . Len ( t , spans [ 0 ] . Events ( ) , 1 )
assert . Equal ( t , spans [ 0 ] . Events ( ) [ 0 ] . Name , semconv . ExceptionEventName )
assert . Equal ( t , spans [ 0 ] . Events ( ) [ 0 ] . Attributes [ 0 ] . Value . AsString ( ) , "*errors.errorString" )
assert . Equal ( t , spans [ 0 ] . Events ( ) [ 0 ] . Attributes [ 1 ] . Value . AsString ( ) , "error message" )
gotStackTraceFunctionName := strings . Split ( spans [ 0 ] . Events ( ) [ 0 ] . Attributes [ 2 ] . Value . AsString ( ) , "\n" )
2021-09-02 23:30:12 +02:00
assert . Truef ( t , strings . HasPrefix ( gotStackTraceFunctionName [ 1 ] , "go.opentelemetry.io/otel/sdk/trace.recordStackTrace" ) , "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.recordStackTrace" , gotStackTraceFunctionName [ 1 ] )
assert . Truef ( t , strings . HasPrefix ( gotStackTraceFunctionName [ 3 ] , "go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).End" ) , "%q not prefixed with go.opentelemetry.io/otel/sdk/trace.(*recordingSpan).End" , gotStackTraceFunctionName [ 3 ] )
2021-08-13 23:44:18 +02:00
}
2020-12-11 07:15:44 +02:00
func TestReadOnlySpan ( t * testing . T ) {
2021-02-18 19:59:37 +02:00
kv := attribute . String ( "foo" , "bar" )
2020-12-11 07:15:44 +02:00
2021-06-08 18:46:42 +02:00
tp := NewTracerProvider ( WithResource ( resource . NewSchemaless ( kv ) ) )
2020-12-11 07:15:44 +02:00
tr := tp . Tracer ( "ReadOnlySpan" , trace . WithInstrumentationVersion ( "3" ) )
// Initialize parent context.
2021-03-18 19:48:13 +02:00
tID , sID := tp . idGenerator . NewIDs ( context . Background ( ) )
2021-03-09 18:17:29 +02:00
parent := trace . NewSpanContext ( trace . SpanContextConfig {
2020-12-11 07:15:44 +02:00
TraceID : tID ,
SpanID : sID ,
TraceFlags : 0x1 ,
2021-03-18 18:05:37 +02:00
Remote : true ,
2021-03-09 18:17:29 +02:00
} )
2020-12-11 07:15:44 +02:00
ctx := trace . ContextWithRemoteSpanContext ( context . Background ( ) , parent )
// Initialize linked context.
2021-03-18 19:48:13 +02:00
tID , sID = tp . idGenerator . NewIDs ( context . Background ( ) )
2021-03-09 18:17:29 +02:00
linked := trace . NewSpanContext ( trace . SpanContextConfig {
2020-12-11 07:15:44 +02:00
TraceID : tID ,
SpanID : sID ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} )
2020-12-11 07:15:44 +02:00
st := time . Now ( )
2021-05-05 01:45:13 +02:00
ctx , s := tr . Start ( ctx , "foo" , trace . WithTimestamp ( st ) ,
2020-12-11 07:15:44 +02:00
trace . WithLinks ( trace . Link { SpanContext : linked } ) )
2021-05-05 01:45:13 +02:00
s . SetAttributes ( kv )
s . AddEvent ( "foo" , trace . WithAttributes ( kv ) )
s . SetStatus ( codes . Ok , "foo" )
2020-12-11 07:15:44 +02:00
// Verify span implements ReadOnlySpan.
2021-05-05 01:45:13 +02:00
ro , ok := s . ( ReadOnlySpan )
2020-12-11 07:15:44 +02:00
require . True ( t , ok )
assert . Equal ( t , "foo" , ro . Name ( ) )
assert . Equal ( t , trace . SpanContextFromContext ( ctx ) , ro . SpanContext ( ) )
assert . Equal ( t , parent , ro . Parent ( ) )
assert . Equal ( t , trace . SpanKindInternal , ro . SpanKind ( ) )
assert . Equal ( t , st , ro . StartTime ( ) )
assert . True ( t , ro . EndTime ( ) . IsZero ( ) )
assert . Equal ( t , kv . Key , ro . Attributes ( ) [ 0 ] . Key )
assert . Equal ( t , kv . Value , ro . Attributes ( ) [ 0 ] . Value )
assert . Equal ( t , linked , ro . Links ( ) [ 0 ] . SpanContext )
assert . Equal ( t , kv . Key , ro . Events ( ) [ 0 ] . Attributes [ 0 ] . Key )
assert . Equal ( t , kv . Value , ro . Events ( ) [ 0 ] . Attributes [ 0 ] . Value )
2021-05-03 21:00:54 +02:00
assert . Equal ( t , codes . Ok , ro . Status ( ) . Code )
assert . Equal ( t , "" , ro . Status ( ) . Description )
2020-12-11 07:15:44 +02:00
assert . Equal ( t , "ReadOnlySpan" , ro . InstrumentationLibrary ( ) . Name )
assert . Equal ( t , "3" , ro . InstrumentationLibrary ( ) . Version )
2022-07-06 20:55:46 +02:00
assert . Equal ( t , "ReadOnlySpan" , ro . InstrumentationScope ( ) . Name )
assert . Equal ( t , "3" , ro . InstrumentationScope ( ) . Version )
2020-12-11 07:15:44 +02:00
assert . Equal ( t , kv . Key , ro . Resource ( ) . Attributes ( ) [ 0 ] . Key )
assert . Equal ( t , kv . Value , ro . Resource ( ) . Attributes ( ) [ 0 ] . Value )
// Verify changes to the original span are reflected in the ReadOnlySpan.
2021-05-05 01:45:13 +02:00
s . SetName ( "bar" )
2020-12-11 07:15:44 +02:00
assert . Equal ( t , "bar" , ro . Name ( ) )
2021-05-05 01:45:13 +02:00
// Verify snapshot() returns snapshots that are independent from the
2020-12-11 07:15:44 +02:00
// original span and from one another.
2021-09-02 23:30:12 +02:00
d1 := s . ( * recordingSpan ) . snapshot ( )
2021-05-05 01:45:13 +02:00
s . AddEvent ( "baz" )
2021-09-02 23:30:12 +02:00
d2 := s . ( * recordingSpan ) . snapshot ( )
2021-05-05 01:45:13 +02:00
for _ , e := range d1 . Events ( ) {
2020-12-11 07:15:44 +02:00
if e . Name == "baz" {
t . Errorf ( "Didn't expect to find 'baz' event" )
}
}
var exists bool
2021-05-05 01:45:13 +02:00
for _ , e := range d2 . Events ( ) {
2020-12-11 07:15:44 +02:00
if e . Name == "baz" {
exists = true
}
}
if ! exists {
t . Errorf ( "Expected to find 'baz' event" )
}
et := st . Add ( time . Millisecond )
2021-05-05 01:45:13 +02:00
s . End ( trace . WithTimestamp ( et ) )
2020-12-11 07:15:44 +02:00
assert . Equal ( t , et , ro . EndTime ( ) )
}
func TestReadWriteSpan ( t * testing . T ) {
2021-02-15 22:28:37 +02:00
tp := NewTracerProvider ( WithResource ( resource . Empty ( ) ) )
2020-12-11 07:15:44 +02:00
tr := tp . Tracer ( "ReadWriteSpan" )
// Initialize parent context.
2021-03-18 19:48:13 +02:00
tID , sID := tp . idGenerator . NewIDs ( context . Background ( ) )
2021-03-09 18:17:29 +02:00
parent := trace . NewSpanContext ( trace . SpanContextConfig {
2020-12-11 07:15:44 +02:00
TraceID : tID ,
SpanID : sID ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} )
2020-12-11 07:15:44 +02:00
ctx := trace . ContextWithRemoteSpanContext ( context . Background ( ) , parent )
_ , span := tr . Start ( ctx , "foo" )
defer span . End ( )
// Verify span implements ReadOnlySpan.
rw , ok := span . ( ReadWriteSpan )
require . True ( t , ok )
// Verify the span can be read from.
assert . False ( t , rw . StartTime ( ) . IsZero ( ) )
// Verify the span can be written to.
rw . SetName ( "bar" )
assert . Equal ( t , "bar" , rw . Name ( ) )
// NOTE: This function tests ReadWriteSpan which is an interface which
// embeds trace.Span and ReadOnlySpan. Since both of these interfaces have
// their own tests, there is no point in testing all the possible methods
// available via ReadWriteSpan as doing so would mean creating a lot of
// duplication.
}
2021-02-18 21:31:35 +02:00
func TestAddEventsWithMoreAttributesThanLimit ( t * testing . T ) {
te := NewTestExporter ( )
2022-03-03 17:56:07 +02:00
sl := NewSpanLimits ( )
sl . AttributePerEventCountLimit = 2
2021-03-08 23:43:11 +02:00
tp := NewTracerProvider (
2022-03-03 17:56:07 +02:00
WithSpanLimits ( sl ) ,
2021-03-08 23:43:11 +02:00
WithSyncer ( te ) ,
WithResource ( resource . Empty ( ) ) ,
)
2021-02-18 21:31:35 +02:00
span := startSpan ( tp , "AddSpanEventWithOverLimitedAttributes" )
span . AddEvent ( "test1" , trace . WithAttributes (
attribute . Bool ( "key1" , true ) ,
attribute . String ( "key2" , "value2" ) ,
) )
// Parts of the attribute should be discard
span . AddEvent ( "test2" , trace . WithAttributes (
attribute . Bool ( "key1" , true ) ,
attribute . String ( "key2" , "value2" ) ,
attribute . String ( "key3" , "value3" ) ,
attribute . String ( "key4" , "value4" ) ,
) )
got , err := endSpan ( te , span )
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
for i := range got . Events ( ) {
if ! checkTime ( & got . Events ( ) [ i ] . Time ) {
2021-02-18 21:31:35 +02:00
t . Error ( "exporting span: expected nonzero Event Time" )
}
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2021-02-18 21:31:35 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
attributes : nil ,
events : [ ] Event {
2021-02-18 21:31:35 +02:00
{
Name : "test1" ,
Attributes : [ ] attribute . KeyValue {
attribute . Bool ( "key1" , true ) ,
attribute . String ( "key2" , "value2" ) ,
} ,
} ,
{
Name : "test2" ,
Attributes : [ ] attribute . KeyValue {
attribute . Bool ( "key1" , true ) ,
attribute . String ( "key2" , "value2" ) ,
} ,
2021-04-06 16:51:15 +02:00
DroppedAttributeCount : 2 ,
2021-02-18 21:31:35 +02:00
} ,
} ,
2022-07-06 20:55:46 +02:00
spanKind : trace . SpanKindInternal ,
instrumentationScope : instrumentation . Scope { Name : "AddSpanEventWithOverLimitedAttributes" } ,
2021-02-18 21:31:35 +02:00
}
if diff := cmpDiff ( got , want ) ; diff != "" {
t . Errorf ( "SetSpanAttributesOverLimit: -got +want %s" , diff )
}
}
func TestAddLinksWithMoreAttributesThanLimit ( t * testing . T ) {
te := NewTestExporter ( )
2022-03-03 17:56:07 +02:00
sl := NewSpanLimits ( )
sl . AttributePerLinkCountLimit = 1
2021-03-08 23:43:11 +02:00
tp := NewTracerProvider (
2022-03-03 17:56:07 +02:00
WithSpanLimits ( sl ) ,
2021-03-08 23:43:11 +02:00
WithSyncer ( te ) ,
WithResource ( resource . Empty ( ) ) ,
)
2021-02-18 21:31:35 +02:00
k1v1 := attribute . String ( "key1" , "value1" )
k2v2 := attribute . String ( "key2" , "value2" )
k3v3 := attribute . String ( "key3" , "value3" )
k4v4 := attribute . String ( "key4" , "value4" )
2021-03-09 18:17:29 +02:00
sc1 := trace . NewSpanContext ( trace . SpanContextConfig { TraceID : trace . TraceID ( [ 16 ] byte { 1 , 1 } ) , SpanID : trace . SpanID { 3 } } )
sc2 := trace . NewSpanContext ( trace . SpanContextConfig { TraceID : trace . TraceID ( [ 16 ] byte { 1 , 1 } ) , SpanID : trace . SpanID { 3 } } )
2021-02-18 21:31:35 +02:00
span := startSpan ( tp , "Links" , trace . WithLinks ( [ ] trace . Link {
{ SpanContext : sc1 , Attributes : [ ] attribute . KeyValue { k1v1 , k2v2 } } ,
{ SpanContext : sc2 , Attributes : [ ] attribute . KeyValue { k2v2 , k3v3 , k4v4 } } ,
} ... ) )
got , err := endSpan ( te , span )
if err != nil {
t . Fatal ( err )
}
2021-05-05 01:45:13 +02:00
want := & snapshot {
spanContext : trace . NewSpanContext ( trace . SpanContextConfig {
2021-02-18 21:31:35 +02:00
TraceID : tid ,
TraceFlags : 0x1 ,
2021-03-09 18:17:29 +02:00
} ) ,
2021-05-05 01:45:13 +02:00
parent : sc . WithRemote ( true ) ,
name : "span0" ,
2021-07-26 17:06:41 +02:00
links : [ ] Link {
2021-04-06 16:51:15 +02:00
{
SpanContext : sc1 ,
Attributes : [ ] attribute . KeyValue { k1v1 } ,
DroppedAttributeCount : 1 ,
} ,
{
SpanContext : sc2 ,
Attributes : [ ] attribute . KeyValue { k2v2 } ,
DroppedAttributeCount : 2 ,
} ,
2021-02-18 21:31:35 +02:00
} ,
2022-07-06 20:55:46 +02:00
spanKind : trace . SpanKindInternal ,
instrumentationScope : instrumentation . Scope { Name : "Links" } ,
2021-02-18 21:31:35 +02:00
}
if diff := cmpDiff ( got , want ) ; diff != "" {
t . Errorf ( "Link: -got +want %s" , diff )
}
}
2021-03-08 18:05:25 +02:00
type stateSampler struct {
prefix string
f func ( trace . TraceState ) trace . TraceState
}
func ( s * stateSampler ) ShouldSample ( p SamplingParameters ) SamplingResult {
decision := Drop
if strings . HasPrefix ( p . Name , s . prefix ) {
decision = RecordAndSample
}
2021-03-30 19:34:40 +02:00
ts := s . f ( trace . SpanContextFromContext ( p . ParentContext ) . TraceState ( ) )
return SamplingResult { Decision : decision , Tracestate : ts }
2021-03-08 18:05:25 +02:00
}
func ( s stateSampler ) Description ( ) string {
return "stateSampler"
}
2022-04-25 22:22:49 +02:00
// Check that a new span propagates the SamplerResult.TraceState.
2021-03-08 18:05:25 +02:00
func TestSamplerTraceState ( t * testing . T ) {
2021-07-26 17:12:53 +02:00
mustTS := func ( ts trace . TraceState , err error ) trace . TraceState {
require . NoError ( t , err )
return ts
}
makeInserter := func ( k , v , prefix string ) Sampler {
2021-03-08 18:05:25 +02:00
return & stateSampler {
prefix : prefix ,
2021-07-26 17:12:53 +02:00
f : func ( t trace . TraceState ) trace . TraceState { return mustTS ( t . Insert ( k , v ) ) } ,
2021-03-08 18:05:25 +02:00
}
}
2021-07-26 17:12:53 +02:00
makeDeleter := func ( k , prefix string ) Sampler {
2021-03-08 18:05:25 +02:00
return & stateSampler {
prefix : prefix ,
2021-07-26 17:12:53 +02:00
f : func ( t trace . TraceState ) trace . TraceState { return t . Delete ( k ) } ,
2021-03-08 18:05:25 +02:00
}
}
clearer := func ( prefix string ) Sampler {
return & stateSampler {
prefix : prefix ,
f : func ( t trace . TraceState ) trace . TraceState { return trace . TraceState { } } ,
}
}
tests := [ ] struct {
name string
sampler Sampler
spanName string
input trace . TraceState
want trace . TraceState
exportSpan bool
} {
{
name : "alwaysOn" ,
sampler : AlwaysSample ( ) ,
2021-07-26 17:12:53 +02:00
input : mustTS ( trace . ParseTraceState ( "k1=v1" ) ) ,
want : mustTS ( trace . ParseTraceState ( "k1=v1" ) ) ,
2021-03-08 18:05:25 +02:00
exportSpan : true ,
} ,
{
name : "alwaysOff" ,
sampler : NeverSample ( ) ,
2021-07-26 17:12:53 +02:00
input : mustTS ( trace . ParseTraceState ( "k1=v1" ) ) ,
want : mustTS ( trace . ParseTraceState ( "k1=v1" ) ) ,
2021-03-08 18:05:25 +02:00
exportSpan : false ,
} ,
{
name : "insertKeySampled" ,
2021-07-26 17:12:53 +02:00
sampler : makeInserter ( "k2" , "v2" , "span" ) ,
2021-03-08 18:05:25 +02:00
spanName : "span0" ,
2021-07-26 17:12:53 +02:00
input : mustTS ( trace . ParseTraceState ( "k1=v1" ) ) ,
want : mustTS ( trace . ParseTraceState ( "k2=v2,k1=v1" ) ) ,
2021-03-08 18:05:25 +02:00
exportSpan : true ,
} ,
{
name : "insertKeyDropped" ,
2021-07-26 17:12:53 +02:00
sampler : makeInserter ( "k2" , "v2" , "span" ) ,
2021-03-08 18:05:25 +02:00
spanName : "nospan0" ,
2021-07-26 17:12:53 +02:00
input : mustTS ( trace . ParseTraceState ( "k1=v1" ) ) ,
want : mustTS ( trace . ParseTraceState ( "k2=v2,k1=v1" ) ) ,
2021-03-08 18:05:25 +02:00
exportSpan : false ,
} ,
{
name : "deleteKeySampled" ,
2021-07-26 17:12:53 +02:00
sampler : makeDeleter ( "k1" , "span" ) ,
2021-03-08 18:05:25 +02:00
spanName : "span0" ,
2021-07-26 17:12:53 +02:00
input : mustTS ( trace . ParseTraceState ( "k1=v1,k2=v2" ) ) ,
want : mustTS ( trace . ParseTraceState ( "k2=v2" ) ) ,
2021-03-08 18:05:25 +02:00
exportSpan : true ,
} ,
{
name : "deleteKeyDropped" ,
2021-07-26 17:12:53 +02:00
sampler : makeDeleter ( "k1" , "span" ) ,
2021-03-08 18:05:25 +02:00
spanName : "nospan0" ,
2021-07-26 17:12:53 +02:00
input : mustTS ( trace . ParseTraceState ( "k1=v1,k2=v2,k3=v3" ) ) ,
want : mustTS ( trace . ParseTraceState ( "k2=v2,k3=v3" ) ) ,
2021-03-08 18:05:25 +02:00
exportSpan : false ,
} ,
{
name : "clearer" ,
sampler : clearer ( "span" ) ,
spanName : "span0" ,
2021-07-26 17:12:53 +02:00
input : mustTS ( trace . ParseTraceState ( "k1=v1,k3=v3" ) ) ,
want : mustTS ( trace . ParseTraceState ( "" ) ) ,
2021-03-08 18:05:25 +02:00
exportSpan : true ,
} ,
}
for _ , ts := range tests {
ts := ts
t . Run ( ts . name , func ( t * testing . T ) {
te := NewTestExporter ( )
2021-03-18 18:34:47 +02:00
tp := NewTracerProvider ( WithSampler ( ts . sampler ) , WithSyncer ( te ) , WithResource ( resource . Empty ( ) ) )
2021-03-08 18:05:25 +02:00
tr := tp . Tracer ( "TraceState" )
2021-03-09 18:17:29 +02:00
sc1 := trace . NewSpanContext ( trace . SpanContextConfig {
2021-03-08 18:05:25 +02:00
TraceID : tid ,
SpanID : sid ,
TraceFlags : trace . FlagsSampled ,
TraceState : ts . input ,
2021-03-09 18:17:29 +02:00
} )
2021-03-08 18:05:25 +02:00
ctx := trace . ContextWithRemoteSpanContext ( context . Background ( ) , sc1 )
_ , span := tr . Start ( ctx , ts . spanName )
// span's TraceState should be set regardless of Sampled/NonSampled state.
2021-03-09 18:17:29 +02:00
require . Equal ( t , ts . want , span . SpanContext ( ) . TraceState ( ) )
2021-03-08 18:05:25 +02:00
span . End ( )
got := te . Spans ( )
if len ( got ) > 0 != ts . exportSpan {
t . Errorf ( "unexpected number of exported spans %d" , len ( got ) )
}
if len ( got ) == 0 {
return
}
2021-05-05 01:45:13 +02:00
receivedState := got [ 0 ] . SpanContext ( ) . TraceState ( )
2021-03-08 18:05:25 +02:00
if diff := cmpDiff ( receivedState , ts . want ) ; diff != "" {
t . Errorf ( "TraceState not propagated: -got +want %s" , diff )
}
} )
}
}
2021-03-18 19:48:13 +02:00
type testIDGenerator struct {
traceID int
spanID int
}
func ( gen * testIDGenerator ) NewIDs ( ctx context . Context ) ( trace . TraceID , trace . SpanID ) {
traceIDHex := fmt . Sprintf ( "%032x" , gen . traceID )
traceID , _ := trace . TraceIDFromHex ( traceIDHex )
gen . traceID ++
spanID := gen . NewSpanID ( ctx , traceID )
return traceID , spanID
}
func ( gen * testIDGenerator ) NewSpanID ( ctx context . Context , traceID trace . TraceID ) trace . SpanID {
spanIDHex := fmt . Sprintf ( "%016x" , gen . spanID )
spanID , _ := trace . SpanIDFromHex ( spanIDHex )
gen . spanID ++
return spanID
}
var _ IDGenerator = ( * testIDGenerator ) ( nil )
func TestWithIDGenerator ( t * testing . T ) {
const (
startTraceID = 1
startSpanID = 1
numSpan = 10
)
gen := & testIDGenerator { traceID : startSpanID , spanID : startSpanID }
for i := 0 ; i < numSpan ; i ++ {
te := NewTestExporter ( )
tp := NewTracerProvider (
WithSyncer ( te ) ,
WithIDGenerator ( gen ) ,
)
span := startSpan ( tp , "TestWithIDGenerator" )
got , err := strconv . ParseUint ( span . SpanContext ( ) . SpanID ( ) . String ( ) , 16 , 64 )
require . NoError ( t , err )
want := uint64 ( startSpanID + i )
assert . Equal ( t , got , want )
_ , err = endSpan ( te , span )
require . NoError ( t , err )
}
}
2022-02-07 22:58:05 +02:00
func TestEmptyRecordingSpanAttributes ( t * testing . T ) {
assert . Nil ( t , ( & recordingSpan { } ) . Attributes ( ) )
}
func TestEmptyRecordingSpanDroppedAttributes ( t * testing . T ) {
assert . Equal ( t , 0 , ( & recordingSpan { } ) . DroppedAttributes ( ) )
}