diff --git a/pkg/trace/api/otlp.go b/pkg/trace/api/otlp.go index 7e70f13b52b2e6..5095e55d27b65a 100644 --- a/pkg/trace/api/otlp.go +++ b/pkg/trace/api/otlp.go @@ -656,7 +656,7 @@ func (o *OTLPReceiver) convertSpan(rattr map[string]string, lib pcommon.Instrume } } if span.Type == "" { - span.Type = spanKind2Type(in.Kind(), span) + span.Type = traceutil.SpanKind2Type(in, rattr) } return span } @@ -696,30 +696,6 @@ func resourceFromTags(meta map[string]string) string { return "" } -// spanKind2Type returns a span's type based on the given kind and other present properties. -func spanKind2Type(kind ptrace.SpanKind, span *pb.Span) string { - var typ string - switch kind { - case ptrace.SpanKindServer: - typ = "web" - case ptrace.SpanKindClient: - typ = "http" - db, ok := span.Meta[string(semconv.AttributeDBSystem)] - if !ok { - break - } - switch db { - case "redis", "memcached": - typ = "cache" - default: - typ = "db" - } - default: - typ = "custom" - } - return typ -} - // computeTopLevelAndMeasured updates the span's top-level and measured attributes. // // An OTLP span is considered top-level if it is a root span or has a span kind of server or consumer. diff --git a/pkg/trace/api/otlp_test.go b/pkg/trace/api/otlp_test.go index 0966ee75ff1e91..60cbe3092ead9d 100644 --- a/pkg/trace/api/otlp_test.go +++ b/pkg/trace/api/otlp_test.go @@ -1590,55 +1590,7 @@ func TestOTLPHelpers(t *testing.T) { } }) - t.Run("spanKind2Type", func(t *testing.T) { - for _, tt := range []struct { - kind ptrace.SpanKind - meta map[string]string - out string - }{ - { - kind: ptrace.SpanKindServer, - out: "web", - }, - { - kind: ptrace.SpanKindClient, - out: "http", - }, - { - kind: ptrace.SpanKindClient, - meta: map[string]string{"db.system": "redis"}, - out: "cache", - }, - { - kind: ptrace.SpanKindClient, - meta: map[string]string{"db.system": "memcached"}, - out: "cache", - }, - { - kind: ptrace.SpanKindClient, - meta: map[string]string{"db.system": "other"}, - out: "db", - }, - { - kind: ptrace.SpanKindProducer, - out: "custom", - }, - { - kind: ptrace.SpanKindConsumer, - out: "custom", - }, - { - kind: ptrace.SpanKindInternal, - out: "custom", - }, - { - kind: ptrace.SpanKindUnspecified, - out: "custom", - }, - } { - assert.Equal(t, tt.out, spanKind2Type(tt.kind, &pb.Span{Meta: tt.meta})) - } - }) + // test spanKind2Type moved to pkg/trace/traceutil/otel_util_test.go t.Run("tagsFromHeaders", func(t *testing.T) { out := tagsFromHeaders(http.Header(map[string][]string{ diff --git a/pkg/trace/stats/otel_util_test.go b/pkg/trace/stats/otel_util_test.go index 939c27f8c9f77a..ea37f44c05d795 100644 --- a/pkg/trace/stats/otel_util_test.go +++ b/pkg/trace/stats/otel_util_test.go @@ -9,8 +9,6 @@ import ( "testing" "time" - pb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/trace" - "github.com/DataDog/datadog-agent/pkg/trace/config" "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" @@ -20,6 +18,9 @@ import ( semconv "go.opentelemetry.io/collector/semconv/v1.17.0" "go.opentelemetry.io/otel/metric/noop" "google.golang.org/protobuf/testing/protocmp" + + pb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/trace" + "github.com/DataDog/datadog-agent/pkg/trace/config" ) var ( diff --git a/pkg/trace/traceutil/otel_util.go b/pkg/trace/traceutil/otel_util.go index 25c03f1c1132d6..56a6ad385fad5c 100644 --- a/pkg/trace/traceutil/otel_util.go +++ b/pkg/trace/traceutil/otel_util.go @@ -219,7 +219,37 @@ func GetOTelAttrValInResAndSpanAttrs(span ptrace.Span, res pcommon.Resource, nor return GetOTelAttrVal(span.Attributes(), normalize, keys...) } +// SpanKind2Type returns a span's type based on the given kind and other present properties. +// This function is used in Resource V1 logic only. See GetOtelSpanType for Resource V2 logic. +func SpanKind2Type(span ptrace.Span, rattr map[string]string) string { + var typ string + switch span.Kind() { + case ptrace.SpanKindServer: + typ = "web" + case ptrace.SpanKindClient: + typ = "http" + res := pcommon.NewResource() + for k, v := range rattr { + res.Attributes().PutStr(k, v) + } + db := GetOTelAttrValInResAndSpanAttrs(span, res, true, semconv.AttributeDBSystem) + if db == "" { + break + } + switch db { + case "redis", "memcached": + typ = "cache" + default: + typ = "db" + } + default: + typ = "custom" + } + return typ +} + // GetOTelSpanType returns the DD span type based on OTel span kind and attributes. +// This uses Resource and operation V2 logic func GetOTelSpanType(span ptrace.Span, res pcommon.Resource) string { typ := GetOTelAttrValInResAndSpanAttrs(span, res, false, "span.type") if typ != "" { diff --git a/pkg/trace/traceutil/otel_util_test.go b/pkg/trace/traceutil/otel_util_test.go index fc9a9c777e7f56..25246e0dd9c013 100644 --- a/pkg/trace/traceutil/otel_util_test.go +++ b/pkg/trace/traceutil/otel_util_test.go @@ -239,6 +239,61 @@ func TestGetOTelSpanType(t *testing.T) { } } +func TestSpanKind2Type(t *testing.T) { + for _, tt := range []struct { + kind ptrace.SpanKind + meta map[string]string + out string + }{ + { + kind: ptrace.SpanKindServer, + out: "web", + }, + { + kind: ptrace.SpanKindClient, + out: "http", + }, + { + kind: ptrace.SpanKindClient, + meta: map[string]string{"db.system": "redis"}, + out: "cache", + }, + { + kind: ptrace.SpanKindClient, + meta: map[string]string{"db.system": "memcached"}, + out: "cache", + }, + { + kind: ptrace.SpanKindClient, + meta: map[string]string{"db.system": "other"}, + out: "db", + }, + { + kind: ptrace.SpanKindProducer, + out: "custom", + }, + { + kind: ptrace.SpanKindConsumer, + out: "custom", + }, + { + kind: ptrace.SpanKindInternal, + out: "custom", + }, + { + kind: ptrace.SpanKindUnspecified, + out: "custom", + }, + } { + t.Run(tt.out, func(t *testing.T) { + span := ptrace.NewSpan() + span.SetKind(tt.kind) + actual := SpanKind2Type(span, tt.meta) + assert.Equal(t, tt.out, actual) + }) + } +} + func TestGetOTelService(t *testing.T) { for _, tt := range []struct { name string diff --git a/pkg/trace/transform/transform.go b/pkg/trace/transform/transform.go index c1d5ff03b1a66d..0f45ccd14300e0 100644 --- a/pkg/trace/transform/transform.go +++ b/pkg/trace/transform/transform.go @@ -13,14 +13,15 @@ import ( "strconv" "strings" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/ptrace" + semconv "go.opentelemetry.io/collector/semconv/v1.6.1" + pb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/trace" "github.com/DataDog/datadog-agent/pkg/trace/config" "github.com/DataDog/datadog-agent/pkg/trace/sampler" "github.com/DataDog/datadog-agent/pkg/trace/traceutil" "github.com/DataDog/datadog-agent/pkg/util/log" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/ptrace" - semconv "go.opentelemetry.io/collector/semconv/v1.6.1" ) // OperationAndResourceNameV2Enabled checks if the new operation and resource name logic should be used @@ -41,12 +42,24 @@ func OtelSpanToDDSpanMinimal( ) *pb.Span { var operationName string var resourceName string + var spanType string if OperationAndResourceNameV2Enabled(conf) { operationName = traceutil.GetOTelOperationNameV2(otelspan) resourceName = traceutil.GetOTelResourceV2(otelspan, otelres) + spanType = traceutil.GetOTelSpanType(otelspan, otelres) } else { operationName = traceutil.GetOTelOperationNameV1(otelspan, otelres, lib, conf.OTLPReceiver.SpanNameAsResourceName, conf.OTLPReceiver.SpanNameRemappings, true) resourceName = traceutil.GetOTelResourceV1(otelspan, otelres) + attr := otelres.Attributes() + rattr := make(map[string]string, attr.Len()) + attr.Range(func(k string, v pcommon.Value) bool { + rattr[k] = v.AsString() + return true + }) + spanType = traceutil.GetOTelAttrVal(otelspan.Attributes(), true, "span.type") + if spanType == "" { + spanType = traceutil.SpanKind2Type(otelspan, rattr) + } } ddspan := &pb.Span{ @@ -58,7 +71,7 @@ func OtelSpanToDDSpanMinimal( ParentID: traceutil.OTelSpanIDToUint64(otelspan.ParentSpanID()), Start: int64(otelspan.StartTimestamp()), Duration: int64(otelspan.EndTimestamp()) - int64(otelspan.StartTimestamp()), - Type: traceutil.GetOTelSpanType(otelspan, otelres), + Type: spanType, Meta: make(map[string]string, otelres.Attributes().Len()+otelspan.Attributes().Len()), Metrics: map[string]float64{}, }