diff --git a/cmd/trace-agent/config/config.go b/cmd/trace-agent/config/config.go index 74cc20cb0b18c..260b6ebb0036c 100644 --- a/cmd/trace-agent/config/config.go +++ b/cmd/trace-agent/config/config.go @@ -255,9 +255,11 @@ func applyDatadogConfig(c *config.AgentConfig) error { grpcPort = coreconfig.Datadog.GetInt(coreconfig.OTLPTracePort) } c.OTLPReceiver = &config.OTLP{ - BindHost: c.ReceiverHost, - GRPCPort: grpcPort, - MaxRequestBytes: c.MaxRequestBytes, + BindHost: c.ReceiverHost, + GRPCPort: grpcPort, + MaxRequestBytes: c.MaxRequestBytes, + SpanNameRemappings: coreconfig.Datadog.GetStringMapString("otlp_config.traces.span_name_remappings"), + SpanNameAsResourceName: coreconfig.Datadog.GetBool("otlp_config.traces.span_name_as_resource_name"), } if coreconfig.Datadog.GetBool("apm_config.telemetry.enabled") { diff --git a/cmd/trace-agent/config/config_test.go b/cmd/trace-agent/config/config_test.go index aa0ac29b9ebc8..aab61689ac626 100644 --- a/cmd/trace-agent/config/config_test.go +++ b/cmd/trace-agent/config/config_test.go @@ -424,6 +424,8 @@ func TestFullYamlConfig(t *testing.T) { assert.EqualValues(123.4, c.MaxMemory) assert.Equal("0.0.0.0", c.ReceiverHost) assert.True(c.LogThrottling) + assert.True(c.OTLPReceiver.SpanNameAsResourceName) + assert.Equal(map[string]string{"a": "b", "and:colons": "in:values", "c": "d", "with.dots": "in.side"}, c.OTLPReceiver.SpanNameRemappings) noProxy := true if _, ok := os.LookupEnv("NO_PROXY"); ok { @@ -476,10 +478,10 @@ func TestFullYamlConfig(t *testing.T) { assert.True(o.HTTP.RemoveQueryString) assert.True(o.HTTP.RemovePathDigits) assert.True(o.RemoveStackTraces) - assert.True(c.Obfuscation.Redis.Enabled) - assert.True(c.Obfuscation.Memcached.Enabled) - assert.True(c.Obfuscation.CreditCards.Enabled) - assert.True(c.Obfuscation.CreditCards.Luhn) + assert.True(o.Redis.Enabled) + assert.True(o.Memcached.Enabled) + assert.True(o.CreditCards.Enabled) + assert.True(o.CreditCards.Luhn) } func TestUndocumentedYamlConfig(t *testing.T) { diff --git a/cmd/trace-agent/config/testdata/full.yaml b/cmd/trace-agent/config/testdata/full.yaml index a43f33a66dd1d..7f2d7efa76827 100644 --- a/cmd/trace-agent/config/testdata/full.yaml +++ b/cmd/trace-agent/config/testdata/full.yaml @@ -14,6 +14,12 @@ otlp_config: receiver: traces: internal_port: 50053 + span_name_remappings: + a: b + c: d + "with.dots": "in.side" + "and:colons": "in:values" + span_name_as_resource_name: true apm_config: enabled: false log_file: abc diff --git a/pkg/config/config_template.yaml b/pkg/config/config_template.yaml index cf6479411a0c8..eda7e59307b76 100644 --- a/pkg/config/config_template.yaml +++ b/pkg/config/config_template.yaml @@ -1029,6 +1029,7 @@ api_key: ## @param receiver_port - integer - optional - default: 8126 ## @env DD_APM_RECEIVER_PORT - integer - optional - default: 8126 ## The port that the trace receiver should listen on. + ## Set to 0 to disable the HTTP receiver. # # receiver_port: 8126 @@ -3254,3 +3255,22 @@ api_key: ## Whether to ingest traces through the OTLP endpoint. Set to false to disable OTLP traces ingest. # # enabled: true + + ## @param span_name_as_resource_name - boolean - optional - default: false + ## @env DD_OTLP_CONFIG_SPAN_NAME_AS_RESOURCE_NAME - boolean - optional - default: false + ## If set to true the OpenTelemetry span name will used in the Datadog resource name. + ## If set to false the resource name will be filled with the instrumentation library name + span kind. + # + # span_name_as_resource_name: false + + ## @param span_name_remappings - map - optional + ## @env DD_OTLP_CONFIG_SPAN_NAME_REMAPPINGS - boolean - optional + ## Defines a map of span names and preferred names to map to. This can be used to automatically map Datadog Span + ## Operation Names to an updated value. + ## span_name_remappings: + ## "io.opentelemetry.javaagent.spring.client": "spring.client" + ## "instrumentation:express.server": "express" + ## "go.opentelemetry.io_contrib_instrumentation_net_http_otelhttp.client": "http.client" + # + # span_name_remappings: + # : diff --git a/pkg/config/otlp.go b/pkg/config/otlp.go index 5a8ff30a75c0f..baaccf90360f3 100644 --- a/pkg/config/otlp.go +++ b/pkg/config/otlp.go @@ -148,6 +148,10 @@ func setupOTLPEnvironmentVariables(config Config) { config.BindEnv(OTLPSection + ".receiver.protocols.grpc.write_buffer_size") config.BindEnv(OTLPSection + ".receiver.protocols.grpc.include_metadata") + // Traces settingds + config.BindEnv("otlp_config.traces.span_name_remappings") + config.BindEnv("otlp_config.traces.span_name_as_resource_name") + // HTTP settings config.BindEnv(OTLPSection + ".receiver.protocols.http.endpoint") config.BindEnv(OTLPSection + ".receiver.protocols.http.max_request_body_size") diff --git a/pkg/trace/api/api.go b/pkg/trace/api/api.go index b088025f53d4b..bc349668d646d 100644 --- a/pkg/trace/api/api.go +++ b/pkg/trace/api/api.go @@ -134,6 +134,10 @@ func replyWithVersion(hash string, h http.Handler) http.Handler { // Start starts doing the HTTP server and is ready to receive traces func (r *HTTPReceiver) Start() { + if r.conf.ReceiverPort == 0 { + log.Debug("HTTP receiver disabled by config (apm_config.receiver_port: 0).") + return + } mux := r.buildMux() timeout := 5 * time.Second @@ -278,6 +282,9 @@ func (r *HTTPReceiver) listenTCP(addr string) (net.Listener, error) { // Stop stops the receiver and shuts down the HTTP server. func (r *HTTPReceiver) Stop() error { + if r.conf.ReceiverPort == 0 { + return nil + } r.exit <- struct{}{} <-r.exit diff --git a/pkg/trace/api/api_test.go b/pkg/trace/api/api_test.go index c34615f4705f8..b82ebcefaa85d 100644 --- a/pkg/trace/api/api_test.go +++ b/pkg/trace/api/api_test.go @@ -65,8 +65,10 @@ func newTestReceiverConfig() *config.AgentConfig { func TestMain(m *testing.M) { defer func(old func(string, ...interface{})) { killProcess = old }(killProcess) - killProcess = func(_ string, _ ...interface{}) {} - + killProcess = func(format string, args ...interface{}) { + fmt.Printf(format, args...) + fmt.Println() + } os.Exit(m.Run()) } diff --git a/pkg/trace/api/otlp.go b/pkg/trace/api/otlp.go index ca5416df74c88..67ad463a66b82 100644 --- a/pkg/trace/api/otlp.go +++ b/pkg/trace/api/otlp.go @@ -19,6 +19,7 @@ import ( "sync" "time" + "github.com/DataDog/datadog-agent/pkg/otlp/model/attributes" "github.com/DataDog/datadog-agent/pkg/trace/api/apiutil" "github.com/DataDog/datadog-agent/pkg/trace/config" "github.com/DataDog/datadog-agent/pkg/trace/info" @@ -27,7 +28,9 @@ import ( "github.com/DataDog/datadog-agent/pkg/trace/metrics/timing" "github.com/DataDog/datadog-agent/pkg/trace/pb" "github.com/DataDog/datadog-agent/pkg/trace/sampler" + "github.com/DataDog/datadog-agent/pkg/trace/traceutil" + "go.opentelemetry.io/collector/model/otlpgrpc" semconv "go.opentelemetry.io/collector/model/semconv/v1.6.1" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" @@ -83,7 +86,7 @@ func (o *OTLPReceiver) Start() { log.Criticalf("Error starting OpenTelemetry gRPC server: %v", err) } else { o.grpcsrv = grpc.NewServer() - ptraceotlp.RegisterServer(o.grpcsrv, o) + otlpgrpc.RegisterTracesServer(o.grpcsrv, o) o.wg.Add(1) go func() { defer o.wg.Done() @@ -113,13 +116,13 @@ func (o *OTLPReceiver) Stop() { o.wg.Wait() } -// Export implements ptraceotlp.TracesServer -func (o *OTLPReceiver) Export(ctx context.Context, in ptraceotlp.Request) (ptraceotlp.Response, error) { +// Export implements otlpgrpc.TracesServer +func (o *OTLPReceiver) Export(ctx context.Context, in ptraceotlp.Request) (otlpgrpc.TracesResponse, error) { defer timing.Since("datadog.trace_agent.otlp.process_grpc_request_ms", time.Now()) md, _ := metadata.FromIncomingContext(ctx) metrics.Count("datadog.trace_agent.otlp.payload", 1, tagsFromHeaders(http.Header(md), otlpProtocolGRPC), 1) o.processRequest(otlpProtocolGRPC, http.Header(md), in) - return ptraceotlp.NewResponse(), nil + return otlpgrpc.NewTracesResponse(), nil } // ServeHTTP implements http.Handler @@ -149,8 +152,7 @@ func (o *OTLPReceiver) ServeHTTP(w http.ResponseWriter, req *http.Request) { in := ptraceotlp.NewRequest() switch getMediaType(req) { case "application/x-protobuf": - err := in.UnmarshalProto(slurp) - if err != nil { + if err := in.UnmarshalProto(slurp); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) metrics.Count("datadog.trace_agent.otlp.error", 1, append(mtags, "reason:decode_proto"), 1) return @@ -158,8 +160,7 @@ func (o *OTLPReceiver) ServeHTTP(w http.ResponseWriter, req *http.Request) { case "application/json": fallthrough default: - err := in.UnmarshalJSON(slurp) - if err != nil { + if err := in.UnmarshalJSON(slurp); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) metrics.Count("datadog.trace_agent.otlp.error", 1, append(mtags, "reason:decode_json"), 1) return @@ -203,70 +204,124 @@ func fastHeaderGet(h http.Header, canonicalKey string) string { func (o *OTLPReceiver) processRequest(protocol string, header http.Header, in ptraceotlp.Request) { for i := 0; i < in.Traces().ResourceSpans().Len(); i++ { rspans := in.Traces().ResourceSpans().At(i) - // each rspans is coming from a different resource and should be considered - // a separate payload; typically there is only one item in this slice - attr := rspans.Resource().Attributes() - rattr := make(map[string]string, attr.Len()) - attr.Range(func(k string, v pcommon.Value) bool { - rattr[k] = v.AsString() - return true - }) - lang := rattr[string(semconv.AttributeTelemetrySDKLanguage)] - if lang == "" { - lang = fastHeaderGet(header, headerLang) - } - tagstats := &info.TagStats{ - Tags: info.Tags{ - Lang: lang, - LangVersion: fastHeaderGet(header, headerLangVersion), - Interpreter: fastHeaderGet(header, headerLangInterpreter), - LangVendor: fastHeaderGet(header, headerLangInterpreterVendor), - TracerVersion: fmt.Sprintf("otlp-%s", rattr[string(semconv.AttributeTelemetrySDKVersion)]), - EndpointVersion: fmt.Sprintf("opentelemetry_%s_v1", protocol), - }, - Stats: info.NewStats(), - } - tracesByID := make(map[uint64]pb.Trace) - for i := 0; i < rspans.ScopeSpans().Len(); i++ { - libspans := rspans.ScopeSpans().At(i) - lib := libspans.Scope() - for i := 0; i < libspans.Spans().Len(); i++ { - span := libspans.Spans().At(i) - traceID := traceIDToUint64(span.TraceID().Bytes()) - if tracesByID[traceID] == nil { - tracesByID[traceID] = pb.Trace{} + o.ReceiveResourceSpans(rspans, header, protocol) + } +} + +// OTLPIngestSummary returns a summary of the received resource spans. +type OTLPIngestSummary struct { + // Hostname indicates the hostname of the passed resource spans. + Hostname string + // Tags returns a set of Datadog-specific tags which are relevant for identifying + // the source of the passed resource spans. + Tags []string +} + +// ReceiveResourceSpans processes the given rspans and sends them to writer. +func (o *OTLPReceiver) ReceiveResourceSpans(rspans ptrace.ResourceSpans, header http.Header, protocol string) OTLPIngestSummary { + // each rspans is coming from a different resource and should be considered + // a separate payload; typically there is only one item in this slice + attr := rspans.Resource().Attributes() + rattr := make(map[string]string, attr.Len()) + attr.Range(func(k string, v pcommon.Value) bool { + rattr[k] = v.AsString() + return true + }) + hostname, _ := attributes.HostnameFromAttributes(attr) + env := rattr[string(semconv.AttributeDeploymentEnvironment)] + lang := rattr[string(semconv.AttributeTelemetrySDKLanguage)] + if lang == "" { + lang = fastHeaderGet(header, headerLang) + } + containerID := rattr[string(semconv.AttributeContainerID)] + if containerID == "" { + containerID = rattr[string(semconv.AttributeK8SPodUID)] + } + if containerID == "" { + containerID = fastHeaderGet(header, headerContainerID) + } + tagstats := &info.TagStats{ + Tags: info.Tags{ + Lang: lang, + LangVersion: fastHeaderGet(header, headerLangVersion), + Interpreter: fastHeaderGet(header, headerLangInterpreter), + LangVendor: fastHeaderGet(header, headerLangInterpreterVendor), + TracerVersion: fmt.Sprintf("otlp-%s", rattr[string(semconv.AttributeTelemetrySDKVersion)]), + EndpointVersion: fmt.Sprintf("opentelemetry_%s_v1", protocol), + }, + Stats: info.NewStats(), + } + tracesByID := make(map[uint64]pb.Trace) + for i := 0; i < rspans.InstrumentationLibrarySpans().Len(); i++ { + libspans := rspans.InstrumentationLibrarySpans().At(i) + lib := libspans.InstrumentationLibrary() + for i := 0; i < libspans.Spans().Len(); i++ { + span := libspans.Spans().At(i) + traceID := traceIDToUint64(span.TraceID().Bytes()) + if tracesByID[traceID] == nil { + tracesByID[traceID] = pb.Trace{} + } + ddspan := o.convertSpan(rattr, lib, span) + if hostname == "" { + // if we didn't find a hostname at the resource level + // try and see if the span has a hostname set + if v := ddspan.Meta["_dd.hostname"]; v != "" { + hostname = v } - tracesByID[traceID] = append(tracesByID[traceID], convertSpan(rattr, lib, span)) } - } - tags := tagstats.AsTags() - metrics.Count("datadog.trace_agent.otlp.spans", int64(rspans.ScopeSpans().Len()), tags, 1) - metrics.Count("datadog.trace_agent.otlp.traces", int64(len(tracesByID)), tags, 1) - traceChunks := make([]*pb.TraceChunk, 0, len(tracesByID)) - p := Payload{ - Source: tagstats, - } - for _, spans := range tracesByID { - traceChunks = append(traceChunks, &pb.TraceChunk{ - // auto-keep all incoming traces; it was already chosen as a keeper on - // the client side. - Priority: int32(sampler.PriorityAutoKeep), - Spans: spans, - }) - } - p.TracerPayload = &pb.TracerPayload{ - Chunks: traceChunks, - ContainerID: fastHeaderGet(header, headerContainerID), - LanguageName: tagstats.Lang, - LanguageVersion: tagstats.LangVersion, - TracerVersion: tagstats.TracerVersion, - } - if ctags := getContainerTags(o.conf.ContainerTags, p.TracerPayload.ContainerID); ctags != "" { - p.TracerPayload.Tags = map[string]string{ - tagContainersTags: ctags, + if env == "" { + // no env at resource level, try the first span + if v := ddspan.Meta["env"]; v != "" { + env = v + } } + tracesByID[traceID] = append(tracesByID[traceID], ddspan) + } + } + tags := tagstats.AsTags() + metrics.Count("datadog.trace_agent.otlp.spans", int64(rspans.InstrumentationLibrarySpans().Len()), tags, 1) + metrics.Count("datadog.trace_agent.otlp.traces", int64(len(tracesByID)), tags, 1) + traceChunks := make([]*pb.TraceChunk, 0, len(tracesByID)) + p := Payload{ + Source: tagstats, + } + for _, spans := range tracesByID { + traceChunks = append(traceChunks, &pb.TraceChunk{ + // auto-keep all incoming traces; it was already chosen as a keeper on + // the client side. + Priority: int32(sampler.PriorityAutoKeep), + Spans: spans, + }) + } + if env == "" { + env = o.conf.DefaultEnv + } + if hostname == "" { + hostname = o.conf.Hostname + } + p.TracerPayload = &pb.TracerPayload{ + Hostname: hostname, + Chunks: traceChunks, + Env: traceutil.NormalizeTag(env), + ContainerID: containerID, + LanguageName: tagstats.Lang, + LanguageVersion: tagstats.LangVersion, + TracerVersion: tagstats.TracerVersion, + } + if ctags := getContainerTags(o.conf.ContainerTags, p.TracerPayload.ContainerID); ctags != "" { + p.TracerPayload.Tags = map[string]string{ + tagContainersTags: ctags, } - o.out <- &p + } + select { + case o.out <- &p: + // 👍 + default: + log.Warn("Payload in channel full. Dropped 1 payload.") + } + return OTLPIngestSummary{ + Hostname: hostname, + Tags: attributes.RunningTagsFromAttributes(attr), } } @@ -331,27 +386,18 @@ func marshalEvents(events ptrace.SpanEventSlice) string { // convertSpan converts the span in to a Datadog span, and uses the rattr resource tags and the lib instrumentation // library attributes to further augment it. -func convertSpan(rattr map[string]string, lib pcommon.InstrumentationScope, in ptrace.Span) *pb.Span { - name := spanKindName(in.Kind()) - if lib.Name() != "" { - name = lib.Name() + "." + name - } else { - name = "opentelemetry." + name - } +func (o *OTLPReceiver) convertSpan(rattr map[string]string, lib pcommon.InstrumentationScope, in ptrace.Span) *pb.Span { traceID := in.TraceID().Bytes() meta := make(map[string]string, len(rattr)) for k, v := range rattr { meta[k] = v } span := &pb.Span{ - Name: name, TraceID: traceIDToUint64(traceID), SpanID: spanIDToUint64(in.SpanID().Bytes()), ParentID: spanIDToUint64(in.ParentSpanID().Bytes()), Start: int64(in.StartTimestamp()), Duration: int64(in.EndTimestamp()) - int64(in.StartTimestamp()), - Service: rattr[string(semconv.AttributeServiceName)], - Resource: in.Name(), Meta: meta, Metrics: map[string]float64{}, } @@ -371,53 +417,114 @@ func convertSpan(rattr map[string]string, lib pcommon.InstrumentationScope, in p case pcommon.ValueTypeInt: span.Metrics[k] = float64(v.IntVal()) default: - span.Meta[k] = v.AsString() + switch k { + case "operation.name": + span.Name = v.AsString() + case "service.name": + span.Service = v.AsString() + case "resource.name": + span.Resource = v.AsString() + case "span.type": + span.Type = v.AsString() + case "analytics.event": + if v, err := strconv.ParseBool(v.AsString()); err != nil { + if v { + span.Metrics[sampler.KeySamplingRateEventExtraction] = 1 + } else { + span.Metrics[sampler.KeySamplingRateEventExtraction] = 0 + } + } + default: + span.Meta[k] = v.AsString() + } } return true }) + if ctags := attributes.ContainerTagFromAttributes(span.Meta); ctags != "" { + span.Meta[tagContainersTags] = ctags + } if _, ok := span.Meta["env"]; !ok { if env := span.Meta[string(semconv.AttributeDeploymentEnvironment)]; env != "" { - span.Meta["env"] = env + span.Meta["env"] = traceutil.NormalizeTag(env) } } if in.TraceState() != ptrace.TraceStateEmpty { - span.Meta["trace_state"] = string(in.TraceState()) + span.Meta["w3c.tracestate"] = string(in.TraceState()) } if lib.Name() != "" { - span.Meta["instrumentation_library.name"] = lib.Name() + span.Meta[semconv.OtelLibraryName] = lib.Name() } if lib.Version() != "" { - span.Meta["instrumentation_library.version"] = lib.Version() + span.Meta[semconv.OtelLibraryVersion] = lib.Version() } - if svc := span.Meta[string(semconv.AttributePeerService)]; svc != "" { - span.Service = svc + span.Meta[semconv.OtelStatusCode] = in.Status().Code().String() + if msg := in.Status().Message(); msg != "" { + span.Meta[semconv.OtelStatusDescription] = msg } - if r := resourceFromTags(span.Meta); r != "" { - span.Resource = r - } - span.Type = spanKind2Type(in.Kind(), span) status2Error(in.Status(), in.Events(), span) + if span.Name == "" { + name := in.Name() + if !o.conf.OTLPReceiver.SpanNameAsResourceName { + name = spanKindName(in.Kind()) + if lib.Name() != "" { + name = lib.Name() + "." + name + } else { + name = "opentelemetry." + name + } + } + if v, ok := o.conf.OTLPReceiver.SpanNameRemappings[name]; ok { + name = v + } + span.Name = name + } + if span.Service == "" { + if svc := span.Meta[string(semconv.AttributePeerService)]; svc != "" { + span.Service = svc + } else if svc := rattr[string(semconv.AttributeServiceName)]; svc != "" { + span.Service = svc + } else { + span.Service = "OTLPResourceNoServiceName" + } + } + if span.Resource == "" { + if r := resourceFromTags(span.Meta); r != "" { + span.Resource = r + } else { + span.Resource = in.Name() + } + } + if span.Type == "" { + span.Type = spanKind2Type(in.Kind(), span) + } return span } // resourceFromTags attempts to deduce a more accurate span resource from the given list of tags meta. // If this is not possible, it returns an empty string. func resourceFromTags(meta map[string]string) string { - var r string if m := meta[string(semconv.AttributeHTTPMethod)]; m != "" { - r = m + // use the HTTP method + route (if available) if route := meta[string(semconv.AttributeHTTPRoute)]; route != "" { - r += " " + route + return m + " " + route } else if route := meta["grpc.path"]; route != "" { - r += " " + route + return m + " " + route } + return m } else if m := meta[string(semconv.AttributeMessagingOperation)]; m != "" { - r = m + // use the messaging operation if dest := meta[string(semconv.AttributeMessagingDestination)]; dest != "" { - r += " " + dest + return m + " " + dest + } + return m + } else if m := meta[string(semconv.AttributeRPCMethod)]; m != "" { + // use the RPC method + if svc := meta[string(semconv.AttributeRPCService)]; svc != "" { + // ...and service if availabl + return m + " " + svc } + return m } - return r + return "" } // status2Error checks the given status and events and applies any potential error and messages @@ -432,21 +539,29 @@ func status2Error(status ptrace.SpanStatus, events ptrace.SpanEventSlice, span * if strings.ToLower(e.Name()) != "exception" { continue } - e.Attributes().Range(func(k string, v pcommon.Value) bool { - switch k { - case string(semconv.AttributeExceptionMessage): - span.Meta["error.msg"] = v.AsString() - case string(semconv.AttributeExceptionType): - span.Meta["error.type"] = v.AsString() - case string(semconv.AttributeExceptionStacktrace): - span.Meta["error.stack"] = v.AsString() - } - return true - }) + attrs := e.Attributes() + if v, ok := attrs.Get(semconv.AttributeExceptionMessage); ok { + span.Meta["error.msg"] = v.AsString() + } + if v, ok := attrs.Get(semconv.AttributeExceptionType); ok { + span.Meta["error.type"] = v.AsString() + } + if v, ok := attrs.Get(semconv.AttributeExceptionStacktrace); ok { + span.Meta["error.stack"] = v.AsString() + } } if _, ok := span.Meta["error.msg"]; !ok { + // no error message was extracted, find alternatives if status.Message() != "" { + // use the status message span.Meta["error.msg"] = status.Message() + } else if httpcode, ok := span.Meta["http.status_code"]; ok { + // we have status code that we can use as details + if httptext, ok := span.Meta["http.status_text"]; ok { + span.Meta["error.msg"] = fmt.Sprintf("%s %s", httpcode, httptext) + } else { + span.Meta["error.msg"] = httpcode + } } } } diff --git a/pkg/trace/api/otlp_test.go b/pkg/trace/api/otlp_test.go index 55bcb6c164303..c57d01da98e51 100644 --- a/pkg/trace/api/otlp_test.go +++ b/pkg/trace/api/otlp_test.go @@ -20,6 +20,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/trace/testutil" "github.com/stretchr/testify/assert" + semconv "go.opentelemetry.io/collector/model/semconv/v1.6.1" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" ) @@ -81,6 +82,75 @@ var otlpTestTracesRequest = testutil.NewOTLPTracesRequest([]testutil.OTLPResourc }, }) +func TestOTLPNameRemapping(t *testing.T) { + cfg := config.New() + cfg.OTLPReceiver.SpanNameRemappings = map[string]string{"libname.unspecified": "new"} + out := make(chan *Payload, 1) + rcv := NewOTLPReceiver(out, cfg) + rcv.ReceiveResourceSpans(testutil.NewOTLPTracesRequest([]testutil.OTLPResourceSpan{ + { + LibName: "libname", + LibVersion: "1.2", + Attributes: map[string]interface{}{}, + Spans: []*testutil.OTLPSpan{ + {Name: "asd"}, + }, + }, + }).Traces().ResourceSpans().At(0), http.Header{}, "") + timeout := time.After(500 * time.Millisecond) + select { + case <-timeout: + t.Fatal("timed out") + case p := <-out: + assert.Equal(t, "new", p.TracerPayload.Chunks[0].Spans[0].Name) + } +} + +func TestOTLPHostname(t *testing.T) { + for _, tt := range []struct { + config, resource, span string + out string + }{ + { + config: "config-hostname", + resource: "resource-hostname", + span: "span-hostname", + out: "resource-hostname", + }, + { + config: "config-hostname", + out: "config-hostname", + }, + { + config: "config-hostname", + span: "span-hostname", + out: "span-hostname", + }, + } { + cfg := config.New() + cfg.Hostname = tt.config + out := make(chan *Payload, 1) + rcv := NewOTLPReceiver(out, cfg) + rcv.ReceiveResourceSpans(testutil.NewOTLPTracesRequest([]testutil.OTLPResourceSpan{ + { + LibName: "a", + LibVersion: "1.2", + Attributes: map[string]interface{}{"datadog.host.name": tt.resource}, + Spans: []*testutil.OTLPSpan{ + {Attributes: map[string]interface{}{"_dd.hostname": tt.span}}, + }, + }, + }).Traces().ResourceSpans().At(0), http.Header{}, "") + timeout := time.After(500 * time.Millisecond) + select { + case <-timeout: + t.Fatal("timed out") + case p := <-out: + assert.Equal(t, tt.out, p.TracerPayload.Hostname) + } + } +} + func TestOTLPReceiver(t *testing.T) { t.Run("New", func(t *testing.T) { cfg := config.New() @@ -383,6 +453,8 @@ func TestOTLPHelpers(t *testing.T) { func TestOTLPConvertSpan(t *testing.T) { now := uint64(otlpTestSpan.StartTimestamp()) + cfg := config.New() + o := NewOTLPReceiver(nil, cfg) for i, tt := range []struct { rattr map[string]string libname string @@ -410,19 +482,21 @@ func TestOTLPConvertSpan(t *testing.T) { Duration: 200000000, Error: 1, Meta: map[string]string{ - "name": "john", - "otel.trace_id": "72df520af2bde7a5240031ead750e5f3", - "env": "staging", - "instrumentation_library.name": "ddtracer", - "instrumentation_library.version": "v2", - "service.name": "pylons", - "service.version": "v1.2.3", - "trace_state": "state", - "version": "v1.2.3", - "events": `[{"time_unix_nano":123,"name":"boom","attributes":{"key":"Out of memory","accuracy":"2.4"},"dropped_attributes_count":2},{"time_unix_nano":456,"name":"exception","attributes":{"exception.message":"Out of memory","exception.type":"mem","exception.stacktrace":"1/2/3"},"dropped_attributes_count":2}]`, - "error.msg": "Out of memory", - "error.type": "mem", - "error.stack": "1/2/3", + "name": "john", + "otel.trace_id": "72df520af2bde7a5240031ead750e5f3", + "env": "staging", + "otel.status_code": "STATUS_CODE_ERROR", + "otel.status_description": "Error", + "otel.library.name": "ddtracer", + "otel.library.version": "v2", + "service.name": "pylons", + "service.version": "v1.2.3", + "w3c.tracestate": "state", + "version": "v1.2.3", + "events": `[{"time_unix_nano":123,"name":"boom","attributes":{"key":"Out of memory","accuracy":"2.4"},"dropped_attributes_count":2},{"time_unix_nano":456,"name":"exception","attributes":{"exception.message":"Out of memory","exception.type":"mem","exception.stacktrace":"1/2/3"},"dropped_attributes_count":2}]`, + "error.msg": "Out of memory", + "error.type": "mem", + "error.stack": "1/2/3", }, Metrics: map[string]float64{ "approx": 1.2, @@ -488,22 +562,24 @@ func TestOTLPConvertSpan(t *testing.T) { Duration: 200000000, Error: 1, Meta: map[string]string{ - "name": "john", - "env": "prod", - "deployment.environment": "prod", - "instrumentation_library.name": "ddtracer", - "otel.trace_id": "72df520af2bde7a5240031ead750e5f3", - "instrumentation_library.version": "v2", - "service.version": "v1.2.3", - "trace_state": "state", - "version": "v1.2.3", - "events": "[{\"time_unix_nano\":123,\"name\":\"boom\",\"attributes\":{\"message\":\"Out of memory\",\"accuracy\":\"2.4\"},\"dropped_attributes_count\":2},{\"time_unix_nano\":456,\"name\":\"exception\",\"attributes\":{\"exception.message\":\"Out of memory\",\"exception.type\":\"mem\",\"exception.stacktrace\":\"1/2/3\"},\"dropped_attributes_count\":2}]", - "error.msg": "Out of memory", - "error.type": "mem", - "error.stack": "1/2/3", - "http.method": "GET", - "http.route": "/path", - "peer.service": "userbase", + "name": "john", + "env": "prod", + "deployment.environment": "prod", + "otel.trace_id": "72df520af2bde7a5240031ead750e5f3", + "otel.status_code": "STATUS_CODE_ERROR", + "otel.status_description": "Error", + "otel.library.name": "ddtracer", + "otel.library.version": "v2", + "service.version": "v1.2.3", + "w3c.tracestate": "state", + "version": "v1.2.3", + "events": "[{\"time_unix_nano\":123,\"name\":\"boom\",\"attributes\":{\"message\":\"Out of memory\",\"accuracy\":\"2.4\"},\"dropped_attributes_count\":2},{\"time_unix_nano\":456,\"name\":\"exception\",\"attributes\":{\"exception.message\":\"Out of memory\",\"exception.type\":\"mem\",\"exception.stacktrace\":\"1/2/3\"},\"dropped_attributes_count\":2}]", + "error.msg": "Out of memory", + "error.type": "mem", + "error.stack": "1/2/3", + "http.method": "GET", + "http.route": "/path", + "peer.service": "userbase", }, Metrics: map[string]float64{ "approx": 1.2, @@ -569,27 +645,81 @@ func TestOTLPConvertSpan(t *testing.T) { Duration: 200000000, Error: 1, Meta: map[string]string{ + "name": "john", + "env": "staging", + "otel.status_code": "STATUS_CODE_ERROR", + "otel.status_description": "Error", + "otel.library.name": "ddtracer", + "otel.library.version": "v2", + "service.name": "pylons", + "service.version": "v1.2.3", + "w3c.tracestate": "state", + "version": "v1.2.3", + "otel.trace_id": "72df520af2bde7a5240031ead750e5f3", + "events": "[{\"time_unix_nano\":123,\"name\":\"boom\",\"attributes\":{\"message\":\"Out of memory\",\"accuracy\":\"2.4\"},\"dropped_attributes_count\":2},{\"time_unix_nano\":456,\"name\":\"exception\",\"attributes\":{\"exception.message\":\"Out of memory\",\"exception.type\":\"mem\",\"exception.stacktrace\":\"1/2/3\"},\"dropped_attributes_count\":2}]", + "error.msg": "Out of memory", + "error.type": "mem", + "error.stack": "1/2/3", + "http.method": "GET", + "http.route": "/path", + }, + Metrics: map[string]float64{ + "approx": 1.2, + "count": 2, + }, + Type: "web", + }, + }, { + rattr: map[string]string{ + "env": "staging", + }, + libname: "ddtracer", + libver: "v2", + in: testutil.NewOTLPSpan(&testutil.OTLPSpan{ + Name: "/path", + Start: now, + End: now + 200000000, + Attributes: map[string]interface{}{ + "service.name": "mongo", + "operation.name": "READ", + "resource.name": "/path", + "span.type": "db", "name": "john", + semconv.AttributeContainerID: "cid", + semconv.AttributeK8SContainerName: "k8s-container", + "http.method": "GET", + "http.route": "/path", + "approx": 1.2, + "count": 2, + }, + }), + out: &pb.Span{ + Service: "mongo", + Name: "READ", + Resource: "/path", + TraceID: 2594128270069917171, + SpanID: 2594128270069917171, + ParentID: 0, + Start: int64(now), + Duration: 200000000, + Meta: map[string]string{ "env": "staging", - "instrumentation_library.name": "ddtracer", - "instrumentation_library.version": "v2", - "service.name": "pylons", - "service.version": "v1.2.3", - "trace_state": "state", - "version": "v1.2.3", - "otel.trace_id": "72df520af2bde7a5240031ead750e5f3", - "events": "[{\"time_unix_nano\":123,\"name\":\"boom\",\"attributes\":{\"message\":\"Out of memory\",\"accuracy\":\"2.4\"},\"dropped_attributes_count\":2},{\"time_unix_nano\":456,\"name\":\"exception\",\"attributes\":{\"exception.message\":\"Out of memory\",\"exception.type\":\"mem\",\"exception.stacktrace\":\"1/2/3\"},\"dropped_attributes_count\":2}]", - "error.msg": "Out of memory", - "error.type": "mem", - "error.stack": "1/2/3", + "_dd.tags.container": "container_id:cid,kube_container_name:k8s-container", + semconv.AttributeContainerID: "cid", + semconv.AttributeK8SContainerName: "k8s-container", "http.method": "GET", "http.route": "/path", + "otel.status_code": "STATUS_CODE_UNSET", + "otel.library.name": "ddtracer", + "otel.library.version": "v2", + "name": "john", + "otel.trace_id": "72df520af2bde7a5240031ead750e5f3", }, Metrics: map[string]float64{ "approx": 1.2, "count": 2, }, - Type: "web", + Type: "db", }, }, } { @@ -598,23 +728,33 @@ func TestOTLPConvertSpan(t *testing.T) { lib.SetVersion(tt.libver) assert := assert.New(t) want := tt.out - got := convertSpan(tt.rattr, lib, tt.in) + got := o.convertSpan(tt.rattr, lib, tt.in) if len(want.Meta) != len(got.Meta) { - t.Fatalf("(%d) Meta count mismatch", i) + t.Fatalf("(%d) Meta count mismatch:\n%#v", i, got.Meta) } for k, v := range want.Meta { - if k != "events" { + switch k { + case "events": + // events contain maps with no guaranteed order of + // traversal; best to unpack to compare + var gote, wante []testutil.OTLPSpanEvent + if err := json.Unmarshal([]byte(v), &wante); err != nil { + t.Fatalf("(%d) Error unmarshalling: %v", i, err) + } + if err := json.Unmarshal([]byte(got.Meta[k]), &gote); err != nil { + t.Fatalf("(%d) Error unmarshalling: %v", i, err) + } + assert.Equal(wante, gote) + case "_dd.container_tags": + // order not guaranteed, so we need to unpack and sort to compare + gott := strings.Split(got.Meta[tagContainersTags], ",") + wantt := strings.Split(want.Meta[tagContainersTags], ",") + sort.Strings(gott) + sort.Strings(wantt) + assert.Equal(wantt, gott) + default: assert.Equal(v, got.Meta[k], fmt.Sprintf("(%d) Meta %v:%v", i, k, v)) - continue - } - var gote, wante []testutil.OTLPSpanEvent - if err := json.Unmarshal([]byte(v), &wante); err != nil { - t.Fatalf("(%d) Error unmarshalling: %v", i, err) - } - if err := json.Unmarshal([]byte(got.Meta[k]), &gote); err != nil { - t.Fatalf("(%d) Error unmarshalling: %v", i, err) } - assert.Equal(wante, gote) } if len(want.Metrics) != len(got.Metrics) { t.Fatalf("(%d) Metrics count mismatch:\n\n%v\n\n%v", i, want.Metrics, got.Metrics) @@ -636,7 +776,7 @@ func TestResourceAttributesMap(t *testing.T) { rattr := map[string]string{"key": "val"} lib := pcommon.NewInstrumentationScope() span := testutil.NewOTLPSpan(&testutil.OTLPSpan{}) - convertSpan(rattr, lib, span) + NewOTLPReceiver(nil, config.New()).convertSpan(rattr, lib, span) assert.Len(t, rattr, 1) // ensure "rattr" has no new entries assert.Equal(t, "val", rattr["key"]) } diff --git a/pkg/trace/config/config.go b/pkg/trace/config/config.go index 76abb2279272b..9af60d8d891cd 100644 --- a/pkg/trace/config/config.go +++ b/pkg/trace/config/config.go @@ -49,6 +49,13 @@ type OTLP struct { // If unset (or 0), the receiver will be off. GRPCPort int `mapstructure:"grpc_port"` + // SpanNameRemappings is the map of datadog span names and preferred name to map to. This can be used to + // automatically map Datadog Span Operation Names to an updated value. All entries should be key/value pairs. + SpanNameRemappings map[string]string `mapstructure:"span_name_remappings"` + + // SpanNameAsResourceName uses the OTLP span name as the Datadog resource name. + SpanNameAsResourceName bool `mapstructure:"span_name_as_resource_name"` + // MaxRequestBytes specifies the maximum number of bytes that will be read // from an incoming HTTP request. MaxRequestBytes int64 `mapstructure:"-"` diff --git a/pkg/trace/go.mod b/pkg/trace/go.mod index bbc8536958ac4..0cf5e30dc1688 100644 --- a/pkg/trace/go.mod +++ b/pkg/trace/go.mod @@ -4,6 +4,7 @@ go 1.17 require ( github.com/DataDog/datadog-agent/pkg/obfuscate v0.36.0-rc.4 + github.com/DataDog/datadog-agent/pkg/otlp/model v0.34.0 github.com/DataDog/datadog-agent/pkg/remoteconfig/client v0.36.0-rc.4 github.com/DataDog/datadog-go/v5 v5.1.0 github.com/DataDog/sketches-go v1.2.1 @@ -12,13 +13,13 @@ require ( github.com/golang/protobuf v1.5.2 github.com/google/gofuzz v1.2.0 github.com/pkg/errors v0.9.1 - github.com/shirou/gopsutil/v3 v3.22.3 + github.com/shirou/gopsutil/v3 v3.22.2 github.com/stretchr/testify v1.7.1 github.com/tinylib/msgp v1.1.6 github.com/vmihailenco/msgpack/v4 v4.3.12 go.opentelemetry.io/collector/model v0.49.0 go.opentelemetry.io/collector/pdata v0.49.0 - golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 + golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba google.golang.org/grpc v1.45.0 k8s.io/apimachinery v0.21.5 @@ -37,8 +38,8 @@ require ( github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/secure-systems-lab/go-securesystemslib v0.3.1 // indirect github.com/theupdateframework/go-tuf v0.1.0 // indirect - github.com/tklauser/go-sysconf v0.3.10 // indirect - github.com/tklauser/numcpus v0.4.0 // indirect + github.com/tklauser/go-sysconf v0.3.9 // indirect + github.com/tklauser/numcpus v0.3.0 // indirect github.com/vmihailenco/tagparser v0.1.1 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect @@ -51,5 +52,6 @@ require ( replace ( github.com/DataDog/datadog-agent/pkg/obfuscate => ../obfuscate + github.com/DataDog/datadog-agent/pkg/otlp/model => ../otlp/model github.com/DataDog/datadog-agent/pkg/remoteconfig/client => ../remoteconfig/client ) diff --git a/pkg/trace/go.sum b/pkg/trace/go.sum index 871d8ef24033d..fa6da34f094e9 100644 --- a/pkg/trace/go.sum +++ b/pkg/trace/go.sum @@ -1,6 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/datadog-agent/pkg/quantile v0.36.0-rc.4/go.mod h1:UsCz7/ojY6EFWgJht3m4GP1HPCQNgI1fIG7kBzsTOLs= github.com/DataDog/datadog-go/v5 v5.1.0 h1:Zmq3tCk9+Tdq8Du73M71Zo6Dyx+cEo9QkCSCqQlHFaQ= github.com/DataDog/datadog-go/v5 v5.1.0/go.mod h1:KhiYb2Badlv9/rofz+OznKoEF5XKTonWyhx5K83AP8E= github.com/DataDog/sketches-go v1.2.1 h1:qTBzWLnZ3kM2kw39ymh6rMcnN+5VULwFs++lEYUUsro= @@ -13,6 +14,7 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -151,8 +153,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -163,8 +167,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/secure-systems-lab/go-securesystemslib v0.3.1 h1:LJuyMziazadwmQRRu1M7GMUo5S1oH1+YxU9FjuSFU8k= github.com/secure-systems-lab/go-securesystemslib v0.3.1/go.mod h1:o8hhjkbNl2gOamKUA/eNW3xUrntHT9L4W89W1nfj43U= -github.com/shirou/gopsutil/v3 v3.22.3 h1:UebRzEomgMpv61e3hgD1tGooqX5trFbdU/ehphbHd00= -github.com/shirou/gopsutil/v3 v3.22.3/go.mod h1:D01hZJ4pVHPpCTZ3m3T2+wDF2YAGfd+H4ifUguaQzHM= +github.com/shirou/gopsutil/v3 v3.22.2 h1:wCrArWFkHYIdDxx/FSfF5RB4dpJYW6t7rcp3+zL8uks= +github.com/shirou/gopsutil/v3 v3.22.2/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -184,10 +188,10 @@ github.com/theupdateframework/go-tuf v0.1.0 h1:ZtpteY7vwPlJd2x4LNaMPtL2EjKZ/YDf2 github.com/theupdateframework/go-tuf v0.1.0/go.mod h1:Vlrf6NdcfjEjbF24KDNIL07h8kUjzznkSJgihzWMC9Q= github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw= github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= -github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= -github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= -github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= -github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= +github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo= +github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= +github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ= +github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= @@ -202,6 +206,10 @@ go.opentelemetry.io/collector/model v0.49.0/go.mod h1:nOYQv9KoFPs6ihJwOi24qB209E go.opentelemetry.io/collector/pdata v0.49.0 h1:aYj5rOlRC0x7lGXbc185LMsMMoY/pjOTXr5s1O2SzXs= go.opentelemetry.io/collector/pdata v0.49.0/go.mod h1:YwmKuiFhNgtmhRdpi8Q8FAWPa0AwJTCSlssSsAtuRcY= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.20.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -212,6 +220,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -274,9 +283,11 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 h1:XDXtA5hveEEV8JB2l7nhMTp3t3cHp9ZpwcdjqyEWLlo= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -303,6 +314,7 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/trace/testutil/testutil.go b/pkg/trace/testutil/testutil.go index 195a55e8f4fe8..3b32913024425 100644 --- a/pkg/trace/testutil/testutil.go +++ b/pkg/trace/testutil/testutil.go @@ -16,6 +16,7 @@ package testutil import ( + "fmt" "net" "testing" ) @@ -24,11 +25,11 @@ import ( func FindTCPPort() (int, error) { addr, err := net.ResolveTCPAddr("tcp", "localhost:0") if err != nil { - return 0, err + return 0, fmt.Errorf("resolve: %v", err) } l, err := net.ListenTCP("tcp", addr) if err != nil { - return 0, err + return 0, fmt.Errorf("listen: %v", err) } defer l.Close() return l.Addr().(*net.TCPAddr).Port, nil diff --git a/releasenotes/notes/apm-otlp-standardize-attributes-7a52f9ccbf3e9152.yaml b/releasenotes/notes/apm-otlp-standardize-attributes-7a52f9ccbf3e9152.yaml new file mode 100644 index 0000000000000..143b6db8a0a09 --- /dev/null +++ b/releasenotes/notes/apm-otlp-standardize-attributes-7a52f9ccbf3e9152.yaml @@ -0,0 +1,11 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +fixes: + - | + APM OTLP: This change ensures that the ingest now standardizes certain attribute keys to their correct Datadog tag counter parts, such as: container tags, "operation.name", "service.name", etc.