From c3b8645567c8befd19da2072e1d127b5e852cfb4 Mon Sep 17 00:00:00 2001 From: Jennie Gao Date: Sun, 22 Oct 2023 17:33:12 -0400 Subject: [PATCH 1/2] tracer: align structs --- ddtrace/tracer/abandonedspans.go | 14 ++- ddtrace/tracer/log_test.go | 12 +- ddtrace/tracer/metrics_test.go | 10 +- ddtrace/tracer/option.go | 170 ++++++++++++++--------------- ddtrace/tracer/option_test.go | 2 +- ddtrace/tracer/payload.go | 12 +- ddtrace/tracer/rules_sampler.go | 25 ++--- ddtrace/tracer/sampler.go | 2 +- ddtrace/tracer/sampler_test.go | 34 +++--- ddtrace/tracer/span.go | 44 ++++---- ddtrace/tracer/span_test.go | 6 +- ddtrace/tracer/spancontext.go | 44 +++----- ddtrace/tracer/spancontext_test.go | 6 +- ddtrace/tracer/sqlcomment_test.go | 6 +- ddtrace/tracer/stats.go | 29 +++-- ddtrace/tracer/stats_payload.go | 6 +- ddtrace/tracer/textmap.go | 8 +- ddtrace/tracer/textmap_test.go | 62 ++++++----- ddtrace/tracer/tracer.go | 70 +++++------- ddtrace/tracer/tracer_test.go | 4 +- ddtrace/tracer/transport.go | 4 +- ddtrace/tracer/transport_test.go | 27 ++--- ddtrace/tracer/util_test.go | 40 +++---- ddtrace/tracer/writer.go | 17 +-- ddtrace/tracer/writer_test.go | 36 +++--- 25 files changed, 330 insertions(+), 360 deletions(-) diff --git a/ddtrace/tracer/abandonedspans.go b/ddtrace/tracer/abandonedspans.go index defad41831..3f6ec5a2c8 100644 --- a/ddtrace/tracer/abandonedspans.go +++ b/ddtrace/tracer/abandonedspans.go @@ -25,11 +25,12 @@ var ( // bucket is a not thread-safe generic implementation of a dynamic collection of elements // stored under a value-bound key (like time). Inspired by concentrator.rawBucket. type bucket[K comparable, T any] struct { - start, duration uint64 // index is a map of data's entries by aggregating value to avoid iterating data. index map[K]*list.Element // data is a list because insertion order may be important to users. - data *list.List + data *list.List + start uint64 + duration uint64 } func newBucket[K comparable, T any](btime uint64, bsize int64) *bucket[K, T] { @@ -107,18 +108,19 @@ type abandonedSpansDebugger struct { // In takes candidate spans and adds them to the debugger. In chan *abandonedSpanCandidate - // waits for any active goroutines - wg sync.WaitGroup - // stop causes the debugger to shut down when closed. stop chan struct{} + // waits for any active goroutines + wg sync.WaitGroup + // stopped reports whether the debugger is stopped (when non-zero). stopped uint32 // addedSpans and removedSpans are internal counters, mainly for testing // purposes - addedSpans, removedSpans uint32 + addedSpans uint32 + removedSpans uint32 } // newAbandonedSpansDebugger creates a new abandonedSpansDebugger debugger diff --git a/ddtrace/tracer/log_test.go b/ddtrace/tracer/log_test.go index 61eea2c475..4bbfeba38d 100644 --- a/ddtrace/tracer/log_test.go +++ b/ddtrace/tracer/log_test.go @@ -32,7 +32,7 @@ func TestStartupLog(t *testing.T) { tp.Ignore("appsec: ", telemetry.LogPrefix) logStartup(tracer) require.Len(t, tp.Logs(), 2) - assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"disabled","sampling_rules":null,"sampling_rules_error":"","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"health_metrics_enabled":false,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":((true)|(false)),"Stats":((true)|(false)),"DataStreams":((true)|(false)),"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[1]) + assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"disabled","sampling_rules":null,"sampling_rules_error":"","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"health_metrics_enabled":false,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"StatsdPort":0,"DropP0s":((true)|(false)),"Stats":((true)|(false)),"DataStreams":((true)|(false))},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[1]) }) t.Run("configured", func(t *testing.T) { @@ -64,7 +64,7 @@ func TestStartupLog(t *testing.T) { tp.Ignore("appsec: ", telemetry.LogPrefix) logStartup(tracer) require.Len(t, tp.Logs(), 2) - assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"100","sampling_rules":\[{"service":"mysql","name":"","sample_rate":0\.75,"type":"trace\(0\)"}\],"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"health_metrics_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":false,"Stats":false,"DataStreams":false,"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":true,"metadata":{"version":"v1"}}}`, tp.Logs()[1]) + assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"100","sampling_rules":\[{"service":"mysql","name":"","type":"trace\(0\)","sample_rate":0\.75}\],"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"health_metrics_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"StatsdPort":0,"DropP0s":false,"Stats":false,"DataStreams":false},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"metadata":{"version":"v1"},"enabled":true}}`, tp.Logs()[1]) }) t.Run("limit", func(t *testing.T) { @@ -96,7 +96,7 @@ func TestStartupLog(t *testing.T) { tp.Ignore("appsec: ", telemetry.LogPrefix) logStartup(tracer) require.Len(t, tp.Logs(), 2) - assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"1000.001","sampling_rules":\[{"service":"mysql","name":"","sample_rate":0\.75,"type":"trace\(0\)"}\],"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"health_metrics_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":false,"Stats":false,"DataStreams":false,"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[1]) + assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"configuredEnv","service":"configured.service","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":true,"analytics_enabled":true,"sample_rate":"0\.123000","sample_rate_limit":"1000.001","sampling_rules":\[{"service":"mysql","name":"","type":"trace\(0\)","sample_rate":0\.75}\],"sampling_rules_error":"","service_mappings":{"initial_service":"new_service"},"tags":{"runtime-id":"[^"]*","tag":"value","tag2":"NaN"},"runtime_metrics_enabled":true,"health_metrics_enabled":true,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"2.3.4","architecture":"[^"]*","global_service":"configured.service","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"StatsdPort":0,"DropP0s":false,"Stats":false,"DataStreams":false},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[1]) }) t.Run("errors", func(t *testing.T) { @@ -111,7 +111,7 @@ func TestStartupLog(t *testing.T) { tp.Ignore("appsec: ", telemetry.LogPrefix) logStartup(tracer) require.Len(t, tp.Logs(), 2) - assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"100","sampling_rules":\[{"service":"some.service","name":"","sample_rate":0\.234,"type":"trace\(0\)"}\],"sampling_rules_error":"\\n\\tat index 1: rate not provided","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"health_metrics_enabled":false,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"DropP0s":((true)|(false)),"Stats":((true)|(false)),"DataStreams":((true)|(false)),"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[1]) + assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v0.4/traces","agent_error":"Post .*","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"100","sampling_rules":\[{"service":"some.service","name":"","type":"trace\(0\)","sample_rate":0\.234}\],"sampling_rules_error":"\\n\\tat index 1: rate not provided","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"health_metrics_enabled":false,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"false","appsec":((true)|(false)),"agent_features":{"StatsdPort":0,"DropP0s":((true)|(false)),"Stats":((true)|(false)),"DataStreams":((true)|(false))},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[1]) }) t.Run("lambda", func(t *testing.T) { @@ -124,7 +124,7 @@ func TestStartupLog(t *testing.T) { tp.Ignore("appsec: ", telemetry.LogPrefix) logStartup(tracer) assert.Len(tp.Logs(), 1) - assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v0.4/traces","agent_error":"","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"disabled","sampling_rules":null,"sampling_rules_error":"","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"health_metrics_enabled":false,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"true","appsec":((true)|(false)),"agent_features":{"DropP0s":false,"Stats":false,"DataStreams":false,"StatsdPort":0},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[0]) + assert.Regexp(logPrefixRegexp+` INFO: DATADOG TRACER CONFIGURATION {"date":"[^"]*","os_name":"[^"]*","os_version":"[^"]*","version":"[^"]*","lang":"Go","lang_version":"[^"]*","env":"","service":"tracer\.test(\.exe)?","agent_url":"http://localhost:9/v0.4/traces","agent_error":"","debug":false,"analytics_enabled":false,"sample_rate":"NaN","sample_rate_limit":"disabled","sampling_rules":null,"sampling_rules_error":"","service_mappings":null,"tags":{"runtime-id":"[^"]*"},"runtime_metrics_enabled":false,"health_metrics_enabled":false,"profiler_code_hotspots_enabled":((false)|(true)),"profiler_endpoints_enabled":((false)|(true)),"dd_version":"","architecture":"[^"]*","global_service":"","lambda_mode":"true","appsec":((true)|(false)),"agent_features":{"StatsdPort":0,"DropP0s":false,"Stats":false,"DataStreams":false},"integrations":{.*},"partial_flush_enabled":false,"partial_flush_min_spans":1000,"orchestrion":{"enabled":false}}`, tp.Logs()[0]) }) t.Run("integrations", func(t *testing.T) { @@ -138,7 +138,7 @@ func TestStartupLog(t *testing.T) { require.Len(t, tp.Logs(), 2) for n, s := range tracer.config.integrations { - expect := fmt.Sprintf("\"%s\":{\"instrumented\":%t,\"available\":%t,\"available_version\":\"%s\"}", n, s.Instrumented, s.Available, s.Version) + expect := fmt.Sprintf("\"%s\":{\"available_version\":\"%s\",\"instrumented\":%t,\"available\":%t}", n, s.Version, s.Instrumented, s.Available) assert.Contains(tp.Logs()[1], expect, "expected integration %s", expect) } }) diff --git a/ddtrace/tracer/metrics_test.go b/ddtrace/tracer/metrics_test.go index 12c58c2239..07712e5af3 100644 --- a/ddtrace/tracer/metrics_test.go +++ b/ddtrace/tracer/metrics_test.go @@ -27,25 +27,25 @@ const ( ) type testStatsdClient struct { - mu sync.RWMutex + counts map[string]int64 + waitCh chan struct{} gaugeCalls []testStatsdCall incrCalls []testStatsdCall countCalls []testStatsdCall timingCalls []testStatsdCall - counts map[string]int64 tags []string - waitCh chan struct{} n int - closed bool flushed int + mu sync.RWMutex + closed bool } type testStatsdCall struct { name string + tags []string floatVal float64 intVal int64 timeVal time.Duration - tags []string rate float64 } diff --git a/ddtrace/tracer/option.go b/ddtrace/tracer/option.go index 18e8c17397..fa9ce7b307 100644 --- a/ddtrace/tracer/option.go +++ b/ddtrace/tracer/option.go @@ -110,87 +110,76 @@ var ( // config holds the tracer configuration. type config struct { - // debug, when true, writes details to logs. - debug bool - // agent holds the capabilities of the agent and determines some // of the behaviour of the tracer. agent agentFeatures - // integrations reports if the user has instrumented a Datadog integration and - // if they have a version of the library available to integrate. - integrations map[string]integrationConfig + // logger specifies the logger to use when printing errors. If not specified, the "log" package + // will be used. + logger ddtrace.Logger - // featureFlags specifies any enabled feature flags. - featureFlags map[string]struct{} + // sampler specifies the sampler that will be used for sampling traces. + sampler Sampler - // logToStdout reports whether we should log all traces to the standard - // output instead of using the agent. This is used in Lambda environments. - logToStdout bool + // transport specifies the Transport interface which will be used to send data to the agent. + transport transport - // sendRetries is the number of times a trace payload send is retried upon - // failure. - sendRetries int + // orchestrionCfg holds Orchestrion (aka auto-instrumentation) configuration. + // Only used for telemetry currently. + orchestrionCfg orchestrionConfig - // logStartup, when true, causes various startup info to be written - // when the tracer starts. - logStartup bool + // propagator propagates span context cross-process + propagator Propagator - // serviceName specifies the name of this application. - serviceName string + // statsdClient is set when a user provides a custom statsd client for tracking metrics + // associated with the runtime and the tracer. + statsdClient internal.StatsdClient - // universalVersion, reports whether span service name and config service name - // should match to set application version tag. False by default - universalVersion bool + // featureFlags specifies any enabled feature flags. + featureFlags map[string]struct{} - // version specifies the version of this application - version string + // peerServiceMappings holds a set of service mappings to dynamically rename peer.service values. + peerServiceMappings map[string]string - // env contains the environment that this application will run under. - env string + // httpClient specifies the HTTP client to be used by the agent's transport. + httpClient *http.Client - // sampler specifies the sampler that will be used for sampling traces. - sampler Sampler + // tickChan specifies a channel which will receive the time every time the tracer must flush. + // It defaults to time.Ticker; replaced in tests. + tickChan <-chan time.Time + + // integrations reports if the user has instrumented a Datadog integration and + // if they have a version of the library available to integrate. + integrations map[string]integrationConfig // agentURL is the agent URL that receives traces from the tracer. agentURL *url.URL - // serviceMappings holds a set of service mappings to dynamically rename services + // peerServiceMappings holds a set of service mappings to dynamically rename peer.service values. serviceMappings map[string]string // globalTags holds a set of tags that will be automatically applied to // all spans. globalTags map[string]interface{} - // transport specifies the Transport interface which will be used to send data to the agent. - transport transport - - // propagator propagates span context cross-process - propagator Propagator - - // httpClient specifies the HTTP client to be used by the agent's transport. - httpClient *http.Client - // hostname is automatically assigned when the DD_TRACE_REPORT_HOSTNAME is set to true, // and is added as a special tag to the root span of traces. hostname string - // logger specifies the logger to use when printing errors. If not specified, the "log" package - // will be used. - logger ddtrace.Logger + // env contains the environment that this application will run under. + env string - // runtimeMetrics specifies whether collection of runtime metrics is enabled. - runtimeMetrics bool + // version specifies the version of this application + version string + + // serviceName specifies the name of this application. + serviceName string // dogstatsdAddr specifies the address to connect for sending metrics to the // Datadog Agent. If not set, it defaults to "localhost:8125" or to the // combination of the environment variables DD_AGENT_HOST and DD_DOGSTATSD_PORT. dogstatsdAddr string - // statsdClient is set when a user provides a custom statsd client for tracking metrics - // associated with the runtime and the tracer. - statsdClient internal.StatsdClient - // spanRules contains user-defined rules to determine the sampling rate to apply // to a single span without affecting the entire trace spanRules []SamplingRule @@ -199,13 +188,24 @@ type config struct { // to the entire trace if any spans satisfy the criteria traceRules []SamplingRule - // tickChan specifies a channel which will receive the time every time the tracer must flush. - // It defaults to time.Ticker; replaced in tests. - tickChan <-chan time.Time + // spanAttributeSchemaVersion holds the selected DD_TRACE_SPAN_ATTRIBUTE_SCHEMA version. + spanAttributeSchemaVersion int - // noDebugStack disables the collection of debug stack traces globally. No traces reporting - // errors will record a stack trace when this option is set. - noDebugStack bool + // spanTimeout represents how old a span can be before it should be logged as a possible + // misconfiguration + spanTimeout time.Duration + + // partialFlushMinSpans is the number of finished spans in a single trace to trigger a + // partial flush, or 0 if partial flushing is disabled. + // Value from DD_TRACE_PARTIAL_FLUSH_MIN_SPANS, default 1000. + partialFlushMinSpans int + + // sendRetries is the number of times a trace payload send is retried upon + // failure. + sendRetries int + + // runtimeMetrics specifies whether collection of runtime metrics is enabled. + runtimeMetrics bool // profilerHotspots specifies whether profiler Code Hotspots is enabled. profilerHotspots bool @@ -219,26 +219,26 @@ type config struct { // enableHostnameDetection specifies whether the tracer should enable hostname detection. enableHostnameDetection bool - // spanAttributeSchemaVersion holds the selected DD_TRACE_SPAN_ATTRIBUTE_SCHEMA version. - spanAttributeSchemaVersion int + // noDebugStack disables the collection of debug stack traces globally. No traces reporting + // errors will record a stack trace when this option is set. + noDebugStack bool // peerServiceDefaultsEnabled indicates whether the peer.service tag calculation is enabled or not. peerServiceDefaultsEnabled bool - // peerServiceMappings holds a set of service mappings to dynamically rename peer.service values. - peerServiceMappings map[string]string + // debug, when true, writes details to logs. + debug bool // debugAbandonedSpans controls if the tracer should log when old, open spans are found debugAbandonedSpans bool - // spanTimeout represents how old a span can be before it should be logged as a possible - // misconfiguration - spanTimeout time.Duration + // universalVersion, reports whether span service name and config service name + // should match to set application version tag. False by default + universalVersion bool - // partialFlushMinSpans is the number of finished spans in a single trace to trigger a - // partial flush, or 0 if partial flushing is disabled. - // Value from DD_TRACE_PARTIAL_FLUSH_MIN_SPANS, default 1000. - partialFlushMinSpans int + // logStartup, when true, causes various startup info to be written + // when the tracer starts. + logStartup bool // partialFlushEnabled specifices whether the tracer should enable partial flushing. Value // from DD_TRACE_PARTIAL_FLUSH_ENABLED, default false. @@ -250,18 +250,18 @@ type config struct { // dataStreamsMonitoringEnabled specifies whether the tracer should enable monitoring of data streams dataStreamsMonitoringEnabled bool - // orchestrionCfg holds Orchestrion (aka auto-instrumentation) configuration. - // Only used for telemetry currently. - orchestrionCfg orchestrionConfig + // logToStdout reports whether we should log all traces to the standard + // output instead of using the agent. This is used in Lambda environments. + logToStdout bool } // orchestrionConfig contains Orchestrion configuration. type orchestrionConfig struct { - // Enabled indicates whether this tracer was instanciated via Orchestrion. - Enabled bool `json:"enabled"` - // Metadata holds Orchestrion specific metadata (e.g orchestrion version, mode (toolexec or manual) etc..) Metadata map[string]string `json:"metadata,omitempty"` + + // Enabled indicates whether this tracer was instanciated via Orchestrion. + Enabled bool `json:"enabled"` } // HasFeature reports whether feature f is enabled. @@ -544,33 +544,31 @@ func defaultDogstatsdAddr() string { } type integrationConfig struct { - Instrumented bool `json:"instrumented"` // indicates if the user has imported and used the integration - Available bool `json:"available"` // indicates if the user is using a library that can be used with DataDog integrations - Version string `json:"available_version"` // if available, indicates the version of the library the user has + Version string `json:"available_version"` + Instrumented bool `json:"instrumented"` + Available bool `json:"available"` } // agentFeatures holds information about the trace-agent's capabilities. // When running WithLambdaMode, a zero-value of this struct will be used // as features. type agentFeatures struct { + // featureFlags specifies all the feature flags reported by the trace-agent. + featureFlags map[string]struct{} + + // StatsdPort specifies the Dogstatsd port as provided by the agent. + // If it's the default, it will be 0, which means 8125. + StatsdPort int + // DropP0s reports whether it's ok for the tracer to not send any // P0 traces to the agent. DropP0s bool - // Stats reports whether the agent can receive client-computed stats on // the /v0.6/stats endpoint. Stats bool - // DataStreams reports whether the agent can receive data streams stats on // the /v0.1/pipeline_stats endpoint. DataStreams bool - - // StatsdPort specifies the Dogstatsd port as provided by the agent. - // If it's the default, it will be 0, which means 8125. - StatsdPort int - - // featureFlags specifies all the feature flags reported by the trace-agent. - featureFlags map[string]struct{} } // HasFlag reports whether the agent has set the feat feature flag. @@ -598,9 +596,9 @@ func loadAgentFeatures(logToStdout bool, agentURL *url.URL, httpClient *http.Cli defer resp.Body.Close() type infoResponse struct { Endpoints []string `json:"endpoints"` - ClientDropP0s bool `json:"client_drop_p0s"` - StatsdPort int `json:"statsd_port"` FeatureFlags []string `json:"feature_flags"` + StatsdPort int `json:"statsd_port"` + ClientDropP0s bool `json:"client_drop_p0s"` } var info infoResponse if err := json.NewDecoder(resp.Body).Decode(&info); err != nil { @@ -1226,13 +1224,13 @@ func WithHeaderTags(headerAsTags []string) StartOption { // UserMonitoringConfig is used to configure what is used to identify a user. // This configuration can be set by combining one or several UserMonitoringOption with a call to SetUser(). type UserMonitoringConfig struct { - PropagateID bool + Metadata map[string]string Email string Name string Role string SessionID string Scope string - Metadata map[string]string + PropagateID bool } // UserMonitoringOption represents a function that can be provided as a parameter to SetUser. diff --git a/ddtrace/tracer/option_test.go b/ddtrace/tracer/option_test.go index e6398b0d28..2bc6134e3f 100644 --- a/ddtrace/tracer/option_test.go +++ b/ddtrace/tracer/option_test.go @@ -911,8 +911,8 @@ func TestTagSeparators(t *testing.T) { assert := assert.New(t) for _, tag := range []struct { - in string out map[string]string + in string }{{ in: "env:test aKey:aVal bKey:bVal cKey:", out: map[string]string{ diff --git a/ddtrace/tracer/payload.go b/ddtrace/tracer/payload.go index 2d66ac54af..2cb0a5ecaf 100644 --- a/ddtrace/tracer/payload.go +++ b/ddtrace/tracer/payload.go @@ -41,22 +41,22 @@ import ( // • https://github.com/DataDog/dd-trace-go/pull/549 // • https://github.com/DataDog/dd-trace-go/pull/976 type payload struct { + // reader is used for reading the contents of buf. + reader *bytes.Reader + // header specifies the first few bytes in the msgpack stream // indicating the type of array (fixarray, array16 or array32) // and the number of items contained in the stream. header []byte + // buf holds the sequence of msgpack-encoded items. + buf bytes.Buffer + // off specifies the current read position on the header. off int // count specifies the number of items in the stream. count uint32 - - // buf holds the sequence of msgpack-encoded items. - buf bytes.Buffer - - // reader is used for reading the contents of buf. - reader *bytes.Reader } var _ io.Reader = (*payload)(nil) diff --git a/ddtrace/tracer/rules_sampler.go b/ddtrace/tracer/rules_sampler.go index cf5d200d96..7f516fa549 100644 --- a/ddtrace/tracer/rules_sampler.go +++ b/ddtrace/tracer/rules_sampler.go @@ -58,22 +58,18 @@ func (r *rulesSampler) TraceRateLimit() (float64, bool) { return r.traces.limit( type SamplingRule struct { // Service specifies the regex pattern that a span service name must match. Service *regexp.Regexp - // Name specifies the regex pattern that a span operation name must match. - Name *regexp.Regexp - + Name *regexp.Regexp + limiter *rateLimiter + exactService string + exactName string // Rate specifies the sampling rate that should be applied to spans that match // service and/or name of the rule. Rate float64 - // MaxPerSecond specifies max number of spans per second that can be sampled per the rule. // If not specified, the default is no limit. MaxPerSecond float64 - ruleType SamplingRuleType - exactService string - exactName string - limiter *rateLimiter } // match returns true when the span's details match all the expected values in the rule. @@ -199,9 +195,9 @@ func SpanNameServiceMPSRule(name, service string, rate, limit float64) SamplingR // Its value is the number of spans to sample per second. // Spans that matched the rules but exceeded the rate limit are not sampled. type traceRulesSampler struct { + limiter *rateLimiter // used to limit the volume of spans sampled rules []SamplingRule // the rules to match spans with globalRate float64 // a rate to apply when no rules match a span - limiter *rateLimiter // used to limit the volume of spans sampled } // newTraceRulesSampler configures a *traceRulesSampler instance using the given set of rules. @@ -378,14 +374,13 @@ func (rs *singleSpanRulesSampler) apply(span *span) bool { // rateLimiter is a wrapper on top of golang.org/x/time/rate which implements a rate limiter but also // returns the effective rate of allowance. type rateLimiter struct { - limiter *rate.Limiter - - mu sync.Mutex // guards below fields - prevTime time.Time // time at which prevAllowed and prevSeen were set + prevTime time.Time // time at which prevAllowed and prevSeen were set + limiter *rate.Limiter allowed float64 // number of spans allowed in the current period seen float64 // number of spans seen in the current period prevAllowed float64 // number of spans allowed in the previous period prevSeen float64 // number of spans seen in the previous period + mu sync.Mutex // guards below fields } // allowOne returns the rate limiter's decision to allow the span to be sampled, and the @@ -568,11 +563,11 @@ func unmarshalSamplingRules(b []byte, spanType SamplingRuleType) ([]SamplingRule // MarshalJSON implements the json.Marshaler interface. func (sr *SamplingRule) MarshalJSON() ([]byte, error) { s := struct { + MaxPerSecond *float64 `json:"max_per_second,omitempty"` Service string `json:"service"` Name string `json:"name"` - Rate float64 `json:"sample_rate"` Type string `json:"type"` - MaxPerSecond *float64 `json:"max_per_second,omitempty"` + Rate float64 `json:"sample_rate"` }{} if sr.exactService != "" { s.Service = sr.exactService diff --git a/ddtrace/tracer/sampler.go b/ddtrace/tracer/sampler.go index 2c95caf26f..e28996edad 100644 --- a/ddtrace/tracer/sampler.go +++ b/ddtrace/tracer/sampler.go @@ -93,9 +93,9 @@ func sampledByRate(n uint64, rate float64) bool { // prioritySampler holds a set of per-service sampling rates and applies // them to spans. type prioritySampler struct { - mu sync.RWMutex rates map[string]float64 defaultRate float64 + mu sync.RWMutex } func newPrioritySampler() *prioritySampler { diff --git a/ddtrace/tracer/sampler_test.go b/ddtrace/tracer/sampler_test.go index 62657f1f05..ece1edc26e 100644 --- a/ddtrace/tracer/sampler_test.go +++ b/ddtrace/tracer/sampler_test.go @@ -51,8 +51,8 @@ func TestPrioritySampler(t *testing.T) { type key struct{ service, env string } for _, tt := range []struct { - in string out map[key]float64 + in string }{ { in: `{}`, @@ -214,8 +214,8 @@ func TestRuleEnvVars(t *testing.T) { assert := assert.New(t) defer os.Unsetenv("DD_TRACE_RATE_LIMIT") for _, tt := range []struct { - in string out *rate.Limiter + in string }{ {in: "", out: rate.NewLimiter(100.0, 100)}, {in: "0.0", out: rate.NewLimiter(0.0, 0)}, @@ -237,8 +237,8 @@ func TestRuleEnvVars(t *testing.T) { for _, tt := range []struct { value string - ruleN int errStr string + ruleN int }{ { value: "[]", @@ -278,8 +278,8 @@ func TestRuleEnvVars(t *testing.T) { for i, tt := range []struct { value string - ruleN int errStr string + ruleN int }{ { value: "[]", @@ -931,21 +931,21 @@ func TestGlobMatch(t *testing.T) { func TestSamplingRuleMarshall(t *testing.T) { for _, tt := range []struct { - in SamplingRule out string + in SamplingRule }{ - {SamplingRule{nil, nil, 0, 0, 0, "srv", "ops", nil}, - `{"service":"srv","name":"ops","sample_rate":0,"type":"trace(0)"}`}, - {SamplingRule{regexp.MustCompile("srv.[0-9]+]"), nil, 0, 0, 0, "srv", "ops", nil}, - `{"service":"srv","name":"ops","sample_rate":0,"type":"trace(0)"}`}, - {SamplingRule{regexp.MustCompile("srv.*"), regexp.MustCompile("ops.[0-9]+]"), 0, 0, 0, "", "", nil}, - `{"service":"srv.*","name":"ops.[0-9]+]","sample_rate":0,"type":"trace(0)"}`}, - {SamplingRule{regexp.MustCompile("srv.[0-9]+]"), regexp.MustCompile("ops.[0-9]+]"), 0.55, 0, 0, "", "", nil}, - `{"service":"srv.[0-9]+]","name":"ops.[0-9]+]","sample_rate":0.55,"type":"trace(0)"}`}, - {SamplingRule{regexp.MustCompile("srv.[0-9]+]"), regexp.MustCompile("ops.[0-9]+]"), 0.55, 0, 1, "", "", nil}, - `{"service":"srv.[0-9]+]","name":"ops.[0-9]+]","sample_rate":0.55,"type":"span(1)"}`}, - {SamplingRule{regexp.MustCompile("srv.[0-9]+]"), regexp.MustCompile("ops.[0-9]+]"), 0.55, 1000, 1, "", "", nil}, - `{"service":"srv.[0-9]+]","name":"ops.[0-9]+]","sample_rate":0.55,"type":"span(1)","max_per_second":1000}`}, + {`{"service":"srv","name":"ops","type":"trace(0)","sample_rate":0}`, + SamplingRule{nil, nil, nil, "srv", "ops", 0, 0, 0}}, + {`{"service":"srv","name":"ops","type":"trace(0)","sample_rate":0}`, + SamplingRule{regexp.MustCompile("srv.[0-9]+]"), nil, nil, "srv", "ops", 0, 0, 0}}, + {`{"service":"srv.*","name":"ops.[0-9]+]","type":"trace(0)","sample_rate":0}`, + SamplingRule{regexp.MustCompile("srv.*"), regexp.MustCompile("ops.[0-9]+]"), nil, "", "", 0, 0, 0}}, + {`{"service":"srv.[0-9]+]","name":"ops.[0-9]+]","type":"trace(0)","sample_rate":0.55}`, + SamplingRule{regexp.MustCompile("srv.[0-9]+]"), regexp.MustCompile("ops.[0-9]+]"), nil, "", "", 0.55, 0, 0}}, + {`{"service":"srv.[0-9]+]","name":"ops.[0-9]+]","type":"span(1)","sample_rate":0.55}`, + SamplingRule{regexp.MustCompile("srv.[0-9]+]"), regexp.MustCompile("ops.[0-9]+]"), nil, "", "", 0.55, 0, 1}}, + {`{"max_per_second":1000,"service":"srv.[0-9]+]","name":"ops.[0-9]+]","type":"span(1)","sample_rate":0.55}`, + SamplingRule{regexp.MustCompile("srv.[0-9]+]"), regexp.MustCompile("ops.[0-9]+]"), nil, "", "", 0.55, 1000, 1}}, } { m, err := tt.in.MarshalJSON() assert.Nil(t, err) diff --git a/ddtrace/tracer/span.go b/ddtrace/tracer/span.go index c4a0530409..c6c72a0f5b 100644 --- a/ddtrace/tracer/span.go +++ b/ddtrace/tracer/span.go @@ -62,30 +62,26 @@ type errorConfig struct { // span represents a computation. Callers must call Finish when a span is // complete to ensure it's submitted. type span struct { - sync.RWMutex `msg:"-"` // all fields are protected by this RWMutex - - Name string `msg:"name"` // operation name - Service string `msg:"service"` // service name (i.e. "grpc.server", "http.request") - Resource string `msg:"resource"` // resource name (i.e. "/user?id=123", "SELECT * FROM users") - Type string `msg:"type"` // protocol associated with the span (i.e. "web", "db", "cache") - Start int64 `msg:"start"` // span start time expressed in nanoseconds since epoch - Duration int64 `msg:"duration"` // duration of the span expressed in nanoseconds - Meta map[string]string `msg:"meta,omitempty"` // arbitrary map of metadata - Metrics map[string]float64 `msg:"metrics,omitempty"` // arbitrary map of numeric metrics - SpanID uint64 `msg:"span_id"` // identifier of this span - TraceID uint64 `msg:"trace_id"` // lower 64-bits of the root span identifier - ParentID uint64 `msg:"parent_id"` // identifier of the span's direct parent - Error int32 `msg:"error"` // error status of the span; 0 means no errors - - goExecTraced bool `msg:"-"` - noDebugStack bool `msg:"-"` // disables debug stack traces - finished bool `msg:"-"` // true if the span has been submitted to a tracer. Can only be read/modified if the trace is locked. - context *spanContext `msg:"-"` // span propagation context - - pprofCtxActive context.Context `msg:"-"` // contains pprof.WithLabel labels to tell the profiler more about this span - pprofCtxRestore context.Context `msg:"-"` // contains pprof.WithLabel labels of the parent span (if any) that need to be restored when this span finishes - - taskEnd func() // ends execution tracer (runtime/trace) task, if started + pprofCtxRestore context.Context `msg:"-"` // contains pprof.WithLabel labels of the parent span (if any) that need to be restored when this span finishes + pprofCtxActive context.Context `msg:"-"` // contains pprof.WithLabel labels to tell the profiler more about this span + context *spanContext `msg:"-"` // span propagation context + taskEnd func() // ends execution tracer (runtime/trace) task, if started + Meta map[string]string `msg:"meta,omitempty"` // arbitrary map of metadata + Metrics map[string]float64 `msg:"metrics,omitempty"` // arbitrary map of numeric metrics + Name string `msg:"name"` // operation name + Service string `msg:"service"` // service name (i.e. "grpc.server", "http.request") + Resource string `msg:"resource"` // resource name (i.e. "/user?id=123", "SELECT * FROM users") + Type string `msg:"type"` // protocol associated with the span (i.e. "web", "db", "cache") + TraceID uint64 `msg:"trace_id"` // lower 64-bits of the root span identifier + ParentID uint64 `msg:"parent_id"` // identifier of the span's direct parent + SpanID uint64 `msg:"span_id"` // identifier of this span + Duration int64 `msg:"duration"` // duration of the span expressed in nanoseconds + Start int64 `msg:"start"` // span start time expressed in nanoseconds since epoch + sync.RWMutex `msg:"-"` // all fields are protected by this RWMutex + Error int32 `msg:"error"` // error status of the span; 0 means no errors + goExecTraced bool `msg:"-"` + noDebugStack bool `msg:"-"` // disables debug stack traces + finished bool `msg:"-"` // true if the span has been submitted to a tracer. Can only be read/modified if the trace is locked. } // Context yields the SpanContext for this Span. Note that the return diff --git a/ddtrace/tracer/span_test.go b/ddtrace/tracer/span_test.go index 78d8940b5a..f1d0516b73 100644 --- a/ddtrace/tracer/span_test.go +++ b/ddtrace/tracer/span_test.go @@ -115,16 +115,16 @@ func TestSpanFinishTwice(t *testing.T) { func TestShouldDrop(t *testing.T) { for _, tt := range []struct { prio int - errors int32 rate float64 + errors int32 want bool }{ {1, 0, 0, true}, {2, 1, 0, true}, {0, 1, 0, true}, {0, 0, 1, true}, - {0, 0, 0.5, true}, - {0, 0, 0.00001, false}, + {0, 0.5, 0, true}, + {0, 0.00001, 0, false}, {0, 0, 0, false}, } { t.Run("", func(t *testing.T) { diff --git a/ddtrace/tracer/spancontext.go b/ddtrace/tracer/spancontext.go index 0f71e3f225..c687e30080 100644 --- a/ddtrace/tracer/spancontext.go +++ b/ddtrace/tracer/spancontext.go @@ -83,23 +83,16 @@ func (t *traceID) UpperHex() string { // spawn a direct descendant of the span that it belongs to. It can be used // to create distributed tracing by propagating it using the provided interfaces. type spanContext struct { - updated bool // updated is tracking changes for priority / origin / x-datadog-tags - - // the below group should propagate only locally - - trace *trace // reference to the trace that this span belongs too - span *span // reference to the span that hosts this context - errors int32 // number of spans with errors in this trace - - // the below group should propagate cross-process - - traceID traceID - spanID uint64 - - mu sync.RWMutex // guards below fields + trace *trace // reference to the trace that this span belongs too + span *span // reference to the span that hosts this context baggage map[string]string - hasBaggage uint32 // atomic int for quick checking presence of baggage. 0 indicates no baggage, otherwise baggage exists. origin string // e.g. "synthetics" + spanID uint64 + mu sync.RWMutex + errors int32 // number of spans with errors in this trace + hasBaggage uint32 // atomic int for quick checking presence of baggage. 0 indicates no baggage, otherwise baggage exists. + traceID traceID + updated bool // updated is tracking changes for priority / origin / x-datadog-tags } // newSpanContext creates a new SpanContext to serve as context for the given @@ -241,20 +234,19 @@ const ( // priority, the root reference and a buffer of the spans which are part of the // trace, if these exist. type trace struct { - mu sync.RWMutex // guards below fields - spans []*span // all the spans that are part of this trace - tags map[string]string // trace level tags - propagatingTags map[string]string // trace level tags that will be propagated across service boundaries - finished int // the number of finished spans - full bool // signifies that the span buffer is full - priority *float64 // sampling priority - locked bool // specifies if the sampling priority can be altered - samplingDecision samplingDecision // samplingDecision indicates whether to send the trace to the agent. - + tags map[string]string // trace level tags + propagatingTags map[string]string // trace level tags that will be propagated across service boundaries + priority *float64 // sampling priority // root specifies the root of the trace, if known; it is nil when a span // context is extracted from a carrier, at which point there are no spans in // the trace yet. - root *span + root *span + spans []*span // all the spans that are part of this trace + finished int // the number of finished spans + mu sync.RWMutex // guards below fields + samplingDecision samplingDecision // samplingDecision indicates whether to send the trace to the agent. + full bool // signifies that the span buffer is full + locked bool // specifies if the sampling priority can be altered } var ( diff --git a/ddtrace/tracer/spancontext_test.go b/ddtrace/tracer/spancontext_test.go index 32c65c8be6..75fee5c17b 100644 --- a/ddtrace/tracer/spancontext_test.go +++ b/ddtrace/tracer/spancontext_test.go @@ -329,13 +329,13 @@ func TestSpanFinishPriority(t *testing.T) { func TestSpanPeerService(t *testing.T) { testCases := []struct { - name string - spanOpts []StartSpanOption - peerServiceDefaultsEnabled bool peerServiceMappings map[string]string + name string wantPeerService string wantPeerServiceSource string wantPeerServiceRemappedFrom string + spanOpts []StartSpanOption + peerServiceDefaultsEnabled bool }{ { name: "PeerServiceSet", diff --git a/ddtrace/tracer/sqlcomment_test.go b/ddtrace/tracer/sqlcomment_test.go index 024081b609..86e764b94c 100644 --- a/ddtrace/tracer/sqlcomment_test.go +++ b/ddtrace/tracer/sqlcomment_test.go @@ -20,14 +20,14 @@ import ( func TestSQLCommentCarrier(t *testing.T) { testCases := []struct { + expectedExtractErr error name string query string mode DBMPropagationMode - injectSpan bool - samplingPriority int expectedQuery string + samplingPriority int + injectSpan bool expectedSpanIDGen bool - expectedExtractErr error }{ { name: "default", diff --git a/ddtrace/tracer/stats.go b/ddtrace/tracer/stats.go index 720a2a0230..85bcba9177 100644 --- a/ddtrace/tracer/stats.go +++ b/ddtrace/tracer/stats.go @@ -40,26 +40,22 @@ var defaultStatsBucketSize = (10 * time.Second).Nanoseconds() // flushing them occasionally to the underlying transport located in the given // tracer config. type concentrator struct { + statsdClient internal.StatsdClient // statsd client for sending metrics. // In specifies the channel to be used for feeding data to the concentrator. // In order for In to have a consumer, the concentrator must be started using // a call to Start. In chan *aggregableSpan - - // mu guards below fields - mu sync.Mutex - // buckets maintains a set of buckets, where the map key represents // the starting point in time of that bucket, in nanoseconds. - buckets map[int64]*rawBucket - + buckets map[int64]*rawBucket + stop chan struct{} // closing this channel triggers shutdown + cfg *config // tracer startup configuration + wg sync.WaitGroup // waits for any active goroutines + bucketSize int64 // the size of a bucket in nanoseconds + // mu guards below fields + mu sync.Mutex // stopped reports whether the concentrator is stopped (when non-zero) stopped uint32 - - wg sync.WaitGroup // waits for any active goroutines - bucketSize int64 // the size of a bucket in nanoseconds - stop chan struct{} // closing this channel triggers shutdown - cfg *config // tracer startup configuration - statsdClient internal.StatsdClient // statsd client for sending metrics. } // newConcentrator creates a new concentrator using the given tracer @@ -223,8 +219,9 @@ type aggregation struct { } type rawBucket struct { - start, duration uint64 - data map[aggregation]*rawGroupedStats + data map[aggregation]*rawGroupedStats + start uint64 + duration uint64 } func newRawBucket(btime uint64, bsize int64) *rawBucket { @@ -279,12 +276,12 @@ func (sb *rawBucket) Export() statsBucket { } type rawGroupedStats struct { + okDistribution *ddsketch.DDSketch + errDistribution *ddsketch.DDSketch hits uint64 topLevelHits uint64 errors uint64 duration uint64 - okDistribution *ddsketch.DDSketch - errDistribution *ddsketch.DDSketch } func newRawGroupedStats() *rawGroupedStats { diff --git a/ddtrace/tracer/stats_payload.go b/ddtrace/tracer/stats_payload.go index 35a68b46b9..8940d02e73 100644 --- a/ddtrace/tracer/stats_payload.go +++ b/ddtrace/tracer/stats_payload.go @@ -25,14 +25,14 @@ type statsPayload struct { // statsBucket specifies a set of stats computed over a duration. type statsBucket struct { + // Stats contains a set of statistics computed for the duration of this bucket. + Stats []groupedStats + // Start specifies the beginning of this bucket. Start uint64 // Duration specifies the duration of this bucket. Duration uint64 - - // Stats contains a set of statistics computed for the duration of this bucket. - Stats []groupedStats } // groupedStats contains a set of statistics grouped under various aggregation keys. diff --git a/ddtrace/tracer/textmap.go b/ddtrace/tracer/textmap.go index bff595e154..2be61bf7a2 100644 --- a/ddtrace/tracer/textmap.go +++ b/ddtrace/tracer/textmap.go @@ -176,10 +176,10 @@ func NewPropagator(cfg *PropagatorConfig, propagators ...Propagator) Propagator injectors, injectorNames := getPropagators(cfg, injectorsPs) extractors, extractorsNames := getPropagators(cfg, extractorsPs) return &chainedPropagator{ - injectors, - extractors, injectorNames, extractorsNames, + injectors, + extractors, } } @@ -187,10 +187,10 @@ func NewPropagator(cfg *PropagatorConfig, propagators ...Propagator) Propagator // When injecting, all injectors are called to propagate the span context. // When extracting, it tries each extractor, selecting the first successful one. type chainedPropagator struct { - injectors []Propagator - extractors []Propagator injectorNames string extractorsNames string + injectors []Propagator + extractors []Propagator } // getPropagators returns a list of propagators based on ps, which is a comma seperated diff --git a/ddtrace/tracer/textmap_test.go b/ddtrace/tracer/textmap_test.go index 9b67ef1ac3..a4d8675702 100644 --- a/ddtrace/tracer/textmap_test.go +++ b/ddtrace/tracer/textmap_test.go @@ -453,9 +453,9 @@ func TestEnvVars(t *testing.T) { t.Setenv(k, v) } var tests = []struct { - tid traceID - spanID uint64 out map[string]string + spanID uint64 + tid traceID }{ { tid: traceIDFrom128Bits(9863134987902842, 1412508178991881), @@ -680,16 +680,16 @@ func TestEnvVars(t *testing.T) { t.Run("b3 single header inject", func(t *testing.T) { t.Setenv(headerPropagationStyleInject, "b3 single header") var tests = []struct { - in []uint64 // contains [, , ] out string + in []uint64 // contains [, , ] }{ { - []uint64{18368781661998368512, 17939463908140879269, 1}, "feeb0599801f4700-f8f5c76089ad8da5-1", + []uint64{18368781661998368512, 17939463908140879269, 1}, }, { - []uint64{11681107445354718197, 11667520360719770894, 0}, "a21ba1551789e3f5-a1eb5bf36e56e50e-0", + []uint64{11681107445354718197, 11667520360719770894, 0}, }, } for i, tc := range tests { @@ -724,29 +724,29 @@ func TestEnvVars(t *testing.T) { t.Setenv(k, v) } var tests = []struct { - in []uint64 // contains [, ] out map[string]string + in []uint64 // contains [, ] }{ { - []uint64{1412508178991881, 1842642739201064}, map[string]string{ b3TraceIDHeader: "000504ab30404b09", b3SpanIDHeader: "00068bdfb1eb0428", }, + []uint64{1412508178991881, 1842642739201064}, }, { - []uint64{9530669991610245, 9455715668862222}, map[string]string{ b3TraceIDHeader: "0021dc1807524785", b3SpanIDHeader: "002197ec5d8a250e", }, + []uint64{9530669991610245, 9455715668862222}, }, { - []uint64{1, 1}, map[string]string{ b3TraceIDHeader: "0000000000000001", b3SpanIDHeader: "0000000000000001", }, + []uint64{1, 1}, }, } for _, tc := range tests { @@ -857,29 +857,29 @@ func TestEnvVars(t *testing.T) { t.Setenv(k, v) } var tests = []struct { - in []uint64 // contains [, ] out map[string]string + in []uint64 // contains [, ] }{ { - []uint64{1412508178991881, 1842642739201064}, map[string]string{ b3TraceIDHeader: "000504ab30404b09", b3SpanIDHeader: "00068bdfb1eb0428", }, + []uint64{1412508178991881, 1842642739201064}, }, { - []uint64{9530669991610245, 9455715668862222}, map[string]string{ b3TraceIDHeader: "0021dc1807524785", b3SpanIDHeader: "002197ec5d8a250e", }, + []uint64{9530669991610245, 9455715668862222}, }, { - []uint64{1, 1}, map[string]string{ b3TraceIDHeader: "0000000000000001", b3SpanIDHeader: "0000000000000001", }, + []uint64{1, 1}, }, } for _, tc := range tests { @@ -927,10 +927,10 @@ func TestEnvVars(t *testing.T) { } var tests = []struct { in TextMapCarrier + propagatingTags map[string]string + origin string out []uint64 // contains [, ] tid traceID - origin string - propagatingTags map[string]string }{ { in: TextMapCarrier{ @@ -1175,11 +1175,11 @@ func TestEnvVars(t *testing.T) { var tests = []struct { inHeaders TextMapCarrier outHeaders TextMapCarrier - sid uint64 - tid traceID - priority int traceID128 string origin string + sid uint64 + priority int + tid traceID }{ { inHeaders: TextMapCarrier{ @@ -1248,12 +1248,12 @@ func TestEnvVars(t *testing.T) { t.Setenv(k, v) } var tests = []struct { - tid traceID - sid uint64 out TextMapCarrier - priority int - origin string propagatingTags map[string]string + origin string + sid uint64 + priority int + tid traceID }{ { out: TextMapCarrier{ @@ -1523,9 +1523,9 @@ func TestEnvVars(t *testing.T) { var tests = []struct { in TextMapCarrier outMap TextMapCarrier - out []uint64 // contains [, ] - priority float64 origin string + out []uint64 + priority float64 }{ { in: TextMapCarrier{ @@ -1596,10 +1596,10 @@ func TestEnvVars(t *testing.T) { var tests = []struct { in TextMapCarrier outMap TextMapCarrier + origin string out []uint64 // contains [, ] - tid traceID priority float64 - origin string + tid traceID }{ { in: TextMapCarrier{ @@ -1956,8 +1956,14 @@ func FuzzMarshalPropagatingTags(f *testing.F) { func FuzzComposeTracestate(f *testing.F) { testCases := []struct { - priority int - k1, v1, k2, v2, k3, v3, oldState string + k1 string + v1 string + k2 string + v2 string + k3 string + v3 string + oldState string + priority int }{ {priority: 1, k1: "keyOne", v1: "json", diff --git a/ddtrace/tracer/tracer.go b/ddtrace/tracer/tracer.go index 0c333d2d5c..67170d8a2f 100644 --- a/ddtrace/tracer/tracer.go +++ b/ddtrace/tracer/tracer.go @@ -42,67 +42,55 @@ var _ ddtrace.Tracer = (*tracer)(nil) // channels. It additionally holds two buffers which accumulates error and trace // queues to be processed by the payload encoder. type tracer struct { - config *config - - // stats specifies the concentrator used to compute statistics, when client-side - // stats are enabled. - stats *concentrator - // traceWriter is responsible for sending finished traces to their // destination, such as the Trace Agent or Datadog Forwarder. traceWriter traceWriter - + // statsd is used for tracking metrics associated with the runtime and the tracer. + statsd globalinternal.StatsdClient + // obfuscator holds the obfuscator used to obfuscate resources in aggregated stats. + // obfuscator may be nil if disabled. + obfuscator *obfuscate.Obfuscator // out receives chunk with spans to be added to the payload. out chan *chunk - // flush receives a channel onto which it will confirm after a flush has been // triggered and completed. flush chan chan<- struct{} - // stop causes the tracer to shut down when closed. stop chan struct{} - - // stopOnce ensures the tracer is stopped exactly once. - stopOnce sync.Once - - // wg waits for all goroutines to exit when stopping. - wg sync.WaitGroup - + // stats specifies the concentrator used to compute statistics, when client-side + // stats are enabled. + stats *concentrator // prioritySampling holds an instance of the priority sampler. prioritySampling *prioritySampler - - // pid of the process - pid int - - // These integers track metrics about spans and traces as they are started, - // finished, and dropped - spansStarted, spansFinished, tracesDropped uint32 - - // Records the number of dropped P0 traces and spans. - droppedP0Traces, droppedP0Spans uint32 - - // partialTrace the number of partially dropped traces. - partialTraces uint32 - // rulesSampling holds an instance of the rules sampler used to apply either trace sampling, // or single span sampling rules on spans. These are user-defined // rules for applying a sampling rate to spans that match the designated service // or operation name. rulesSampling *rulesSampler - - // obfuscator holds the obfuscator used to obfuscate resources in aggregated stats. - // obfuscator may be nil if disabled. - obfuscator *obfuscate.Obfuscator - - // statsd is used for tracking metrics associated with the runtime and the tracer. - statsd globalinternal.StatsdClient - - // dataStreams processes data streams monitoring information - dataStreams *datastreams.Processor - + config *config // abandonedSpansDebugger specifies where and how potentially abandoned spans are stored // when abandoned spans debugging is enabled. abandonedSpansDebugger *abandonedSpansDebugger + // dataStreams processes data streams monitoring information + dataStreams *datastreams.Processor + // wg waits for all goroutines to exit when stopping. + wg sync.WaitGroup + // pid of the process + pid int + // stopOnce ensures the tracer is stopped exactly once. + stopOnce sync.Once + // These integers track metrics about spans and traces as they are started, + // finished, and dropped + spansStarted uint32 + spansFinished uint32 + tracesDropped uint32 + + // partialTrace the number of partially dropped traces. + partialTraces uint32 + + // Records the number of dropped P0 traces and spans. + droppedP0Spans uint32 + droppedP0Traces uint32 } const ( diff --git a/ddtrace/tracer/tracer_test.go b/ddtrace/tracer/tracer_test.go index 91829844d6..10ff4d0100 100644 --- a/ddtrace/tracer/tracer_test.go +++ b/ddtrace/tracer/tracer_test.go @@ -2022,9 +2022,9 @@ func startTestTracer(t testing.TB, opts ...StartOption) (trc *tracer, transport // Mock Transport with a real Encoder type dummyTransport struct { - sync.RWMutex traces spanLists stats []*statsPayload + sync.RWMutex } func newDummyTransport() *dummyTransport { @@ -2129,9 +2129,9 @@ func cpspan(s *span) *span { } type testTraceWriter struct { - mu sync.RWMutex buf []*span flushed []*span + mu sync.RWMutex } func newTestTraceWriter() *testTraceWriter { diff --git a/ddtrace/tracer/transport.go b/ddtrace/tracer/transport.go index 7a6e80a56a..bf9c334981 100644 --- a/ddtrace/tracer/transport.go +++ b/ddtrace/tracer/transport.go @@ -74,10 +74,10 @@ type transport interface { } type httpTransport struct { - traceURL string // the delivery URL for traces - statsURL string // the delivery URL for stats client *http.Client // the HTTP client used in the POST headers map[string]string // the Transport headers + traceURL string // the delivery URL for traces + statsURL string // the delivery URL for stats } // newTransport returns a new Transport implementation that sends traces to a diff --git a/ddtrace/tracer/transport_test.go b/ddtrace/tracer/transport_test.go index 5b3d3b831d..88f0fbc46b 100644 --- a/ddtrace/tracer/transport_test.go +++ b/ddtrace/tracer/transport_test.go @@ -81,18 +81,19 @@ func TestTracesAgentIntegration(t *testing.T) { func TestResolveAgentAddr(t *testing.T) { c := new(config) for _, tt := range []struct { - inOpt StartOption - envHost, envPort string - out *url.URL + inOpt StartOption + out *url.URL + envHost string + envPort string }{ - {nil, "", "", &url.URL{Scheme: "http", Host: defaultAddress}}, - {nil, "ip.local", "", &url.URL{Scheme: "http", Host: fmt.Sprintf("ip.local:%s", defaultPort)}}, - {nil, "", "1234", &url.URL{Scheme: "http", Host: fmt.Sprintf("%s:1234", defaultHostname)}}, - {nil, "ip.local", "1234", &url.URL{Scheme: "http", Host: "ip.local:1234"}}, - {WithAgentAddr("host:1243"), "", "", &url.URL{Scheme: "http", Host: "host:1243"}}, - {WithAgentAddr("ip.other:9876"), "ip.local", "", &url.URL{Scheme: "http", Host: "ip.other:9876"}}, - {WithAgentAddr("ip.other:1234"), "", "9876", &url.URL{Scheme: "http", Host: "ip.other:1234"}}, - {WithAgentAddr("ip.other:8888"), "ip.local", "1234", &url.URL{Scheme: "http", Host: "ip.other:8888"}}, + {nil, &url.URL{Scheme: "http", Host: defaultAddress}, "", ""}, + {nil, &url.URL{Scheme: "http", Host: fmt.Sprintf("ip.local:%s", defaultPort)}, "ip.local", ""}, + {nil, &url.URL{Scheme: "http", Host: fmt.Sprintf("%s:1234", defaultHostname)}, "", "1234"}, + {nil, &url.URL{Scheme: "http", Host: "ip.local:1234"}, "ip.local", "1234"}, + {WithAgentAddr("host:1243"), &url.URL{Scheme: "http", Host: "host:1243"}, "", ""}, + {WithAgentAddr("ip.other:9876"), &url.URL{Scheme: "http", Host: "ip.other:9876"}, "ip.local", ""}, + {WithAgentAddr("ip.other:1234"), &url.URL{Scheme: "http", Host: "ip.other:1234"}, "", "9876"}, + {WithAgentAddr("ip.other:8888"), &url.URL{Scheme: "http", Host: "ip.other:8888"}, "ip.local", "1234"}, } { t.Run("", func(t *testing.T) { if tt.envHost != "" { @@ -124,9 +125,9 @@ func TestResolveAgentAddr(t *testing.T) { func TestTransportResponse(t *testing.T) { for name, tt := range map[string]struct { - status int body string err string + status int }{ "ok": { status: http.StatusOK, @@ -198,8 +199,8 @@ func TestTraceCountHeader(t *testing.T) { } type recordingRoundTripper struct { - reqs []*http.Request rt http.RoundTripper + reqs []*http.Request } // wrapRecordingRoundTripper wraps the client Transport with one that records all diff --git a/ddtrace/tracer/util_test.go b/ddtrace/tracer/util_test.go index 87e4d9ef19..e74a2fefc7 100644 --- a/ddtrace/tracer/util_test.go +++ b/ddtrace/tracer/util_test.go @@ -73,17 +73,17 @@ func TestParseUint64(t *testing.T) { func TestIsValidPropagatableTraceTag(t *testing.T) { for i, tt := range [...]struct { + err error key string value string - err error }{ - {"hello", "world", nil}, - {"hello", "world=", nil}, - {"hello=", "world", fmt.Errorf("key contains an invalid character 61")}, - {"", "world", fmt.Errorf("key length must be greater than zero")}, - {"hello", "", fmt.Errorf("value length must be greater than zero")}, - {"こんにちは", "world", fmt.Errorf("key contains an invalid character 12371")}, - {"hello", "世界", fmt.Errorf("value contains an invalid character 19990")}, + {nil, "hello", "world"}, + {nil, "hello", "world="}, + {fmt.Errorf("key contains an invalid character 61"), "hello=", "world"}, + {fmt.Errorf("key length must be greater than zero"), "", "world"}, + {fmt.Errorf("value length must be greater than zero"), "hello", ""}, + {fmt.Errorf("key contains an invalid character 12371"), "こんにちは", "world"}, + {fmt.Errorf("value contains an invalid character 19990"), "hello", "世界"}, } { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { assert.Equal(t, tt.err, isValidPropagatableTag(tt.key, tt.value)) @@ -93,20 +93,20 @@ func TestIsValidPropagatableTraceTag(t *testing.T) { func TestParsePropagatableTraceTags(t *testing.T) { for i, tt := range [...]struct { - input string - output map[string]string err error + output map[string]string + input string }{ - {"hello=world", map[string]string{"hello": "world"}, nil}, - {" hello = world ", map[string]string{" hello ": " world "}, nil}, - {"hello=world,service=account", map[string]string{"hello": "world", "service": "account"}, nil}, - {"hello=wor=ld====,service=account,tag1=val=ue1", map[string]string{"hello": "wor=ld====", "service": "account", "tag1": "val=ue1"}, nil}, - {"hello", nil, fmt.Errorf("invalid format")}, - {"hello=world,service=", nil, fmt.Errorf("invalid format")}, - {"hello=world,", nil, fmt.Errorf("invalid format")}, - {"=world", nil, fmt.Errorf("invalid format")}, - {"hello=,tag1=value1", nil, fmt.Errorf("invalid format")}, - {",hello=world", nil, fmt.Errorf("invalid format")}, + {nil, map[string]string{"hello": "world"}, "hello=world"}, + {nil, map[string]string{" hello ": " world "}, " hello = world "}, + {nil, map[string]string{"hello": "world", "service": "account"}, "hello=world,service=account"}, + {nil, map[string]string{"hello": "wor=ld====", "service": "account", "tag1": "val=ue1"}, "hello=wor=ld====,service=account,tag1=val=ue1"}, + {fmt.Errorf("invalid format"), nil, "hello"}, + {fmt.Errorf("invalid format"), nil, "hello=world,service="}, + {fmt.Errorf("invalid format"), nil, "hello=world,"}, + {fmt.Errorf("invalid format"), nil, "=world"}, + {fmt.Errorf("invalid format"), nil, "hello=,tag1=value1"}, + {fmt.Errorf("invalid format"), nil, ",hello=world"}, } { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { output, err := parsePropagatableTraceTags(tt.input) diff --git a/ddtrace/tracer/writer.go b/ddtrace/tracer/writer.go index a4665bddb5..2749c1feaa 100644 --- a/ddtrace/tracer/writer.go +++ b/ddtrace/tracer/writer.go @@ -32,24 +32,19 @@ type traceWriter interface { } type agentTraceWriter struct { + // statsd is used to send metrics + statsd globalinternal.StatsdClient // config holds the tracer configuration config *config - // payload encodes and buffers traces in msgpack format payload *payload - // climit limits the number of concurrent outgoing connections climit chan struct{} - - // wg waits for all uploads to finish - wg sync.WaitGroup - // prioritySampling is the prioritySampler into which agentTraceWriter will // read sampling rates sent by the agent prioritySampling *prioritySampler - - // statsd is used to send metrics - statsd globalinternal.StatsdClient + // wg waits for all uploads to finish + wg sync.WaitGroup } func newAgentTraceWriter(c *config, s *prioritySampler, statsdClient globalinternal.StatsdClient) *agentTraceWriter { @@ -132,11 +127,11 @@ var logWriter io.Writer = os.Stdout // (https://github.com/DataDog/datadog-serverless-functions/tree/master/aws/logs_monitoring) // and writes them to os.Stdout. This is used to send traces from an AWS Lambda environment. type logTraceWriter struct { + w io.Writer + statsd globalinternal.StatsdClient config *config buf bytes.Buffer hasTraces bool - w io.Writer - statsd globalinternal.StatsdClient } func newLogTraceWriter(c *config, statsdClient globalinternal.StatsdClient) *logTraceWriter { diff --git a/ddtrace/tracer/writer_test.go b/ddtrace/tracer/writer_test.go index dd6cc61d0a..ee93114a16 100644 --- a/ddtrace/tracer/writer_test.go +++ b/ddtrace/tracer/writer_test.go @@ -39,52 +39,52 @@ func makeSpan(n int) *span { func TestEncodeFloat(t *testing.T) { for _, tt := range []struct { - f float64 expect string + f float64 }{ { - 9.9999999999999990e20, "999999999999999900000", + 9.9999999999999990e20, }, { - 9.9999999999999999e20, "1e+21", + 9.9999999999999999e20, }, { - -9.9999999999999990e20, "-999999999999999900000", + -9.9999999999999990e20, }, { - -9.9999999999999999e20, "-1e+21", + -9.9999999999999999e20, }, { - 0.000001, "0.000001", + 0.000001, }, { - 0.0000009, "9e-7", + 0.0000009, }, { - -0.000001, "-0.000001", + -0.000001, }, { - -0.0000009, "-9e-7", + -0.0000009, }, { - math.NaN(), "null", + math.NaN(), }, { - math.Inf(-1), "null", + math.Inf(-1), }, { - math.Inf(1), "null", + math.Inf(1), }, } { t.Run(tt.expect, func(t *testing.T) { @@ -152,17 +152,17 @@ func TestLogWriter(t *testing.T) { h := newLogTraceWriter(cfg, statsd) h.w = &buf type jsonSpan struct { + Meta map[string]string `json:"meta"` + Metrics map[string]float64 `json:"metrics"` TraceID string `json:"trace_id"` SpanID string `json:"span_id"` ParentID string `json:"parent_id"` Name string `json:"name"` Resource string `json:"resource"` - Error int32 `json:"error"` - Meta map[string]string `json:"meta"` - Metrics map[string]float64 `json:"metrics"` + Service string `json:"service"` Start int64 `json:"start"` Duration int64 `json:"duration"` - Service string `json:"service"` + Error int32 `json:"error"` } type jsonPayload struct { Traces [][]jsonSpan `json:"traces"` @@ -321,12 +321,12 @@ func TestLogWriterOverflow(t *testing.T) { } type failingTransport struct { + assert *assert.Assertions + traces spanLists dummyTransport failCount int sendAttempts int tracesSent bool - traces spanLists - assert *assert.Assertions } func (t *failingTransport) send(p *payload) (io.ReadCloser, error) { From d305a5c87fe28f1092b1beb892d78ed0e185a024 Mon Sep 17 00:00:00 2001 From: Jennie Gao Date: Tue, 24 Oct 2023 15:13:16 -0400 Subject: [PATCH 2/2] enable fieldalignment in golangci --- .golangci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index 2019ec79d7..c79fb5adfc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -12,6 +12,7 @@ linters: - gci - revive - bodyclose + - govet linters-settings: gci: sections: @@ -20,3 +21,7 @@ linters-settings: - default skip-generated: true custom-order: true + govet: + disable-all: true + enable: + - fieldalignment