diff --git a/enrichments/trace/config/config.go b/enrichments/trace/config/config.go index 6e7e22c..568651e 100644 --- a/enrichments/trace/config/config.go +++ b/enrichments/trace/config/config.go @@ -40,17 +40,22 @@ type ScopeConfig struct { // ElasticTransactionConfig configures the enrichment attributes for the // spans which are identified as elastic transaction. type ElasticTransactionConfig struct { - Root AttributeConfig `mapstructure:"root"` - Name AttributeConfig `mapstructure:"name"` - Type AttributeConfig `mapstructure:"type"` - Result AttributeConfig `mapstructure:"result"` - EventOutcome AttributeConfig `mapstructure:"event_outcome"` + ID AttributeConfig `mapstructure:"id"` + Root AttributeConfig `mapstructure:"root"` + Name AttributeConfig `mapstructure:"name"` + ProcessorEvent AttributeConfig `mapstructure:"processor_event"` + DurationUs AttributeConfig `mapstructure:"duration_us"` + Type AttributeConfig `mapstructure:"type"` + Result AttributeConfig `mapstructure:"result"` + EventOutcome AttributeConfig `mapstructure:"event_outcome"` } // ElasticSpanConfig configures the enrichment attributes for the spans // which are NOT identified as elastic transaction. type ElasticSpanConfig struct { Name AttributeConfig `mapstructure:"name"` + ProcessorEvent AttributeConfig `mapstructure:"processor_event"` + DurationUs AttributeConfig `mapstructure:"duration_us"` EventOutcome AttributeConfig `mapstructure:"event_outcome"` ServiceTarget AttributeConfig `mapstructure:"service_target"` DestinationService AttributeConfig `mapstructure:"destination_service"` @@ -73,14 +78,19 @@ func Enabled() Config { ServiceFrameworkVersion: AttributeConfig{Enabled: true}, }, Transaction: ElasticTransactionConfig{ - Root: AttributeConfig{Enabled: true}, - Name: AttributeConfig{Enabled: true}, - Type: AttributeConfig{Enabled: true}, - Result: AttributeConfig{Enabled: true}, - EventOutcome: AttributeConfig{Enabled: true}, + ID: AttributeConfig{Enabled: true}, + Root: AttributeConfig{Enabled: true}, + Name: AttributeConfig{Enabled: true}, + ProcessorEvent: AttributeConfig{Enabled: true}, + DurationUs: AttributeConfig{Enabled: true}, + Type: AttributeConfig{Enabled: true}, + Result: AttributeConfig{Enabled: true}, + EventOutcome: AttributeConfig{Enabled: true}, }, Span: ElasticSpanConfig{ Name: AttributeConfig{Enabled: true}, + ProcessorEvent: AttributeConfig{Enabled: true}, + DurationUs: AttributeConfig{Enabled: true}, EventOutcome: AttributeConfig{Enabled: true}, ServiceTarget: AttributeConfig{Enabled: true}, DestinationService: AttributeConfig{Enabled: true}, diff --git a/enrichments/trace/internal/elastic/attributes.go b/enrichments/trace/internal/elastic/attributes.go index 890df26..d72316e 100644 --- a/enrichments/trace/internal/elastic/attributes.go +++ b/enrichments/trace/internal/elastic/attributes.go @@ -27,13 +27,18 @@ const ( AttributeServiceFrameworkVersion = "service.framework.version" // span attributes + AttributeProcessorEvent = "processor.event" + AttributeTransactionID = "transaction.id" AttributeTransactionRoot = "transaction.root" AttributeTransactionName = "transaction.name" AttributeTransactionType = "transaction.type" + AttributeTransactionDurationUs = "transaction.duration.us" AttributeTransactionResult = "transaction.result" AttributeSpanName = "span.name" AttributeEventOutcome = "event.outcome" + AttributeSuccessCount = "event.success_count" AttributeServiceTargetType = "service.target.type" AttributeServiceTargetName = "service.target.name" AttributeSpanDestinationServiceResource = "span.destination.service.resource" + AttributeSpanDurationUs = "span.duration.us" ) diff --git a/enrichments/trace/internal/elastic/span.go b/enrichments/trace/internal/elastic/span.go index efdaea8..d304f6e 100644 --- a/enrichments/trace/internal/elastic/span.go +++ b/enrichments/trace/internal/elastic/span.go @@ -161,12 +161,21 @@ func (s *spanEnrichmentContext) enrichTransaction( span ptrace.Span, cfg config.ElasticTransactionConfig, ) { + if cfg.ID.Enabled { + span.Attributes().PutStr(AttributeTransactionID, span.SpanID().String()) + } if cfg.Root.Enabled { span.Attributes().PutBool(AttributeTransactionRoot, isTraceRoot(span)) } if cfg.Name.Enabled { span.Attributes().PutStr(AttributeTransactionName, span.Name()) } + if cfg.ProcessorEvent.Enabled { + span.Attributes().PutStr(AttributeProcessorEvent, "transaction") + } + if cfg.DurationUs.Enabled { + span.Attributes().PutInt(AttributeTransactionDurationUs, getDurationUs(span)) + } if cfg.Type.Enabled { s.setTxnType(span) } @@ -185,9 +194,15 @@ func (s *spanEnrichmentContext) enrichSpan( if cfg.Name.Enabled { span.Attributes().PutStr(AttributeSpanName, span.Name()) } + if cfg.ProcessorEvent.Enabled { + span.Attributes().PutStr(AttributeProcessorEvent, "span") + } if cfg.EventOutcome.Enabled { s.setEventOutcome(span) } + if cfg.DurationUs.Enabled { + span.Attributes().PutInt(AttributeSpanDurationUs, getDurationUs(span)) + } if cfg.ServiceTarget.Enabled { s.setServiceTarget(span) } @@ -245,17 +260,21 @@ func (s *spanEnrichmentContext) setTxnResult(span ptrace.Span) { func (s *spanEnrichmentContext) setEventOutcome(span ptrace.Span) { // default to success outcome outcome := "success" + successCount := 1 switch { case s.spanStatusCode == ptrace.StatusCodeError: outcome = "failure" + successCount = 0 case s.spanStatusCode == ptrace.StatusCodeOk: // keep the default success outcome case s.httpStatusCode >= http.StatusInternalServerError: // TODO (lahsivjar): Handle GRPC status code? - not handled in apm-data // TODO (lahsivjar): Move to HTTPResponseStatusCode? Backward compatibility? outcome = "failure" + successCount = 0 } span.Attributes().PutStr(AttributeEventOutcome, outcome) + span.Attributes().PutInt(AttributeSuccessCount, int64(successCount)) } func (s *spanEnrichmentContext) setServiceTarget(span ptrace.Span) { @@ -337,6 +356,10 @@ func (s *spanEnrichmentContext) setDestinationService(span ptrace.Span) { } } +func getDurationUs(span ptrace.Span) int64 { + return int64(span.EndTimestamp()-span.StartTimestamp()) / 1000 +} + func isTraceRoot(span ptrace.Span) bool { return span.ParentSpanID().IsEmpty() } diff --git a/enrichments/trace/internal/elastic/span_test.go b/enrichments/trace/internal/elastic/span_test.go index 27fecd9..d0882d3 100644 --- a/enrichments/trace/internal/elastic/span_test.go +++ b/enrichments/trace/internal/elastic/span_test.go @@ -20,10 +20,12 @@ package elastic import ( "net/http" "testing" + "time" "github.com/elastic/opentelemetry-lib/enrichments/trace/config" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" semconv "go.opentelemetry.io/collector/semconv/v1.25.0" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" @@ -32,6 +34,17 @@ import ( // Tests the enrichment logic for elastic's transaction definition. func TestElasticTransactionEnrich(t *testing.T) { + now := time.Unix(3600, 0) + expectedDuration := time.Minute + endTs := pcommon.NewTimestampFromTime(now) + startTs := pcommon.NewTimestampFromTime(now.Add(-1 * expectedDuration)) + getElasticTxn := func() ptrace.Span { + span := ptrace.NewSpan() + span.SetSpanID([8]byte{1}) + span.SetStartTimestamp(startTs) + span.SetEndTimestamp(endTs) + return span + } for _, tc := range []struct { name string input ptrace.Span @@ -39,44 +52,54 @@ func TestElasticTransactionEnrich(t *testing.T) { enrichedAttrs map[string]any }{ { + // test case gives a summary of what is emitted by default name: "empty", input: ptrace.NewSpan(), config: config.Enabled().Transaction, enrichedAttrs: map[string]any{ - AttributeTransactionRoot: true, - AttributeTransactionName: "", - AttributeEventOutcome: "success", - AttributeTransactionResult: "Success", - AttributeTransactionType: "unknown", + AttributeTransactionRoot: true, + AttributeTransactionID: "", + AttributeTransactionName: "", + AttributeProcessorEvent: "transaction", + AttributeTransactionDurationUs: int64(0), + AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), + AttributeTransactionResult: "Success", + AttributeTransactionType: "unknown", }, }, { name: "all_disabled", - input: ptrace.NewSpan(), + input: getElasticTxn(), enrichedAttrs: map[string]any{}, }, { name: "http_status_ok", input: func() ptrace.Span { - span := ptrace.NewSpan() + span := getElasticTxn() span.SetName("testtxn") span.Attributes().PutInt(semconv.AttributeHTTPStatusCode, http.StatusOK) return span }(), config: config.Enabled().Transaction, enrichedAttrs: map[string]any{ - AttributeTransactionRoot: true, - AttributeTransactionName: "testtxn", - AttributeEventOutcome: "success", - AttributeTransactionResult: "HTTP 2xx", - AttributeTransactionType: "request", + AttributeTransactionRoot: true, + AttributeTransactionID: "0100000000000000", + AttributeTransactionName: "testtxn", + AttributeProcessorEvent: "transaction", + AttributeTransactionDurationUs: expectedDuration.Microseconds(), + AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), + AttributeTransactionResult: "HTTP 2xx", + AttributeTransactionType: "request", }, }, { name: "http_status_1xx", input: func() ptrace.Span { - span := ptrace.NewSpan() + span := getElasticTxn() span.SetName("testtxn") + span.SetSpanID([8]byte{1}) // attributes should be preferred over span status for txn result span.Status().SetCode(ptrace.StatusCodeOk) span.Attributes().PutInt( @@ -87,18 +110,23 @@ func TestElasticTransactionEnrich(t *testing.T) { }(), config: config.Enabled().Transaction, enrichedAttrs: map[string]any{ - AttributeTransactionRoot: true, - AttributeTransactionName: "testtxn", - AttributeEventOutcome: "success", - AttributeTransactionResult: "HTTP 1xx", - AttributeTransactionType: "request", + AttributeTransactionRoot: true, + AttributeTransactionID: "0100000000000000", + AttributeTransactionName: "testtxn", + AttributeProcessorEvent: "transaction", + AttributeTransactionDurationUs: expectedDuration.Microseconds(), + AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), + AttributeTransactionResult: "HTTP 1xx", + AttributeTransactionType: "request", }, }, { name: "http_status_5xx", input: func() ptrace.Span { - span := ptrace.NewSpan() + span := getElasticTxn() span.SetName("testtxn") + span.SetSpanID([8]byte{1}) // span status code should take precedence over http status attributes // for setting event.outcome span.Status().SetCode(ptrace.StatusCodeOk) @@ -108,18 +136,23 @@ func TestElasticTransactionEnrich(t *testing.T) { }(), config: config.Enabled().Transaction, enrichedAttrs: map[string]any{ - AttributeTransactionRoot: true, - AttributeTransactionName: "testtxn", - AttributeEventOutcome: "success", - AttributeTransactionResult: "HTTP 5xx", - AttributeTransactionType: "request", + AttributeTransactionRoot: true, + AttributeTransactionID: "0100000000000000", + AttributeTransactionName: "testtxn", + AttributeProcessorEvent: "transaction", + AttributeTransactionDurationUs: expectedDuration.Microseconds(), + AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), + AttributeTransactionResult: "HTTP 5xx", + AttributeTransactionType: "request", }, }, { name: "grpc_status_ok", input: func() ptrace.Span { - span := ptrace.NewSpan() + span := getElasticTxn() span.SetName("testtxn") + span.SetSpanID([8]byte{1}) // attributes should be preferred over span status for txn result span.Status().SetCode(ptrace.StatusCodeOk) span.Attributes().PutInt( @@ -130,18 +163,23 @@ func TestElasticTransactionEnrich(t *testing.T) { }(), config: config.Enabled().Transaction, enrichedAttrs: map[string]any{ - AttributeTransactionRoot: true, - AttributeTransactionName: "testtxn", - AttributeEventOutcome: "success", - AttributeTransactionResult: "OK", - AttributeTransactionType: "request", + AttributeTransactionRoot: true, + AttributeTransactionID: "0100000000000000", + AttributeTransactionName: "testtxn", + AttributeProcessorEvent: "transaction", + AttributeTransactionDurationUs: expectedDuration.Microseconds(), + AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), + AttributeTransactionResult: "OK", + AttributeTransactionType: "request", }, }, { name: "grpc_status_internal_error", input: func() ptrace.Span { - span := ptrace.NewSpan() + span := getElasticTxn() span.SetName("testtxn") + span.SetSpanID([8]byte{1}) // attributes should be preferred over span status for txn result span.Status().SetCode(ptrace.StatusCodeOk) span.Attributes().PutInt( @@ -152,62 +190,81 @@ func TestElasticTransactionEnrich(t *testing.T) { }(), config: config.Enabled().Transaction, enrichedAttrs: map[string]any{ - AttributeTransactionRoot: true, - AttributeTransactionName: "testtxn", - AttributeEventOutcome: "success", - AttributeTransactionResult: "Internal", - AttributeTransactionType: "request", + AttributeTransactionRoot: true, + AttributeTransactionID: "0100000000000000", + AttributeTransactionName: "testtxn", + AttributeProcessorEvent: "transaction", + AttributeTransactionDurationUs: expectedDuration.Microseconds(), + AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), + AttributeTransactionResult: "Internal", + AttributeTransactionType: "request", }, }, { name: "span_status_ok", input: func() ptrace.Span { - span := ptrace.NewSpan() + span := getElasticTxn() span.SetName("testtxn") + span.SetSpanID([8]byte{1}) span.Status().SetCode(ptrace.StatusCodeOk) return span }(), config: config.Enabled().Transaction, enrichedAttrs: map[string]any{ - AttributeTransactionRoot: true, - AttributeTransactionName: "testtxn", - AttributeEventOutcome: "success", - AttributeTransactionResult: "Success", - AttributeTransactionType: "unknown", + AttributeTransactionRoot: true, + AttributeTransactionID: "0100000000000000", + AttributeTransactionName: "testtxn", + AttributeProcessorEvent: "transaction", + AttributeTransactionDurationUs: expectedDuration.Microseconds(), + AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), + AttributeTransactionResult: "Success", + AttributeTransactionType: "unknown", }, }, { name: "span_status_error", input: func() ptrace.Span { - span := ptrace.NewSpan() + span := getElasticTxn() span.SetName("testtxn") + span.SetSpanID([8]byte{1}) span.Status().SetCode(ptrace.StatusCodeError) return span }(), config: config.Enabled().Transaction, enrichedAttrs: map[string]any{ - AttributeTransactionRoot: true, - AttributeTransactionName: "testtxn", - AttributeEventOutcome: "failure", - AttributeTransactionResult: "Error", - AttributeTransactionType: "unknown", + AttributeTransactionRoot: true, + AttributeTransactionID: "0100000000000000", + AttributeTransactionName: "testtxn", + AttributeProcessorEvent: "transaction", + AttributeTransactionDurationUs: expectedDuration.Microseconds(), + AttributeEventOutcome: "failure", + AttributeSuccessCount: int64(0), + AttributeTransactionResult: "Error", + AttributeTransactionType: "unknown", }, }, { name: "messaging_type_kafka", input: func() ptrace.Span { - span := ptrace.NewSpan() + span := getElasticTxn() span.SetName("testtxn") + span.SetSpanID([8]byte{1}) span.Attributes().PutStr(semconv.AttributeMessagingSystem, "kafka") return span }(), config: config.Enabled().Transaction, enrichedAttrs: map[string]any{ - AttributeTransactionRoot: true, - AttributeTransactionName: "testtxn", - AttributeEventOutcome: "success", - AttributeTransactionResult: "Success", - AttributeTransactionType: "messaging", + AttributeTransactionRoot: true, + AttributeTransactionID: "0100000000000000", + AttributeTransactionName: "testtxn", + AttributeProcessorEvent: "transaction", + AttributeTransactionDurationUs: expectedDuration.Microseconds(), + AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), + AttributeTransactionResult: "Success", + AttributeTransactionType: "messaging", }, }, } { @@ -230,9 +287,15 @@ func TestElasticTransactionEnrich(t *testing.T) { // Tests the enrichment logic for elastic's span definition. func TestElasticSpanEnrich(t *testing.T) { + now := time.Unix(3600, 0) + expectedDuration := time.Minute + endTs := pcommon.NewTimestampFromTime(now) + startTs := pcommon.NewTimestampFromTime(now.Add(-1 * expectedDuration)) getElasticSpan := func() ptrace.Span { span := ptrace.NewSpan() span.SetParentSpanID([8]byte{8, 9, 10, 11, 12, 13, 14}) + span.SetStartTimestamp(startTs) + span.SetEndTimestamp(endTs) return span } for _, tc := range []struct { @@ -242,12 +305,20 @@ func TestElasticSpanEnrich(t *testing.T) { enrichedAttrs map[string]any }{ { - name: "empty", - input: getElasticSpan(), + // test case gives a summary of what is emitted by default + name: "empty", + input: func() ptrace.Span { + span := ptrace.NewSpan() + span.SetParentSpanID([8]byte{1}) + return span + }(), config: config.Enabled().Span, enrichedAttrs: map[string]any{ - AttributeSpanName: "", - AttributeEventOutcome: "success", + AttributeSpanName: "", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: int64(0), + AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), }, }, { @@ -266,7 +337,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetName: "testsvc", AttributeServiceTargetType: "", AttributeSpanDestinationServiceResource: "testsvc", @@ -287,7 +361,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "http", AttributeServiceTargetName: "testsvc", AttributeSpanDestinationServiceResource: "testsvc", @@ -314,7 +391,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "http", AttributeServiceTargetName: "www.foo.bar:443", AttributeSpanDestinationServiceResource: "testsvc", @@ -339,7 +419,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "http", AttributeServiceTargetName: "www.foo.bar:443", AttributeSpanDestinationServiceResource: "testsvc", @@ -360,7 +443,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "grpc", AttributeServiceTargetName: "testsvc", AttributeSpanDestinationServiceResource: "testsvc", @@ -378,7 +464,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "xmlrpc", AttributeServiceTargetName: "testsvc", AttributeSpanDestinationServiceResource: "testsvc", @@ -398,7 +487,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "external", AttributeServiceTargetName: "service.Test", AttributeSpanDestinationServiceResource: "testsvc", @@ -416,7 +508,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "kafka", AttributeServiceTargetName: "testsvc", AttributeSpanDestinationServiceResource: "testsvc", @@ -434,7 +529,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "messaging", AttributeServiceTargetName: "t1", AttributeSpanDestinationServiceResource: "testsvc/t1", @@ -453,7 +551,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "messaging", AttributeServiceTargetName: "testsvc", AttributeSpanDestinationServiceResource: "testsvc/t1", @@ -478,7 +579,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "elasticsearch", AttributeServiceTargetName: "testsvc", AttributeSpanDestinationServiceResource: "testsvc", @@ -508,7 +612,10 @@ func TestElasticSpanEnrich(t *testing.T) { config: config.Enabled().Span, enrichedAttrs: map[string]any{ AttributeSpanName: "testspan", + AttributeProcessorEvent: "span", + AttributeSpanDurationUs: expectedDuration.Microseconds(), AttributeEventOutcome: "success", + AttributeSuccessCount: int64(1), AttributeServiceTargetType: "cassandra", AttributeServiceTargetName: "testsvc", AttributeSpanDestinationServiceResource: "testsvc",