...

Source file src/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span_test.go

Documentation: go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform

     1  // Copyright The OpenTelemetry Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tracetransform
    16  
    17  import (
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/google/go-cmp/cmp"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  	"google.golang.org/protobuf/proto"
    25  
    26  	"go.opentelemetry.io/otel/attribute"
    27  	"go.opentelemetry.io/otel/codes"
    28  	"go.opentelemetry.io/otel/sdk/instrumentation"
    29  	"go.opentelemetry.io/otel/sdk/resource"
    30  	tracesdk "go.opentelemetry.io/otel/sdk/trace"
    31  	"go.opentelemetry.io/otel/sdk/trace/tracetest"
    32  	semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
    33  	"go.opentelemetry.io/otel/trace"
    34  	tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
    35  )
    36  
    37  func TestSpanKind(t *testing.T) {
    38  	for _, test := range []struct {
    39  		kind     trace.SpanKind
    40  		expected tracepb.Span_SpanKind
    41  	}{
    42  		{
    43  			trace.SpanKindInternal,
    44  			tracepb.Span_SPAN_KIND_INTERNAL,
    45  		},
    46  		{
    47  			trace.SpanKindClient,
    48  			tracepb.Span_SPAN_KIND_CLIENT,
    49  		},
    50  		{
    51  			trace.SpanKindServer,
    52  			tracepb.Span_SPAN_KIND_SERVER,
    53  		},
    54  		{
    55  			trace.SpanKindProducer,
    56  			tracepb.Span_SPAN_KIND_PRODUCER,
    57  		},
    58  		{
    59  			trace.SpanKindConsumer,
    60  			tracepb.Span_SPAN_KIND_CONSUMER,
    61  		},
    62  		{
    63  			trace.SpanKind(-1),
    64  			tracepb.Span_SPAN_KIND_UNSPECIFIED,
    65  		},
    66  	} {
    67  		assert.Equal(t, test.expected, spanKind(test.kind))
    68  	}
    69  }
    70  
    71  func TestNilSpanEvent(t *testing.T) {
    72  	assert.Nil(t, spanEvents(nil))
    73  }
    74  
    75  func TestEmptySpanEvent(t *testing.T) {
    76  	assert.Nil(t, spanEvents([]tracesdk.Event{}))
    77  }
    78  
    79  func TestSpanEvent(t *testing.T) {
    80  	attrs := []attribute.KeyValue{attribute.Int("one", 1), attribute.Int("two", 2)}
    81  	eventTime := time.Date(2020, 5, 20, 0, 0, 0, 0, time.UTC)
    82  	got := spanEvents([]tracesdk.Event{
    83  		{
    84  			Name:       "test 1",
    85  			Attributes: []attribute.KeyValue{},
    86  			Time:       eventTime,
    87  		},
    88  		{
    89  			Name:                  "test 2",
    90  			Attributes:            attrs,
    91  			Time:                  eventTime,
    92  			DroppedAttributeCount: 2,
    93  		},
    94  	})
    95  	if !assert.Len(t, got, 2) {
    96  		return
    97  	}
    98  	eventTimestamp := uint64(1589932800 * 1e9)
    99  	assert.Equal(t, &tracepb.Span_Event{Name: "test 1", Attributes: nil, TimeUnixNano: eventTimestamp}, got[0])
   100  	// Do not test Attributes directly, just that the return value goes to the correct field.
   101  	assert.Equal(t, &tracepb.Span_Event{Name: "test 2", Attributes: KeyValues(attrs), TimeUnixNano: eventTimestamp, DroppedAttributesCount: 2}, got[1])
   102  }
   103  
   104  func TestNilLinks(t *testing.T) {
   105  	assert.Nil(t, links(nil))
   106  }
   107  
   108  func TestEmptyLinks(t *testing.T) {
   109  	assert.Nil(t, links([]tracesdk.Link{}))
   110  }
   111  
   112  func TestLinks(t *testing.T) {
   113  	attrs := []attribute.KeyValue{attribute.Int("one", 1), attribute.Int("two", 2)}
   114  	l := []tracesdk.Link{
   115  		{
   116  			DroppedAttributeCount: 3,
   117  		},
   118  		{
   119  			SpanContext:           trace.SpanContext{},
   120  			Attributes:            attrs,
   121  			DroppedAttributeCount: 3,
   122  		},
   123  	}
   124  	got := links(l)
   125  
   126  	// Make sure we get the same number back first.
   127  	if !assert.Len(t, got, 2) {
   128  		return
   129  	}
   130  
   131  	// Empty should be empty.
   132  	expected := &tracepb.Span_Link{
   133  		TraceId:                []uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
   134  		SpanId:                 []uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
   135  		DroppedAttributesCount: 3,
   136  	}
   137  	assert.Equal(t, expected, got[0])
   138  
   139  	// Do not test Attributes directly, just that the return value goes to the correct field.
   140  	expected.Attributes = KeyValues(attrs)
   141  	assert.Equal(t, expected, got[1])
   142  
   143  	// Changes to our links should not change the produced links.
   144  	l[1].SpanContext = l[1].SpanContext.WithTraceID(trace.TraceID{})
   145  	assert.Equal(t, expected, got[1])
   146  	assert.Equal(t, l[1].DroppedAttributeCount, int(got[1].DroppedAttributesCount))
   147  }
   148  
   149  func TestStatus(t *testing.T) {
   150  	for _, test := range []struct {
   151  		code       codes.Code
   152  		message    string
   153  		otlpStatus tracepb.Status_StatusCode
   154  	}{
   155  		{
   156  			codes.Ok,
   157  			"test Ok",
   158  			tracepb.Status_STATUS_CODE_OK,
   159  		},
   160  		{
   161  			codes.Unset,
   162  			"test Unset",
   163  			tracepb.Status_STATUS_CODE_UNSET,
   164  		},
   165  		{
   166  			message:    "default code is unset",
   167  			otlpStatus: tracepb.Status_STATUS_CODE_UNSET,
   168  		},
   169  		{
   170  			codes.Error,
   171  			"test Error",
   172  			tracepb.Status_STATUS_CODE_ERROR,
   173  		},
   174  	} {
   175  		expected := &tracepb.Status{Code: test.otlpStatus, Message: test.message}
   176  		assert.Equal(t, expected, status(test.code, test.message))
   177  	}
   178  }
   179  
   180  func TestNilSpan(t *testing.T) {
   181  	assert.Nil(t, span(nil))
   182  }
   183  
   184  func TestNilSpanData(t *testing.T) {
   185  	assert.Nil(t, Spans(nil))
   186  }
   187  
   188  func TestEmptySpanData(t *testing.T) {
   189  	assert.Nil(t, Spans(nil))
   190  }
   191  
   192  func TestSpanData(t *testing.T) {
   193  	// Full test of span data
   194  
   195  	// March 31, 2020 5:01:26 1234nanos (UTC)
   196  	startTime := time.Unix(1585674086, 1234)
   197  	endTime := startTime.Add(10 * time.Second)
   198  	traceState, _ := trace.ParseTraceState("key1=val1,key2=val2")
   199  	spanData := tracetest.SpanStub{
   200  		SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
   201  			TraceID:    trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
   202  			SpanID:     trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
   203  			TraceState: traceState,
   204  		}),
   205  		Parent: trace.NewSpanContext(trace.SpanContextConfig{
   206  			TraceID:    trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
   207  			SpanID:     trace.SpanID{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8},
   208  			TraceState: traceState,
   209  			Remote:     true,
   210  		}),
   211  		SpanKind:  trace.SpanKindServer,
   212  		Name:      "span data to span data",
   213  		StartTime: startTime,
   214  		EndTime:   endTime,
   215  		Events: []tracesdk.Event{
   216  			{
   217  				Time: startTime,
   218  				Attributes: []attribute.KeyValue{
   219  					attribute.Int64("CompressedByteSize", 512),
   220  				},
   221  			},
   222  			{
   223  				Time: endTime,
   224  				Attributes: []attribute.KeyValue{
   225  					attribute.String("EventType", "Recv"),
   226  				},
   227  			},
   228  		},
   229  		Links: []tracesdk.Link{
   230  			{
   231  				SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
   232  					TraceID:    trace.TraceID{0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF},
   233  					SpanID:     trace.SpanID{0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7},
   234  					TraceFlags: 0,
   235  				}),
   236  				Attributes: []attribute.KeyValue{
   237  					attribute.String("LinkType", "Parent"),
   238  				},
   239  				DroppedAttributeCount: 0,
   240  			},
   241  			{
   242  				SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
   243  					TraceID:    trace.TraceID{0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF},
   244  					SpanID:     trace.SpanID{0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7},
   245  					TraceFlags: 0,
   246  				}),
   247  				Attributes: []attribute.KeyValue{
   248  					attribute.String("LinkType", "Child"),
   249  				},
   250  				DroppedAttributeCount: 0,
   251  			},
   252  		},
   253  		Status: tracesdk.Status{
   254  			Code:        codes.Error,
   255  			Description: "utterly unrecognized",
   256  		},
   257  		Attributes: []attribute.KeyValue{
   258  			attribute.Int64("timeout_ns", 12e9),
   259  		},
   260  		DroppedAttributes: 1,
   261  		DroppedEvents:     2,
   262  		DroppedLinks:      3,
   263  		Resource: resource.NewWithAttributes(
   264  			"http://example.com/custom-resource-schema",
   265  			attribute.String("rk1", "rv1"),
   266  			attribute.Int64("rk2", 5),
   267  			attribute.StringSlice("rk3", []string{"sv1", "sv2"}),
   268  		),
   269  		InstrumentationLibrary: instrumentation.Scope{
   270  			Name:      "go.opentelemetry.io/test/otel",
   271  			Version:   "v0.0.1",
   272  			SchemaURL: semconv.SchemaURL,
   273  		},
   274  	}
   275  
   276  	// Not checking resource as the underlying map of our Resource makes
   277  	// ordering impossible to guarantee on the output. The Resource
   278  	// transform function has unit tests that should suffice.
   279  	expectedSpan := &tracepb.Span{
   280  		TraceId:                []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
   281  		SpanId:                 []byte{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8},
   282  		ParentSpanId:           []byte{0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA, 0xE9, 0xE8},
   283  		TraceState:             "key1=val1,key2=val2",
   284  		Name:                   spanData.Name,
   285  		Kind:                   tracepb.Span_SPAN_KIND_SERVER,
   286  		StartTimeUnixNano:      uint64(startTime.UnixNano()),
   287  		EndTimeUnixNano:        uint64(endTime.UnixNano()),
   288  		Status:                 status(spanData.Status.Code, spanData.Status.Description),
   289  		Events:                 spanEvents(spanData.Events),
   290  		Links:                  links(spanData.Links),
   291  		Attributes:             KeyValues(spanData.Attributes),
   292  		DroppedAttributesCount: 1,
   293  		DroppedEventsCount:     2,
   294  		DroppedLinksCount:      3,
   295  	}
   296  
   297  	got := Spans(tracetest.SpanStubs{spanData}.Snapshots())
   298  	require.Len(t, got, 1)
   299  
   300  	assert.Equal(t, got[0].GetResource(), Resource(spanData.Resource))
   301  	assert.Equal(t, got[0].SchemaUrl, spanData.Resource.SchemaURL())
   302  	scopeSpans := got[0].GetScopeSpans()
   303  	require.Len(t, scopeSpans, 1)
   304  	assert.Equal(t, scopeSpans[0].SchemaUrl, spanData.InstrumentationLibrary.SchemaURL)
   305  	assert.Equal(t, scopeSpans[0].GetScope(), InstrumentationScope(spanData.InstrumentationLibrary))
   306  	require.Len(t, scopeSpans[0].Spans, 1)
   307  	actualSpan := scopeSpans[0].Spans[0]
   308  
   309  	if diff := cmp.Diff(expectedSpan, actualSpan, cmp.Comparer(proto.Equal)); diff != "" {
   310  		t.Fatalf("transformed span differs %v\n", diff)
   311  	}
   312  }
   313  
   314  // Empty parent span ID should be treated as root span.
   315  func TestRootSpanData(t *testing.T) {
   316  	sd := Spans(tracetest.SpanStubs{
   317  		{},
   318  	}.Snapshots())
   319  	require.Len(t, sd, 1)
   320  	rs := sd[0]
   321  	scopeSpans := rs.GetScopeSpans()
   322  	require.Len(t, scopeSpans, 1)
   323  	got := scopeSpans[0].GetSpans()[0].GetParentSpanId()
   324  
   325  	// Empty means root span.
   326  	assert.Nil(t, got, "incorrect transform of root parent span ID")
   327  }
   328  
   329  func TestSpanDataNilResource(t *testing.T) {
   330  	assert.NotPanics(t, func() {
   331  		Spans(tracetest.SpanStubs{
   332  			{},
   333  		}.Snapshots())
   334  	})
   335  }
   336  

View as plain text