diff --git a/.chloggen/dinesh.gurumurthy_datadog-config-pkg.yaml b/.chloggen/dinesh.gurumurthy_datadog-config-pkg.yaml new file mode 100644 index 000000000000..711f5faf3078 --- /dev/null +++ b/.chloggen/dinesh.gurumurthy_datadog-config-pkg.yaml @@ -0,0 +1,29 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: pkg/datadog + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: | + Create a new module for Datadog exporter configuration. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [35067] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: | + This change introduces a new module for Datadog exporter configuration. The module is shared between the Datadog exporter and the Datadog Connector. + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [api] diff --git a/.chloggen/dinesh.gurumurthy_datadog_exporter_config_deprecation.yaml b/.chloggen/dinesh.gurumurthy_datadog_exporter_config_deprecation.yaml new file mode 100644 index 000000000000..3c87f50cd20d --- /dev/null +++ b/.chloggen/dinesh.gurumurthy_datadog_exporter_config_deprecation.yaml @@ -0,0 +1,29 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: deprecation + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: datadogexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: | + The datadog exporter config has been deprecated in favor of the new `datadog/config` package. The new package is shared between the Datadog exporter and the Datadog Connector. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [35067] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: | + The new `datadog/config` package is a shared module for Datadog exporter configuration. The module is shared between the Datadog exporter and the Datadog Connector. + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [api] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a16109ac225d..676015f2efe9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -147,6 +147,7 @@ internal/tools/ @open-teleme pkg/batchperresourceattr/ @open-telemetry/collector-contrib-approvers @atoulme @dmitryax pkg/batchpersignal/ @open-telemetry/collector-contrib-approvers @jpkrohling pkg/experimentalmetricmetadata/ @open-telemetry/collector-contrib-approvers @rmfitzpatrick +pkg/datadog/ @open-telemetry/collector-contrib-approvers @mx-psi @dineshg13 @liustanley @songy23 @mackjmr @ankitpatel96 pkg/golden/ @open-telemetry/collector-contrib-approvers @djaglowski @atoulme pkg/ottl/ @open-telemetry/collector-contrib-approvers @TylerHelmuth @kentquirk @bogdandrutu @evan-bradley pkg/pdatatest/ @open-telemetry/collector-contrib-approvers @djaglowski @fatsheep9146 diff --git a/cmd/otelcontribcol/builder-config.yaml b/cmd/otelcontribcol/builder-config.yaml index 7fe15b6af354..9be035a583d3 100644 --- a/cmd/otelcontribcol/builder-config.yaml +++ b/cmd/otelcontribcol/builder-config.yaml @@ -490,6 +490,7 @@ replaces: - github.com/open-telemetry/opentelemetry-collector-contrib/confmap/provider/s3provider => ../../confmap/provider/s3provider - github.com/open-telemetry/opentelemetry-collector-contrib/confmap/provider/secretsmanagerprovider => ../../confmap/provider/secretsmanagerprovider - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling => ../../pkg/sampling + - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog => ../../pkg/datadog - github.com/open-telemetry/opentelemetry-collector-contrib/internal/pdatautil => ../../internal/pdatautil - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter => ../../exporter/otelarrowexporter - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver => ../../receiver/otelarrowreceiver @@ -497,4 +498,3 @@ replaces: - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/rabbitmqexporter => ../../exporter/rabbitmqexporter - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/githubreceiver => ../../receiver/githubreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/internal/grpcutil => ../../internal/grpcutil - diff --git a/cmd/otelcontribcol/go.mod b/cmd/otelcontribcol/go.mod index 6988b402d2a6..4e96982d1077 100644 --- a/cmd/otelcontribcol/go.mod +++ b/cmd/otelcontribcol/go.mod @@ -656,6 +656,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/internal/sqlquery v0.109.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchperresourceattr v0.109.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.109.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog v0.0.0-00010101000000-000000000000 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata v0.109.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl v0.109.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.109.0 // indirect @@ -1364,6 +1365,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/confmap/provid replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling => ../../pkg/sampling +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog => ../../pkg/datadog + replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/pdatautil => ../../internal/pdatautil replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter => ../../exporter/otelarrowexporter diff --git a/connector/datadogconnector/config.go b/connector/datadogconnector/config.go index 57a8b6182530..f0c2abd3b549 100644 --- a/connector/datadogconnector/config.go +++ b/connector/datadogconnector/config.go @@ -4,11 +4,9 @@ package datadogconnector // import "github.com/open-telemetry/opentelemetry-collector-contrib/connector/datadogconnector" import ( - "fmt" - "regexp" - "time" - "go.opentelemetry.io/collector/component" + + datadogconfig "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" ) var _ component.Config = (*Config)(nil) @@ -19,100 +17,11 @@ type Config struct { Traces TracesConfig `mapstructure:"traces"` } +// Deprecated: [v0.110.0] Use `datadog.TracesConnectorConfig` instead. // TracesConfig defines the traces specific configuration options -type TracesConfig struct { - // ignored resources - // A blocklist of regular expressions can be provided to disable certain traces based on their resource name - // all entries must be surrounded by double quotes and separated by commas. - // ignore_resources: ["(GET|POST) /healthcheck"] - IgnoreResources []string `mapstructure:"ignore_resources"` - - // 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. - // 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 - SpanNameRemappings map[string]string `mapstructure:"span_name_remappings"` - - // 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. - // The default value is `false`. - SpanNameAsResourceName bool `mapstructure:"span_name_as_resource_name"` - - // If set to true, enables an additional stats computation check on spans to see they have an eligible `span.kind` (server, consumer, client, producer). - // If enabled, a span with an eligible `span.kind` will have stats computed. If disabled, only top-level and measured spans will have stats computed. - // NOTE: For stats computed from OTel traces, only top-level spans are considered when this option is off. - // If you are sending OTel traces and want stats on non-top-level spans, this flag will need to be enabled. - // If you are sending OTel traces and do not want stats computed by span kind, you need to disable this flag and disable `compute_top_level_by_span_kind`. - ComputeStatsBySpanKind bool `mapstructure:"compute_stats_by_span_kind"` - - // If set to true, root spans and spans with a server or consumer `span.kind` will be marked as top-level. - // Additionally, spans with a client or producer `span.kind` will have stats computed. - // Enabling this config option may increase the number of spans that generate trace metrics, and may change which spans appear as top-level in Datadog. - // ComputeTopLevelBySpanKind needs to be enabled in both the Datadog connector and Datadog exporter configs if both components are being used. - // The default value is `false`. - ComputeTopLevelBySpanKind bool `mapstructure:"compute_top_level_by_span_kind"` - - // If set to true, enables aggregation of peer related tags (e.g., `peer.service`, `db.instance`, etc.) in the datadog connector. - // If disabled, aggregated trace stats will not include these tags as dimensions on trace metrics. - // For the best experience with peer tags, Datadog also recommends enabling `compute_stats_by_span_kind`. - // If you are using an OTel tracer, it's best to have both enabled because client/producer spans with relevant peer tags - // may not be marked by the datadog connector as top-level spans. - // If enabling both causes the datadog connector to consume too many resources, try disabling `compute_stats_by_span_kind` first. - // A high cardinality of peer tags or APM resources can also contribute to higher CPU and memory consumption. - // You can check for the cardinality of these fields by making trace search queries in the Datadog UI. - // The default list of peer tags can be found in https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/stats/concentrator.go. - PeerTagsAggregation bool `mapstructure:"peer_tags_aggregation"` - - // [BETA] Optional list of supplementary peer tags that go beyond the defaults. The Datadog backend validates all tags - // and will drop ones that are unapproved. The default set of peer tags can be found at - // https://github.com/DataDog/datadog-agent/blob/505170c4ac8c3cbff1a61cf5f84b28d835c91058/pkg/trace/stats/concentrator.go#L55. - PeerTags []string `mapstructure:"peer_tags"` +type TracesConfig = datadogconfig.TracesConnectorConfig - // TraceBuffer specifies the number of Datadog Agent TracerPayloads to buffer before dropping. - // The default value is 1000. - TraceBuffer int `mapstructure:"trace_buffer"` - - // ResourceAttributesAsContainerTags specifies the list of resource attributes to be used as container tags. - ResourceAttributesAsContainerTags []string `mapstructure:"resource_attributes_as_container_tags"` - - // BucketInterval specifies the time interval size of aggregation buckets that aggregate the Datadog trace metrics. - // It is also the time interval that Datadog trace metrics payloads are flushed to the pipeline. - // If you are concerned about the metric volume generated by the Datadog connector and the resulting networking egress, try increasing bucket_interval. - // Default is 10s if unset. - BucketInterval time.Duration `mapstructure:"bucket_interval"` -} - -// Validate the configuration for errors. This is required by component.Config. +// Validate checks if the configuration is valid func (c *Config) Validate() error { - if c.Traces.IgnoreResources != nil { - for _, entry := range c.Traces.IgnoreResources { - _, err := regexp.Compile(entry) - if err != nil { - return fmt.Errorf("%q is not valid resource filter regular expression", entry) - } - } - } - - if c.Traces.SpanNameRemappings != nil { - for key, value := range c.Traces.SpanNameRemappings { - if value == "" { - return fmt.Errorf("%q is not valid value for span name remapping", value) - } - if key == "" { - return fmt.Errorf("%q is not valid key for span name remapping", key) - } - } - } - - if c.Traces.TraceBuffer < 0 { - return fmt.Errorf("Trace buffer must be non-negative") - } - - if c.Traces.BucketInterval < 0 { - return fmt.Errorf("bucket interval must be non-negative") - } - - return nil + return c.Traces.Validate() } diff --git a/connector/datadogconnector/config_test.go b/connector/datadogconnector/config_test.go index 55bf3481eb8d..092eee911fdd 100644 --- a/connector/datadogconnector/config_test.go +++ b/connector/datadogconnector/config_test.go @@ -8,10 +8,11 @@ import ( "time" "github.com/stretchr/testify/assert" + + datadogconfig "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" ) func TestValidate(t *testing.T) { - tests := []struct { name string cfg *Config @@ -21,36 +22,46 @@ func TestValidate(t *testing.T) { name: "span name remapping valid", cfg: &Config{ Traces: TracesConfig{ - SpanNameRemappings: map[string]string{"old.opentelemetryspan.name": "updated.name"}, + TracesConfig: datadogconfig.TracesConfig{ + SpanNameRemappings: map[string]string{"old.opentelemetryspan.name": "updated.name"}, + }, }, }, }, { name: "span name remapping empty val", cfg: &Config{Traces: TracesConfig{ - SpanNameRemappings: map[string]string{"oldname": ""}, + TracesConfig: datadogconfig.TracesConfig{ + SpanNameRemappings: map[string]string{"oldname": ""}, + }, }}, - err: "\"\" is not valid value for span name remapping", + err: "'' is not valid value for span name remapping", }, { name: "span name remapping empty key", cfg: &Config{Traces: TracesConfig{ - SpanNameRemappings: map[string]string{"": "newname"}, + TracesConfig: datadogconfig.TracesConfig{ + SpanNameRemappings: map[string]string{"": "newname"}, + }, }}, - err: "\"\" is not valid key for span name remapping", + err: "'' is not valid key for span name remapping", }, { name: "ignore resources valid", cfg: &Config{Traces: TracesConfig{ - IgnoreResources: []string{"[123]"}, + TracesConfig: datadogconfig.TracesConfig{ + IgnoreResources: []string{"[123]"}, + }, }}, }, { name: "ignore resources missing bracket", cfg: &Config{Traces: TracesConfig{ - IgnoreResources: []string{"[123"}, + TracesConfig: datadogconfig.TracesConfig{ + IgnoreResources: []string{"[123"}, + }, }}, - err: "\"[123\" is not valid resource filter regular expression", + err: "'[123' is not valid resource filter regular expression", }, { name: "With trace_buffer", @@ -63,12 +74,16 @@ func TestValidate(t *testing.T) { cfg: &Config{Traces: TracesConfig{ TraceBuffer: -10, }}, - err: "Trace buffer must be non-negative", + err: "trace buffer must be non-negative", }, { name: "With peer_tags", cfg: &Config{ - Traces: TracesConfig{PeerTags: []string{"tag1", "tag2"}}, + Traces: TracesConfig{ + TracesConfig: datadogconfig.TracesConfig{ + PeerTags: []string{"tag1", "tag2"}, + }, + }, }, }, { diff --git a/connector/datadogconnector/factory.go b/connector/datadogconnector/factory.go index c3d3b18ab126..f5f4b95a7ff2 100644 --- a/connector/datadogconnector/factory.go +++ b/connector/datadogconnector/factory.go @@ -18,6 +18,7 @@ import ( "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/connector/datadogconnector/internal/metadata" + datadogconfig "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" ) const nativeIngestFeatureGateName = "connector.datadogconnector.NativeIngest" @@ -43,9 +44,12 @@ func NewFactory() connector.Factory { func createDefaultConfig() component.Config { return &Config{ Traces: TracesConfig{ - IgnoreResources: []string{}, - TraceBuffer: 1000, - BucketInterval: 10 * time.Second, + TracesConfig: datadogconfig.TracesConfig{ + IgnoreResources: []string{}, + }, + + TraceBuffer: 1000, + BucketInterval: 10 * time.Second, }, } } diff --git a/connector/datadogconnector/factory_test.go b/connector/datadogconnector/factory_test.go index ed081d6d1d6d..f92056baa295 100644 --- a/connector/datadogconnector/factory_test.go +++ b/connector/datadogconnector/factory_test.go @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/component/componenttest" + + datadogconfig "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" ) func TestCreateDefaultConfig(t *testing.T) { @@ -18,9 +20,11 @@ func TestCreateDefaultConfig(t *testing.T) { assert.Equal(t, &Config{ Traces: TracesConfig{ - IgnoreResources: []string{}, - TraceBuffer: 1000, - BucketInterval: 10 * time.Second, + TracesConfig: datadogconfig.TracesConfig{ + IgnoreResources: []string{}, + }, + TraceBuffer: 1000, + BucketInterval: 10 * time.Second, }, }, cfg, "failed to create default config") diff --git a/connector/datadogconnector/go.mod b/connector/datadogconnector/go.mod index f7bcd889ae3f..f37527937920 100644 --- a/connector/datadogconnector/go.mod +++ b/connector/datadogconnector/go.mod @@ -12,6 +12,7 @@ require ( github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.20.0 github.com/google/go-cmp v0.6.0 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter v0.109.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog v0.0.0-00010101000000-000000000000 github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor v0.109.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/stretchr/testify v1.9.0 @@ -363,3 +364,5 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling = replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/pdatautil => ../../internal/pdatautil replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata => ../../pkg/experimentalmetricmetadata + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog => ../../pkg/datadog diff --git a/exporter/datadogexporter/config.go b/exporter/datadogexporter/config.go index 251c4fc84559..9409f86428da 100644 --- a/exporter/datadogexporter/config.go +++ b/exporter/datadogexporter/config.go @@ -5,674 +5,158 @@ package datadogexporter // import "github.com/open-telemetry/opentelemetry-colle import ( "encoding" - "errors" - "fmt" - "regexp" - "strings" - "time" - "github.com/DataDog/datadog-agent/pkg/util/hostname/validate" "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/confighttp" - "go.opentelemetry.io/collector/config/confignet" - "go.opentelemetry.io/collector/config/configopaque" - "go.opentelemetry.io/collector/config/configretry" "go.opentelemetry.io/collector/confmap" - "go.opentelemetry.io/collector/exporter/exporterhelper" - "go.uber.org/zap" -) -var ( - errUnsetAPIKey = errors.New("api.key is not set") - errNoMetadata = errors.New("only_metadata can't be enabled when host_metadata::enabled = false or host_metadata::hostname_source != first_resource") - errEmptyEndpoint = errors.New("endpoint cannot be empty") + datadogconfig "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" ) const ( + // Deprecated: [v0.110.0] Use `datadogconfig.TypeStr` instead. // DefaultSite is the default site of the Datadog intake to send data to - DefaultSite = "datadoghq.com" + DefaultSite = datadogconfig.DefaultSite ) +// Deprecated: [v0.110.0] Use `datadogconfig.TypeStr` instead. // APIConfig defines the API configuration options -type APIConfig struct { - // Key is the Datadog API key to associate your Agent's data with your organization. - // Create a new API key here: https://app.datadoghq.com/account/settings - Key configopaque.String `mapstructure:"key"` - - // Site is the site of the Datadog intake to send data to. - // The default value is "datadoghq.com". - Site string `mapstructure:"site"` - - // FailOnInvalidKey states whether to exit at startup on invalid API key. - // The default value is false. - FailOnInvalidKey bool `mapstructure:"fail_on_invalid_key"` -} +type APIConfig = datadogconfig.APIConfig +// Deprecated: [v0.110.0] Use `datadogconfig.MetricsConfig` instead. // MetricsConfig defines the metrics exporter specific configuration options -type MetricsConfig struct { - // DeltaTTL defines the time that previous points of a cumulative monotonic - // metric are kept in memory to calculate deltas - DeltaTTL int64 `mapstructure:"delta_ttl"` - - // TCPAddr.Endpoint is the host of the Datadog intake server to send metrics to. - // If unset, the value is obtained from the Site. - confignet.TCPAddrConfig `mapstructure:",squash"` - - ExporterConfig MetricsExporterConfig `mapstructure:",squash"` +type MetricsConfig = datadogconfig.MetricsConfig - // HistConfig defines the export of OTLP Histograms. - HistConfig HistogramConfig `mapstructure:"histograms"` - - // SumConfig defines the export of OTLP Sums. - SumConfig SumConfig `mapstructure:"sums"` - - // SummaryConfig defines the export for OTLP Summaries. - SummaryConfig SummaryConfig `mapstructure:"summaries"` -} - -type HistogramMode string +// Deprecated: [v0.110.0] Use `datadogconfig.HistogramMode` instead. +// HistogramMode is the export mode for OTLP Histogram metrics. +type HistogramMode = datadogconfig.HistogramMode const ( // HistogramModeNoBuckets reports no bucket histogram metrics. .sum and .count metrics will still be sent // if `send_count_sum_metrics` is enabled. - HistogramModeNoBuckets HistogramMode = "nobuckets" + HistogramModeNoBuckets HistogramMode = datadogconfig.HistogramModeNoBuckets // HistogramModeCounters reports histograms as Datadog counts, one metric per bucket. - HistogramModeCounters HistogramMode = "counters" + HistogramModeCounters HistogramMode = datadogconfig.HistogramModeCounters // HistogramModeDistributions reports histograms as Datadog distributions (recommended). - HistogramModeDistributions HistogramMode = "distributions" + HistogramModeDistributions HistogramMode = datadogconfig.HistogramModeDistributions ) var _ encoding.TextUnmarshaler = (*HistogramMode)(nil) -func (hm *HistogramMode) UnmarshalText(in []byte) error { - switch mode := HistogramMode(in); mode { - case HistogramModeCounters, HistogramModeDistributions, HistogramModeNoBuckets: - *hm = mode - return nil - default: - return fmt.Errorf("invalid histogram mode %q", mode) - } -} - +// Deprecated: [v0.110.0] Use `datadogconfig.HistogramConfig` instead. // HistogramConfig customizes export of OTLP Histograms. -type HistogramConfig struct { - // Mode for exporting histograms. Valid values are 'distributions', 'counters' or 'nobuckets'. - // - 'distributions' sends histograms as Datadog distributions (recommended). - // - 'counters' sends histograms as Datadog counts, one metric per bucket. - // - 'nobuckets' sends no bucket histogram metrics. Aggregation metrics will still be sent - // if `send_aggregation_metrics` is enabled. - // - // The current default is 'distributions'. - Mode HistogramMode `mapstructure:"mode"` - - // SendCountSum states if the export should send .sum and .count metrics for histograms. - // The default is false. - // Deprecated: [v0.75.0] Use `send_aggregation_metrics` (HistogramConfig.SendAggregations) instead. - SendCountSum bool `mapstructure:"send_count_sum_metrics"` - - // SendAggregations states if the exporter should send .sum, .count, .min and .max metrics for histograms. - // The default is false. - SendAggregations bool `mapstructure:"send_aggregation_metrics"` -} - -func (c *HistogramConfig) validate() error { - if c.Mode == HistogramModeNoBuckets && !c.SendAggregations { - return fmt.Errorf("'nobuckets' mode and `send_aggregation_metrics` set to false will send no histogram metrics") - } - return nil -} +type HistogramConfig = datadogconfig.HistogramConfig +// Deprecated: [v0.110.0] Use `datadogconfig.CumulativeMonotonicMode` instead. // CumulativeMonotonicSumMode is the export mode for OTLP Sum metrics. -type CumulativeMonotonicSumMode string +type CumulativeMonotonicSumMode = datadogconfig.CumulativeMonotonicSumMode const ( + // Deprecated: [v0.110.0] Use `datadogconfig.CumulativeMonotonicSumMode` instead. // CumulativeMonotonicSumModeToDelta calculates delta for // cumulative monotonic sum metrics in the client side and reports // them as Datadog counts. - CumulativeMonotonicSumModeToDelta CumulativeMonotonicSumMode = "to_delta" + CumulativeMonotonicSumModeToDelta CumulativeMonotonicSumMode = datadogconfig.CumulativeMonotonicSumModeToDelta + // Deprecated: [v0.110.0] Use `datadogconfig.CumulativeMonotonicSumMode` instead. // CumulativeMonotonicSumModeRawValue reports the raw value for // cumulative monotonic sum metrics as a Datadog gauge. - CumulativeMonotonicSumModeRawValue CumulativeMonotonicSumMode = "raw_value" + CumulativeMonotonicSumModeRawValue CumulativeMonotonicSumMode = datadogconfig.CumulativeMonotonicSumModeRawValue ) var _ encoding.TextUnmarshaler = (*CumulativeMonotonicSumMode)(nil) -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (sm *CumulativeMonotonicSumMode) UnmarshalText(in []byte) error { - switch mode := CumulativeMonotonicSumMode(in); mode { - case CumulativeMonotonicSumModeToDelta, - CumulativeMonotonicSumModeRawValue: - *sm = mode - return nil - default: - return fmt.Errorf("invalid cumulative monotonic sum mode %q", mode) - } -} - +// Deprecated: [v0.110.0] Use `datadogconfig.InitialValueMode` instead. // InitialValueMode defines what the exporter should do with the initial value // of a time series when transforming from cumulative to delta. -type InitialValueMode string +type InitialValueMode = datadogconfig.InitialValueMode const ( // InitialValueModeAuto reports the initial value if its start timestamp // is set and it happens after the process was started. - InitialValueModeAuto InitialValueMode = "auto" + InitialValueModeAuto InitialValueMode = datadogconfig.InitialValueModeAuto // InitialValueModeDrop always drops the initial value. - InitialValueModeDrop InitialValueMode = "drop" + InitialValueModeDrop InitialValueMode = datadogconfig.InitialValueModeDrop // InitialValueModeKeep always reports the initial value. - InitialValueModeKeep InitialValueMode = "keep" + InitialValueModeKeep InitialValueMode = datadogconfig.InitialValueModeKeep ) var _ encoding.TextUnmarshaler = (*InitialValueMode)(nil) -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (iv *InitialValueMode) UnmarshalText(in []byte) error { - switch mode := InitialValueMode(in); mode { - case InitialValueModeAuto, - InitialValueModeDrop, - InitialValueModeKeep: - *iv = mode - return nil - default: - return fmt.Errorf("invalid initial value mode %q", mode) - } -} - +// Deprecated: [v0.110.0] Use `datadogconfig.SumConfig` instead. // SumConfig customizes export of OTLP Sums. -type SumConfig struct { - // CumulativeMonotonicMode is the mode for exporting OTLP Cumulative Monotonic Sums. - // Valid values are 'to_delta' or 'raw_value'. - // - 'to_delta' calculates delta for cumulative monotonic sums and sends it as a Datadog count. - // - 'raw_value' sends the raw value of cumulative monotonic sums as Datadog gauges. - // - // The default is 'to_delta'. - // See https://docs.datadoghq.com/metrics/otlp/?tab=sum#mapping for details and examples. - CumulativeMonotonicMode CumulativeMonotonicSumMode `mapstructure:"cumulative_monotonic_mode"` - - // InitialCumulativeMonotonicMode defines the behavior of the exporter when receiving the first value - // of a cumulative monotonic sum. - InitialCumulativeMonotonicMode InitialValueMode `mapstructure:"initial_cumulative_monotonic_value"` -} +type SumConfig = datadogconfig.SumConfig +// Deprecated: [v0.110.0] Use `datadogconfig.SummaryMode` instead. // SummaryMode is the export mode for OTLP Summary metrics. -type SummaryMode string +type SummaryMode = datadogconfig.SummaryMode const ( // SummaryModeNoQuantiles sends no `.quantile` metrics. `.sum` and `.count` metrics will still be sent. - SummaryModeNoQuantiles SummaryMode = "noquantiles" + SummaryModeNoQuantiles SummaryMode = datadogconfig.SummaryModeNoQuantiles // SummaryModeGauges sends `.quantile` metrics as gauges tagged by the quantile. - SummaryModeGauges SummaryMode = "gauges" + SummaryModeGauges SummaryMode = datadogconfig.SummaryModeGauges ) var _ encoding.TextUnmarshaler = (*SummaryMode)(nil) -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (sm *SummaryMode) UnmarshalText(in []byte) error { - switch mode := SummaryMode(in); mode { - case SummaryModeNoQuantiles, - SummaryModeGauges: - *sm = mode - return nil - default: - return fmt.Errorf("invalid summary mode %q", mode) - } -} - +// Deprecated: [v0.110.0] Use `datadogconfig.SummaryMode` instead. // SummaryConfig customizes export of OTLP Summaries. -type SummaryConfig struct { - // Mode is the the mode for exporting OTLP Summaries. - // Valid values are 'noquantiles' or 'gauges'. - // - 'noquantiles' sends no `.quantile` metrics. `.sum` and `.count` metrics will still be sent. - // - 'gauges' sends `.quantile` metrics as gauges tagged by the quantile. - // - // The default is 'gauges'. - // See https://docs.datadoghq.com/metrics/otlp/?tab=summary#mapping for details and examples. - Mode SummaryMode `mapstructure:"mode"` -} +type SummaryConfig = datadogconfig.SummaryConfig +// Deprecated: [v0.110.0] Use `datadogconfig.MetricsExporterConfig` instead. // MetricsExporterConfig provides options for a user to customize the behavior of the // metrics exporter -type MetricsExporterConfig struct { - // ResourceAttributesAsTags, if set to true, will use the exporterhelper feature to transform all - // resource attributes into metric labels, which are then converted into tags - ResourceAttributesAsTags bool `mapstructure:"resource_attributes_as_tags"` - - // InstrumentationScopeMetadataAsTags, if set to true, adds the name and version of the - // instrumentation scope that created a metric to the metric tags - InstrumentationScopeMetadataAsTags bool `mapstructure:"instrumentation_scope_metadata_as_tags"` -} +type MetricsExporterConfig = datadogconfig.MetricsExporterConfig +// Deprecated: [v0.110.0] Use `datadogconfig.TracesExporterConfig` instead. // TracesConfig defines the traces exporter specific configuration options -type TracesConfig struct { - // TCPAddr.Endpoint is the host of the Datadog intake server to send traces to. - // If unset, the value is obtained from the Site. - confignet.TCPAddrConfig `mapstructure:",squash"` - - // ignored resources - // A blacklist of regular expressions can be provided to disable certain traces based on their resource name - // all entries must be surrounded by double quotes and separated by commas. - // ignore_resources: ["(GET|POST) /healthcheck"] - IgnoreResources []string `mapstructure:"ignore_resources"` - - // 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. - // 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 - SpanNameRemappings map[string]string `mapstructure:"span_name_remappings"` - - // 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. - // The default value is `false`. - SpanNameAsResourceName bool `mapstructure:"span_name_as_resource_name"` - - // If set to true, enables an additional stats computation check on spans to see they have an eligible `span.kind` (server, consumer, client, producer). - // If enabled, a span with an eligible `span.kind` will have stats computed. If disabled, only top-level and measured spans will have stats computed. - // NOTE: For stats computed from OTel traces, only top-level spans are considered when this option is off. - // If you are sending OTel traces and want stats on non-top-level spans, this flag will need to be enabled. - // If you are sending OTel traces and do not want stats computed by span kind, you need to disable this flag and disable `compute_top_level_by_span_kind`. - ComputeStatsBySpanKind bool `mapstructure:"compute_stats_by_span_kind"` - - // If set to true, root spans and spans with a server or consumer `span.kind` will be marked as top-level. - // Additionally, spans with a client or producer `span.kind` will have stats computed. - // Enabling this config option may increase the number of spans that generate trace metrics, and may change which spans appear as top-level in Datadog. - // ComputeTopLevelBySpanKind needs to be enabled in both the Datadog connector and Datadog exporter configs if both components are being used. - // The default value is `false`. - ComputeTopLevelBySpanKind bool `mapstructure:"compute_top_level_by_span_kind"` - - // If set to true, enables `peer.service` aggregation in the exporter. If disabled, aggregated trace stats will not include `peer.service` as a dimension. - // For the best experience with `peer.service`, it is recommended to also enable `compute_stats_by_span_kind`. - // If enabling both causes the datadog exporter to consume too many resources, try disabling `compute_stats_by_span_kind` first. - // If the overhead remains high, it will be due to a high cardinality of `peer.service` values from the traces. You may need to check your instrumentation. - // Deprecated: Please use PeerTagsAggregation instead - PeerServiceAggregation bool `mapstructure:"peer_service_aggregation"` - - // If set to true, enables aggregation of peer related tags (e.g., `peer.service`, `db.instance`, etc.) in the datadog exporter. - // If disabled, aggregated trace stats will not include these tags as dimensions on trace metrics. - // For the best experience with peer tags, Datadog also recommends enabling `compute_stats_by_span_kind`. - // If you are using an OTel tracer, it's best to have both enabled because client/producer spans with relevant peer tags - // may not be marked by the datadog exporter as top-level spans. - // If enabling both causes the datadog exporter to consume too many resources, try disabling `compute_stats_by_span_kind` first. - // A high cardinality of peer tags or APM resources can also contribute to higher CPU and memory consumption. - // You can check for the cardinality of these fields by making trace search queries in the Datadog UI. - // The default list of peer tags can be found in https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/stats/concentrator.go. - PeerTagsAggregation bool `mapstructure:"peer_tags_aggregation"` - - // [BETA] Optional list of supplementary peer tags that go beyond the defaults. The Datadog backend validates all tags - // and will drop ones that are unapproved. The default set of peer tags can be found at - // https://github.com/DataDog/datadog-agent/blob/505170c4ac8c3cbff1a61cf5f84b28d835c91058/pkg/trace/stats/concentrator.go#L55. - PeerTags []string `mapstructure:"peer_tags"` - - // TraceBuffer specifies the number of Datadog Agent TracerPayloads to buffer before dropping. - // The default value is 0, meaning the Datadog Agent TracerPayloads are unbuffered. - TraceBuffer int `mapstructure:"trace_buffer"` - - // flushInterval defines the interval in seconds at which the writer flushes traces - // to the intake; used in tests. - flushInterval float64 -} +type TracesConfig = datadogconfig.TracesExporterConfig +// Deprecated: [v0.110.0] Use `datadogconfig.LogsConfig` instead. // LogsConfig defines logs exporter specific configuration -type LogsConfig struct { - // TCPAddr.Endpoint is the host of the Datadog intake server to send logs to. - // If unset, the value is obtained from the Site. - confignet.TCPAddrConfig `mapstructure:",squash"` - - // DumpPayloads report whether payloads should be dumped when logging level is debug. - // Note: this config option does not apply when the `exporter.datadogexporter.UseLogsAgentExporter` feature flag is enabled (now enabled by default). - // Deprecated: This config option is not supported in the Datadog Agent logs pipeline. - DumpPayloads bool `mapstructure:"dump_payloads"` - - // UseCompression enables the logs agent to compress logs before sending them. - // Note: this config option does not apply when the `exporter.datadogexporter.UseLogsAgentExporter` feature flag is disabled. - UseCompression bool `mapstructure:"use_compression"` - - // CompressionLevel accepts values from 0 (no compression) to 9 (maximum compression but higher resource usage). - // Only takes effect if UseCompression is set to true. - // Note: this config option does not apply when the `exporter.datadogexporter.UseLogsAgentExporter` feature flag is disabled. - CompressionLevel int `mapstructure:"compression_level"` - - // BatchWait represents the maximum time the logs agent waits to fill each batch of logs before sending. - // Note: this config option does not apply when the `exporter.datadogexporter.UseLogsAgentExporter` feature flag is disabled. - BatchWait int `mapstructure:"batch_wait"` -} +type LogsConfig = datadogconfig.LogsConfig +// Deprecated: [v0.110.0] Use `datadogconfig.TagsConfig` instead. // TagsConfig defines the tag-related configuration // It is embedded in the configuration -type TagsConfig struct { - // Hostname is the fallback hostname used for payloads without hostname-identifying attributes. - // This option will NOT change the hostname applied to your metrics, traces and logs if they already have hostname-identifying attributes. - // If unset, the hostname will be determined automatically. See https://docs.datadoghq.com/opentelemetry/schema_semantics/hostname/?tab=datadogexporter#fallback-hostname-logic for details. - // - // Prefer using the `datadog.host.name` resource attribute over using this setting. - // See https://docs.datadoghq.com/opentelemetry/schema_semantics/hostname/?tab=datadogexporter#general-hostname-semantic-conventions for details. - Hostname string `mapstructure:"hostname"` -} +type TagsConfig = datadogconfig.TagsConfig +// Deprecated: [v0.110.0] Use `datadogconfig.HostnameSource` instead. // HostnameSource is the source for the hostname of host metadata. -type HostnameSource string +type HostnameSource = datadogconfig.HostnameSource const ( + // Deprecated: [v0.110.0] Use `datadogconfig.HostnameSource` instead. // HostnameSourceFirstResource picks the host metadata hostname from the resource // attributes on the first OTLP payload that gets to the exporter. If it is lacking any // hostname-like attributes, it will fallback to 'config_or_system' behavior (see below). // // Do not use this hostname source if receiving data from multiple hosts. - HostnameSourceFirstResource HostnameSource = "first_resource" + HostnameSourceFirstResource HostnameSource = datadogconfig.HostnameSourceFirstResource + // Deprecated: [v0.110.0] Use `datadogconfig.HostnameSource` instead. // HostnameSourceConfigOrSystem picks the host metadata hostname from the 'hostname' setting, // and if this is empty, from available system APIs and cloud provider endpoints. - HostnameSourceConfigOrSystem HostnameSource = "config_or_system" + HostnameSourceConfigOrSystem HostnameSource = datadogconfig.HostnameSourceConfigOrSystem ) var _ encoding.TextUnmarshaler = (*HostnameSource)(nil) -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (sm *HostnameSource) UnmarshalText(in []byte) error { - switch mode := HostnameSource(in); mode { - case HostnameSourceFirstResource, - HostnameSourceConfigOrSystem: - *sm = mode - return nil - default: - return fmt.Errorf("invalid host metadata hostname source %q", mode) - } -} - +// Deprecated: [v0.110.0] Use `datadogconfig.HostMetadataConfig` instead. // HostMetadataConfig defines the host metadata related configuration. // Host metadata is the information used for populating the infrastructure list, // the host map and providing host tags functionality. // // The exporter will send host metadata for a single host, whose name is chosen // according to `host_metadata::hostname_source`. -type HostMetadataConfig struct { - // Enabled enables the host metadata functionality. - Enabled bool `mapstructure:"enabled"` - - // HostnameSource is the source for the hostname of host metadata. - // This hostname is used for identifying the infrastructure list, host map and host tag information related to the host where the Datadog exporter is running. - // Changing this setting will not change the host used to tag your metrics, traces and logs in any way. - // For remote hosts, see https://docs.datadoghq.com/opentelemetry/schema_semantics/host_metadata/. - // - // Valid values are 'first_resource' and 'config_or_system': - // - 'first_resource' picks the host metadata hostname from the resource - // attributes on the first OTLP payload that gets to the exporter. - // If the first payload lacks hostname-like attributes, it will fallback to 'config_or_system'. - // **Do not use this hostname source if receiving data from multiple hosts**. - // - 'config_or_system' picks the host metadata hostname from the 'hostname' setting, - // If this is empty it will use available system APIs and cloud provider endpoints. - // - // The default is 'config_or_system'. - HostnameSource HostnameSource `mapstructure:"hostname_source"` - - // Tags is a list of host tags. - // These tags will be attached to telemetry signals that have the host metadata hostname. - // To attach tags to telemetry signals regardless of the host, use a processor instead. - Tags []string `mapstructure:"tags"` - - // sourceTimeout is the timeout to fetch from each provider - for example AWS IMDS. - // If unset, or set to zero duration, there will be no timeout applied. - // Default is no timeout. - sourceTimeout time.Duration -} +type HostMetadataConfig = datadogconfig.HostMetadataConfig +// Deprecated: [v0.110.0] Use `datadogconfig.Config` instead. // Config defines configuration for the Datadog exporter. -type Config struct { - confighttp.ClientConfig `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct. - exporterhelper.QueueSettings `mapstructure:"sending_queue"` - configretry.BackOffConfig `mapstructure:"retry_on_failure"` - - TagsConfig `mapstructure:",squash"` - - // API defines the Datadog API configuration. - API APIConfig `mapstructure:"api"` - - // Metrics defines the Metrics exporter specific configuration - Metrics MetricsConfig `mapstructure:"metrics"` - - // Traces defines the Traces exporter specific configuration - Traces TracesConfig `mapstructure:"traces"` - - // Logs defines the Logs exporter specific configuration - Logs LogsConfig `mapstructure:"logs"` - - // HostMetadata defines the host metadata specific configuration - HostMetadata HostMetadataConfig `mapstructure:"host_metadata"` - - // OnlyMetadata defines whether to only send metadata - // This is useful for agent-collector setups, so that - // metadata about a host is sent to the backend even - // when telemetry data is reported via a different host. - // - // This flag is incompatible with disabling host metadata, - // `use_resource_metadata`, or `host_metadata::hostname_source != first_resource` - OnlyMetadata bool `mapstructure:"only_metadata"` - - // Non-fatal warnings found during configuration loading. - warnings []error -} - -// logWarnings logs warning messages that were generated on unmarshaling. -func (c *Config) logWarnings(logger *zap.Logger) { - for _, err := range c.warnings { - logger.Warn(fmt.Sprintf("%v", err)) - } -} +type Config = datadogconfig.Config var _ component.Config = (*Config)(nil) -// Validate the configuration for errors. This is required by component.Config. -func (c *Config) Validate() error { - if err := validateClientConfig(c.ClientConfig); err != nil { - return err - } - - if c.OnlyMetadata && (!c.HostMetadata.Enabled || c.HostMetadata.HostnameSource != HostnameSourceFirstResource) { - return errNoMetadata - } - - if err := validate.ValidHostname(c.Hostname); c.Hostname != "" && err != nil { - return fmt.Errorf("hostname field is invalid: %w", err) - } - - if c.API.Key == "" { - return errUnsetAPIKey - } - - if c.Traces.IgnoreResources != nil { - for _, entry := range c.Traces.IgnoreResources { - _, err := regexp.Compile(entry) - if err != nil { - return fmt.Errorf("'%s' is not valid resource filter regular expression", entry) - } - } - } - - if c.Traces.SpanNameRemappings != nil { - for key, value := range c.Traces.SpanNameRemappings { - if value == "" { - return fmt.Errorf("'%s' is not valid value for span name remapping", value) - } - if key == "" { - return fmt.Errorf("'%s' is not valid key for span name remapping", key) - } - } - } - - err := c.Metrics.HistConfig.validate() - if err != nil { - return err - } - - return nil -} - -func validateClientConfig(cfg confighttp.ClientConfig) error { - var unsupported []string - if cfg.Auth != nil { - unsupported = append(unsupported, "auth") - } - if cfg.Endpoint != "" { - unsupported = append(unsupported, "endpoint") - } - if cfg.Compression != "" { - unsupported = append(unsupported, "compression") - } - if cfg.Headers != nil { - unsupported = append(unsupported, "headers") - } - if cfg.HTTP2ReadIdleTimeout != 0 { - unsupported = append(unsupported, "http2_read_idle_timeout") - } - if cfg.HTTP2PingTimeout != 0 { - unsupported = append(unsupported, "http2_ping_timeout") - } - - if len(unsupported) > 0 { - return fmt.Errorf("these confighttp client configs are currently not respected by Datadog exporter: %s", strings.Join(unsupported, ", ")) - } - return nil -} - -var _ error = (*renameError)(nil) - -// renameError is an error related to a renamed setting. -type renameError struct { - // oldName of the configuration option. - oldName string - // newName of the configuration option. - newName string - // issueNumber on opentelemetry-collector-contrib for tracking - issueNumber uint -} - -// List of settings that have been removed, but for which we keep a custom error. -var removedSettings = []renameError{ - { - oldName: "metrics::send_monotonic_counter", - newName: "metrics::sums::cumulative_monotonic_mode", - issueNumber: 8489, - }, - { - oldName: "tags", - newName: "host_metadata::tags", - issueNumber: 9099, - }, - { - oldName: "send_metadata", - newName: "host_metadata::enabled", - issueNumber: 9099, - }, - { - oldName: "use_resource_metadata", - newName: "host_metadata::hostname_source", - issueNumber: 9099, - }, - { - oldName: "metrics::report_quantiles", - newName: "metrics::summaries::mode", - issueNumber: 8845, - }, - { - oldName: "metrics::instrumentation_library_metadata_as_tags", - newName: "metrics::instrumentation_scope_as_tags", - issueNumber: 11135, - }, -} - -// Error implements the error interface. -func (e renameError) Error() string { - return fmt.Sprintf( - "%q was removed in favor of %q. See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/%d", - e.oldName, - e.newName, - e.issueNumber, - ) -} - -func handleRemovedSettings(configMap *confmap.Conf) error { - var errs []error - for _, removedErr := range removedSettings { - if configMap.IsSet(removedErr.oldName) { - errs = append(errs, removedErr) - } - } - - return errors.Join(errs...) -} - var _ confmap.Unmarshaler = (*Config)(nil) - -// Unmarshal a configuration map into the configuration struct. -func (c *Config) Unmarshal(configMap *confmap.Conf) error { - if err := handleRemovedSettings(configMap); err != nil { - return err - } - - err := configMap.Unmarshal(c) - if err != nil { - return err - } - - // Add deprecation warnings for deprecated settings. - renamingWarnings, err := handleRenamedSettings(configMap, c) - if err != nil { - return err - } - c.warnings = append(c.warnings, renamingWarnings...) - - c.API.Key = configopaque.String(strings.TrimSpace(string(c.API.Key))) - - // If an endpoint is not explicitly set, override it based on the site. - if !configMap.IsSet("metrics::endpoint") { - c.Metrics.TCPAddrConfig.Endpoint = fmt.Sprintf("https://api.%s", c.API.Site) - } - if !configMap.IsSet("traces::endpoint") { - c.Traces.TCPAddrConfig.Endpoint = fmt.Sprintf("https://trace.agent.%s", c.API.Site) - } - if !configMap.IsSet("logs::endpoint") { - c.Logs.TCPAddrConfig.Endpoint = fmt.Sprintf("https://http-intake.logs.%s", c.API.Site) - } - - // Return an error if an endpoint is explicitly set to "" - if c.Metrics.TCPAddrConfig.Endpoint == "" || c.Traces.TCPAddrConfig.Endpoint == "" || c.Logs.TCPAddrConfig.Endpoint == "" { - return errEmptyEndpoint - } - - const ( - initialValueSetting = "metrics::sums::initial_cumulative_monotonic_value" - cumulMonoMode = "metrics::sums::cumulative_monotonic_mode" - ) - if configMap.IsSet(initialValueSetting) && c.Metrics.SumConfig.CumulativeMonotonicMode != CumulativeMonotonicSumModeToDelta { - return fmt.Errorf("%q can only be configured when %q is set to %q", - initialValueSetting, cumulMonoMode, CumulativeMonotonicSumModeToDelta) - } - - logsExporterSettings := []struct { - setting string - valid bool - }{ - {setting: "logs::dump_payloads", valid: !isLogsAgentExporterEnabled()}, - {setting: "logs::use_compression", valid: isLogsAgentExporterEnabled()}, - {setting: "logs::compression_level", valid: isLogsAgentExporterEnabled()}, - {setting: "logs::batch_wait", valid: isLogsAgentExporterEnabled()}, - } - for _, logsExporterSetting := range logsExporterSettings { - if configMap.IsSet(logsExporterSetting.setting) && !logsExporterSetting.valid { - enabledText := "enabled" - if !isLogsAgentExporterEnabled() { - enabledText = "disabled" - } - return fmt.Errorf("%v is not valid when the exporter.datadogexporter.UseLogsAgentExporter feature gate is %v", logsExporterSetting.setting, enabledText) - } - if logsExporterSetting.setting == "logs::dump_payloads" && logsExporterSetting.valid && configMap.IsSet(logsExporterSetting.setting) { - c.warnings = append(c.warnings, fmt.Errorf("%v is deprecated and will raise an error if set when the Datadog Agent logs pipeline is enabled by default in collector version v0.108.0", logsExporterSetting.setting)) - } - } - - return nil -} diff --git a/exporter/datadogexporter/config_test.go b/exporter/datadogexporter/config_test.go index 3d02e6f402f8..d9dd1f42f9e1 100644 --- a/exporter/datadogexporter/config_test.go +++ b/exporter/datadogexporter/config_test.go @@ -8,171 +8,10 @@ import ( "time" "github.com/stretchr/testify/assert" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configauth" - "go.opentelemetry.io/collector/config/confighttp" - "go.opentelemetry.io/collector/config/configopaque" - "go.opentelemetry.io/collector/config/configtls" "go.opentelemetry.io/collector/confmap" -) - -func TestValidate(t *testing.T) { - idleConnTimeout := 30 * time.Second - maxIdleConn := 300 - maxIdleConnPerHost := 150 - maxConnPerHost := 250 - ty, err := component.NewType("ty") - assert.NoError(t, err) - auth := configauth.Authentication{AuthenticatorID: component.NewID(ty)} - - tests := []struct { - name string - cfg *Config - err string - }{ - { - name: "no api::key", - cfg: &Config{}, - err: errUnsetAPIKey.Error(), - }, - { - name: "invalid hostname", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - TagsConfig: TagsConfig{Hostname: "invalid_host"}, - }, - err: "hostname field is invalid: invalid_host is not RFC1123 compliant", - }, - { - name: "no metadata", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - OnlyMetadata: true, - HostMetadata: HostMetadataConfig{Enabled: false}, - }, - err: errNoMetadata.Error(), - }, - { - name: "span name remapping valid", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - Traces: TracesConfig{SpanNameRemappings: map[string]string{"old.opentelemetryspan.name": "updated.name"}}, - }, - }, - { - name: "span name remapping empty val", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - Traces: TracesConfig{SpanNameRemappings: map[string]string{"oldname": ""}}, - }, - err: "'' is not valid value for span name remapping", - }, - { - name: "span name remapping empty key", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - Traces: TracesConfig{SpanNameRemappings: map[string]string{"": "newname"}}, - }, - err: "'' is not valid key for span name remapping", - }, - { - name: "ignore resources valid", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - Traces: TracesConfig{IgnoreResources: []string{"[123]"}}, - }, - }, - { - name: "ignore resources missing bracket", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - Traces: TracesConfig{IgnoreResources: []string{"[123"}}, - }, - err: "'[123' is not valid resource filter regular expression", - }, - { - name: "invalid histogram settings", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - Metrics: MetricsConfig{ - HistConfig: HistogramConfig{ - Mode: HistogramModeNoBuckets, - SendAggregations: false, - }, - }, - }, - err: "'nobuckets' mode and `send_aggregation_metrics` set to false will send no histogram metrics", - }, - { - name: "TLS settings are valid", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - ClientConfig: confighttp.ClientConfig{ - TLSSetting: configtls.ClientConfig{ - InsecureSkipVerify: true, - }, - }, - }, - }, - { - name: "With trace_buffer", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - Traces: TracesConfig{TraceBuffer: 10}, - }, - }, - { - name: "With peer_tags", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - Traces: TracesConfig{PeerTags: []string{"tag1", "tag2"}}, - }, - }, - { - name: "With confighttp client configs", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - ClientConfig: confighttp.ClientConfig{ - ReadBufferSize: 100, - WriteBufferSize: 200, - Timeout: 10 * time.Second, - IdleConnTimeout: &idleConnTimeout, - MaxIdleConns: &maxIdleConn, - MaxIdleConnsPerHost: &maxIdleConnPerHost, - MaxConnsPerHost: &maxConnPerHost, - DisableKeepAlives: true, - TLSSetting: configtls.ClientConfig{InsecureSkipVerify: true}, - }, - }, - }, - { - name: "unsupported confighttp client configs", - cfg: &Config{ - API: APIConfig{Key: "notnull"}, - ClientConfig: confighttp.ClientConfig{ - Endpoint: "endpoint", - Compression: "gzip", - Auth: &auth, - Headers: map[string]configopaque.String{"key": "val"}, - HTTP2ReadIdleTimeout: 250, - HTTP2PingTimeout: 200, - }, - }, - err: "these confighttp client configs are currently not respected by Datadog exporter: auth, endpoint, compression, headers, http2_read_idle_timeout, http2_ping_timeout", - }, - } - for _, testInstance := range tests { - t.Run(testInstance.name, func(t *testing.T) { - err := testInstance.cfg.Validate() - if testInstance.err != "" { - assert.EqualError(t, err, testInstance.err) - } else { - assert.NoError(t, err) - } - }) - } -} + datadogconfig "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" +) func TestUnmarshal(t *testing.T) { cfgWithHTTPConfigs := NewFactory().CreateDefaultConfig().(*Config) @@ -189,7 +28,6 @@ func TestUnmarshal(t *testing.T) { cfgWithHTTPConfigs.IdleConnTimeout = &idleConnTimeout cfgWithHTTPConfigs.DisableKeepAlives = true cfgWithHTTPConfigs.TLSSetting.InsecureSkipVerify = true - cfgWithHTTPConfigs.warnings = nil tests := []struct { name string @@ -287,7 +125,7 @@ func TestUnmarshal(t *testing.T) { "endpoint": "", }, }), - err: errEmptyEndpoint.Error(), + err: datadogconfig.ErrEmptyEndpoint.Error(), }, { name: "Empty trace endpoint", @@ -296,7 +134,7 @@ func TestUnmarshal(t *testing.T) { "endpoint": "", }, }), - err: errEmptyEndpoint.Error(), + err: datadogconfig.ErrEmptyEndpoint.Error(), }, { name: "Empty log endpoint", @@ -305,7 +143,7 @@ func TestUnmarshal(t *testing.T) { "endpoint": "", }, }), - err: errEmptyEndpoint.Error(), + err: datadogconfig.ErrEmptyEndpoint.Error(), }, { name: "invalid initial cumulative monotonic value mode", diff --git a/exporter/datadogexporter/factory.go b/exporter/datadogexporter/factory.go index dd6acd25ce99..588ef9f8908e 100644 --- a/exporter/datadogexporter/factory.go +++ b/exporter/datadogexporter/factory.go @@ -22,7 +22,6 @@ import ( "github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes/source" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/confighttp" - "go.opentelemetry.io/collector/config/confignet" "go.opentelemetry.io/collector/config/configretry" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/exporter" @@ -37,6 +36,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/hostmetadata" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter/internal/metadata" + datadogconfig "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry" ) @@ -186,58 +186,7 @@ func defaultClientConfig() confighttp.ClientConfig { // createDefaultConfig creates the default exporter configuration func (f *factory) createDefaultConfig() component.Config { - return &Config{ - ClientConfig: defaultClientConfig(), - BackOffConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - - API: APIConfig{ - Site: "datadoghq.com", - }, - - Metrics: MetricsConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://api.datadoghq.com", - }, - DeltaTTL: 3600, - ExporterConfig: MetricsExporterConfig{ - ResourceAttributesAsTags: false, - InstrumentationScopeMetadataAsTags: false, - }, - HistConfig: HistogramConfig{ - Mode: "distributions", - SendAggregations: false, - }, - SumConfig: SumConfig{ - CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, - InitialCumulativeMonotonicMode: InitialValueModeAuto, - }, - SummaryConfig: SummaryConfig{ - Mode: SummaryModeGauges, - }, - }, - - Traces: TracesConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://trace.agent.datadoghq.com", - }, - IgnoreResources: []string{}, - }, - - Logs: LogsConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://http-intake.logs.datadoghq.com", - }, - UseCompression: true, - CompressionLevel: 6, - BatchWait: 5, - }, - - HostMetadata: HostMetadataConfig{ - Enabled: true, - HostnameSource: HostnameSourceConfigOrSystem, - }, - } + return datadogconfig.CreateDefaultConfig() } // checkAndCastConfig checks the configuration type and its warnings, and casts it to @@ -247,7 +196,7 @@ func checkAndCastConfig(c component.Config, logger *zap.Logger) *Config { if !ok { panic("programming error: config structure is not of type *datadogexporter.Config") } - cfg.logWarnings(logger) + cfg.LogWarnings(logger) return cfg } @@ -289,7 +238,7 @@ func (f *factory) createMetricsExporter( c component.Config, ) (exporter.Metrics, error) { cfg := checkAndCastConfig(c, set.TelemetrySettings.Logger) - hostProvider, err := f.SourceProvider(set.TelemetrySettings, cfg.Hostname, cfg.HostMetadata.sourceTimeout) + hostProvider, err := f.SourceProvider(set.TelemetrySettings, cfg.Hostname, cfg.HostMetadata.GetSourceTimeout()) if err != nil { return nil, fmt.Errorf("failed to build hostname provider: %w", err) } @@ -409,7 +358,7 @@ func (f *factory) createTracesExporter( wg sync.WaitGroup // waits for agent to exit ) - hostProvider, err := f.SourceProvider(set.TelemetrySettings, cfg.Hostname, cfg.HostMetadata.sourceTimeout) + hostProvider, err := f.SourceProvider(set.TelemetrySettings, cfg.Hostname, cfg.HostMetadata.GetSourceTimeout()) if err != nil { return nil, fmt.Errorf("failed to build hostname provider: %w", err) } @@ -494,9 +443,22 @@ func (f *factory) createLogsExporter( ) (exporter.Logs, error) { cfg := checkAndCastConfig(c, set.TelemetrySettings.Logger) + if cfg.Logs.DumpPayloads && isLogsAgentExporterEnabled() { + set.Logger.Warn("logs::dump_payloads is not valid when the exporter.datadogexporter.UseLogsAgentExporter feature gate is enabled") + } + if cfg.Logs.UseCompression && !isLogsAgentExporterEnabled() { + set.Logger.Warn("logs::use_compression is not valid when the exporter.datadogexporter.UseLogsAgentExporter feature gate is disabled") + } + if cfg.Logs.CompressionLevel != 0 && !isLogsAgentExporterEnabled() { + set.Logger.Warn("logs::compression_level is not valid when the exporter.datadogexporter.UseLogsAgentExporter feature gate is disabled") + } + if cfg.Logs.BatchWait != 0 && !isLogsAgentExporterEnabled() { + set.Logger.Warn("logs::batch_wait is not valid when the exporter.datadogexporter.UseLogsAgentExporter feature gate is disabled") + } + var pusher consumer.ConsumeLogsFunc var logsAgent logsagentpipeline.LogsAgent - hostProvider, err := f.SourceProvider(set.TelemetrySettings, cfg.Hostname, cfg.HostMetadata.sourceTimeout) + hostProvider, err := f.SourceProvider(set.TelemetrySettings, cfg.Hostname, cfg.HostMetadata.GetSourceTimeout()) if err != nil { return nil, fmt.Errorf("failed to build hostname provider: %w", err) } diff --git a/exporter/datadogexporter/factory_test.go b/exporter/datadogexporter/factory_test.go index 29da32051e91..2dd29e64b104 100644 --- a/exporter/datadogexporter/factory_test.go +++ b/exporter/datadogexporter/factory_test.go @@ -16,7 +16,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config/confignet" "go.opentelemetry.io/collector/config/configretry" "go.opentelemetry.io/collector/confmap/confmaptest" @@ -66,350 +65,6 @@ func (p *testPusher) Payloads() []payload.HostMetadata { return p.payloads } -// Test that the factory creates the default configuration -func TestCreateDefaultConfig(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - - assert.Equal(t, &Config{ - ClientConfig: defaultClientConfig(), - BackOffConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - - API: APIConfig{ - Site: "datadoghq.com", - }, - - Metrics: MetricsConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://api.datadoghq.com", - }, - DeltaTTL: 3600, - HistConfig: HistogramConfig{ - Mode: "distributions", - SendAggregations: false, - }, - SumConfig: SumConfig{ - CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, - InitialCumulativeMonotonicMode: InitialValueModeAuto, - }, - SummaryConfig: SummaryConfig{ - Mode: SummaryModeGauges, - }, - }, - - Traces: TracesConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://trace.agent.datadoghq.com", - }, - IgnoreResources: []string{}, - }, - Logs: LogsConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://http-intake.logs.datadoghq.com", - }, - UseCompression: true, - CompressionLevel: 6, - BatchWait: 5, - }, - - HostMetadata: HostMetadataConfig{ - Enabled: true, - HostnameSource: HostnameSourceConfigOrSystem, - }, - OnlyMetadata: false, - }, cfg, "failed to create default config") - - assert.NoError(t, componenttest.CheckConfigStruct(cfg)) -} - -func TestLoadConfig(t *testing.T) { - t.Parallel() - - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) - require.NoError(t, err) - - tests := []struct { - id component.ID - expected component.Config - }{ - { - id: component.NewIDWithName(metadata.Type, "default"), - expected: &Config{ - ClientConfig: defaultClientConfig(), - BackOffConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - API: APIConfig{ - Key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - Site: "datadoghq.com", - FailOnInvalidKey: false, - }, - - Metrics: MetricsConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://api.datadoghq.com", - }, - DeltaTTL: 3600, - HistConfig: HistogramConfig{ - Mode: "distributions", - SendAggregations: false, - }, - SumConfig: SumConfig{ - CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, - InitialCumulativeMonotonicMode: InitialValueModeAuto, - }, - SummaryConfig: SummaryConfig{ - Mode: SummaryModeGauges, - }, - }, - - Traces: TracesConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://trace.agent.datadoghq.com", - }, - IgnoreResources: []string{}, - }, - Logs: LogsConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://http-intake.logs.datadoghq.com", - }, - UseCompression: true, - CompressionLevel: 6, - BatchWait: 5, - }, - HostMetadata: HostMetadataConfig{ - Enabled: true, - HostnameSource: HostnameSourceConfigOrSystem, - }, - OnlyMetadata: false, - }, - }, - { - id: component.NewIDWithName(metadata.Type, "api"), - expected: &Config{ - ClientConfig: defaultClientConfig(), - BackOffConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - TagsConfig: TagsConfig{ - Hostname: "customhostname", - }, - API: APIConfig{ - Key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - Site: "datadoghq.eu", - FailOnInvalidKey: true, - }, - Metrics: MetricsConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://api.datadoghq.eu", - }, - DeltaTTL: 3600, - HistConfig: HistogramConfig{ - Mode: "distributions", - SendAggregations: false, - }, - SumConfig: SumConfig{ - CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, - InitialCumulativeMonotonicMode: InitialValueModeAuto, - }, - SummaryConfig: SummaryConfig{ - Mode: SummaryModeGauges, - }, - }, - Traces: TracesConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://trace.agent.datadoghq.eu", - }, - SpanNameRemappings: map[string]string{ - "old_name1": "new_name1", - "old_name2": "new_name2", - }, - SpanNameAsResourceName: true, - IgnoreResources: []string{}, - TraceBuffer: 10, - }, - Logs: LogsConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://http-intake.logs.datadoghq.eu", - }, - UseCompression: true, - CompressionLevel: 6, - BatchWait: 5, - }, - OnlyMetadata: false, - HostMetadata: HostMetadataConfig{ - Enabled: true, - HostnameSource: HostnameSourceConfigOrSystem, - }, - }, - }, - { - id: component.NewIDWithName(metadata.Type, "api2"), - expected: &Config{ - ClientConfig: defaultClientConfig(), - BackOffConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - TagsConfig: TagsConfig{ - Hostname: "customhostname", - }, - API: APIConfig{ - Key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - Site: "datadoghq.eu", - FailOnInvalidKey: false, - }, - Metrics: MetricsConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://api.datadoghq.test", - }, - DeltaTTL: 3600, - HistConfig: HistogramConfig{ - Mode: "distributions", - SendAggregations: false, - }, - SumConfig: SumConfig{ - CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, - InitialCumulativeMonotonicMode: InitialValueModeAuto, - }, - SummaryConfig: SummaryConfig{ - Mode: SummaryModeGauges, - }, - }, - Traces: TracesConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://trace.agent.datadoghq.test", - }, - SpanNameRemappings: map[string]string{ - "old_name3": "new_name3", - "old_name4": "new_name4", - }, - IgnoreResources: []string{}, - }, - Logs: LogsConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{ - Endpoint: "https://http-intake.logs.datadoghq.test", - }, - UseCompression: true, - CompressionLevel: 6, - BatchWait: 5, - }, - HostMetadata: HostMetadataConfig{ - Enabled: true, - HostnameSource: HostnameSourceConfigOrSystem, - Tags: []string{"example:tag"}, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.id.String(), func(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - - sub, err := cm.Sub(tt.id.String()) - require.NoError(t, err) - require.NoError(t, sub.Unmarshal(cfg)) - - assert.NoError(t, component.ValidateConfig(cfg)) - assert.Equal(t, tt.expected, cfg) - }) - } -} - -func TestOverrideEndpoints(t *testing.T) { - tests := []struct { - componentID string - expectedSite string - expectedMetricsEndpoint string - expectedTracesEndpoint string - expectedLogsEndpoint string - }{ - { - componentID: "nositeandnoendpoints", - expectedSite: "datadoghq.com", - expectedMetricsEndpoint: "https://api.datadoghq.com", - expectedTracesEndpoint: "https://trace.agent.datadoghq.com", - expectedLogsEndpoint: "https://http-intake.logs.datadoghq.com", - }, - { - componentID: "nositeandmetricsendpoint", - expectedSite: "datadoghq.com", - expectedMetricsEndpoint: "metricsendpoint:1234", - expectedTracesEndpoint: "https://trace.agent.datadoghq.com", - expectedLogsEndpoint: "https://http-intake.logs.datadoghq.com", - }, - { - componentID: "nositeandtracesendpoint", - expectedSite: "datadoghq.com", - expectedMetricsEndpoint: "https://api.datadoghq.com", - expectedTracesEndpoint: "tracesendpoint:1234", - expectedLogsEndpoint: "https://http-intake.logs.datadoghq.com", - }, - { - componentID: "nositeandlogsendpoint", - expectedSite: "datadoghq.com", - expectedMetricsEndpoint: "https://api.datadoghq.com", - expectedTracesEndpoint: "https://trace.agent.datadoghq.com", - expectedLogsEndpoint: "logsendpoint:1234", - }, - { - componentID: "nositeandallendpoints", - expectedSite: "datadoghq.com", - expectedMetricsEndpoint: "metricsendpoint:1234", - expectedTracesEndpoint: "tracesendpoint:1234", - expectedLogsEndpoint: "logsendpoint:1234", - }, - - { - componentID: "siteandnoendpoints", - expectedSite: "datadoghq.eu", - expectedMetricsEndpoint: "https://api.datadoghq.eu", - expectedTracesEndpoint: "https://trace.agent.datadoghq.eu", - expectedLogsEndpoint: "https://http-intake.logs.datadoghq.eu", - }, - { - componentID: "siteandmetricsendpoint", - expectedSite: "datadoghq.eu", - expectedMetricsEndpoint: "metricsendpoint:1234", - expectedTracesEndpoint: "https://trace.agent.datadoghq.eu", - expectedLogsEndpoint: "https://http-intake.logs.datadoghq.eu", - }, - { - componentID: "siteandtracesendpoint", - expectedSite: "datadoghq.eu", - expectedMetricsEndpoint: "https://api.datadoghq.eu", - expectedTracesEndpoint: "tracesendpoint:1234", - expectedLogsEndpoint: "https://http-intake.logs.datadoghq.eu", - }, - { - componentID: "siteandallendpoints", - expectedSite: "datadoghq.eu", - expectedMetricsEndpoint: "metricsendpoint:1234", - expectedTracesEndpoint: "tracesendpoint:1234", - expectedLogsEndpoint: "logsendpoint:1234", - }, - } - - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "unmarshal.yaml")) - require.NoError(t, err) - factory := NewFactory() - - for _, testInstance := range tests { - t.Run(testInstance.componentID, func(t *testing.T) { - cfg := factory.CreateDefaultConfig() - sub, err := cm.Sub(component.NewIDWithName(metadata.Type, testInstance.componentID).String()) - require.NoError(t, err) - require.NoError(t, sub.Unmarshal(cfg)) - - componentCfg, ok := cfg.(*Config) - require.True(t, ok, "component.Config is not a Datadog exporter config (wrong ID?)") - assert.Equal(t, testInstance.expectedSite, componentCfg.API.Site) - assert.Equal(t, testInstance.expectedMetricsEndpoint, componentCfg.Metrics.Endpoint) - assert.Equal(t, testInstance.expectedTracesEndpoint, componentCfg.Traces.Endpoint) - assert.Equal(t, testInstance.expectedLogsEndpoint, componentCfg.Logs.Endpoint) - }) - } -} - func TestCreateAPIMetricsExporter(t *testing.T) { server := testutil.DatadogServerMock() defer server.Close() @@ -653,9 +308,9 @@ func TestOnlyMetadata(t *testing.T) { HostMetadata: HostMetadataConfig{ Enabled: true, HostnameSource: HostnameSourceFirstResource, - sourceTimeout: 50 * time.Millisecond, }, } + cfg.HostMetadata.SetSourceTimeout(50 * time.Millisecond) expTraces, err := factory.CreateTracesExporter( ctx, diff --git a/exporter/datadogexporter/go.mod b/exporter/datadogexporter/go.mod index 7b6fa98a65a6..302ef83b1e6b 100644 --- a/exporter/datadogexporter/go.mod +++ b/exporter/datadogexporter/go.mod @@ -57,11 +57,11 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.109.0 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.109.0 - go.opentelemetry.io/collector/config/configauth v0.109.0 + go.opentelemetry.io/collector/config/configauth v0.109.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.15.0 go.opentelemetry.io/collector/config/confighttp v0.109.0 go.opentelemetry.io/collector/config/confignet v0.109.0 - go.opentelemetry.io/collector/config/configopaque v1.15.0 + go.opentelemetry.io/collector/config/configopaque v1.15.0 // indirect go.opentelemetry.io/collector/config/configretry v1.15.0 go.opentelemetry.io/collector/config/configtls v1.15.0 go.opentelemetry.io/collector/confmap v1.15.0 @@ -90,6 +90,8 @@ require ( k8s.io/client-go v0.31.0 ) +require github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog v0.0.0-00010101000000-000000000000 + require ( cloud.google.com/go/auth v0.7.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect @@ -467,3 +469,5 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling = replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/pdatautil => ../../internal/pdatautil replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata => ../../pkg/experimentalmetricmetadata + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog => ../../pkg/datadog diff --git a/exporter/datadogexporter/integrationtest/go.mod b/exporter/datadogexporter/integrationtest/go.mod index 92fd6d6d8427..028b602809c6 100644 --- a/exporter/datadogexporter/integrationtest/go.mod +++ b/exporter/datadogexporter/integrationtest/go.mod @@ -239,6 +239,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter v0.109.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig v0.109.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders v0.109.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog v0.0.0-00010101000000-000000000000 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl v0.109.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.109.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.109.0 // indirect @@ -443,3 +444,5 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling = replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/pdatautil => ../../../internal/pdatautil replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata => ../../../pkg/experimentalmetricmetadata + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog => ../../../pkg/datadog/ diff --git a/exporter/datadogexporter/metrics_exporter_test.go b/exporter/datadogexporter/metrics_exporter_test.go index 937d38f5b40a..08e4bd80dc6b 100644 --- a/exporter/datadogexporter/metrics_exporter_test.go +++ b/exporter/datadogexporter/metrics_exporter_test.go @@ -58,10 +58,9 @@ func TestNewExporter(t *testing.T) { CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, }, }, - HostMetadata: HostMetadataConfig{ - sourceTimeout: 50 * time.Millisecond, - }, + HostMetadata: HostMetadataConfig{}, } + cfg.HostMetadata.SetSourceTimeout(50 * time.Millisecond) params := exportertest.NewNopSettings() f := NewFactory() @@ -351,9 +350,7 @@ func Test_metricsExporter_PushMetricsData(t *testing.T) { ) defer server.Close() - var ( - once sync.Once - ) + var once sync.Once pusher := newTestPusher(t) reporter, err := inframetadata.NewReporter(zap.NewNop(), pusher, 1*time.Second) require.NoError(t, err) @@ -794,9 +791,7 @@ func Test_metricsExporter_PushMetricsData_Zorkian(t *testing.T) { ) defer server.Close() - var ( - once sync.Once - ) + var once sync.Once pusher := newTestPusher(t) reporter, err := inframetadata.NewReporter(zap.NewNop(), pusher, 1*time.Second) require.NoError(t, err) diff --git a/exporter/datadogexporter/traces_exporter.go b/exporter/datadogexporter/traces_exporter.go index 9254ffac7486..a510c7e88ae4 100644 --- a/exporter/datadogexporter/traces_exporter.go +++ b/exporter/datadogexporter/traces_exporter.go @@ -226,7 +226,7 @@ func newTraceAgentConfig(ctx context.Context, params exporter.Settings, cfg *Con return clientutil.NewHTTPClient(cfg.ClientConfig) } } - if v := cfg.Traces.flushInterval; v > 0 { + if v := cfg.Traces.GetFlushInterval(); v > 0 { acfg.TraceWriter.FlushPeriodSeconds = v } if v := cfg.Traces.TraceBuffer; v > 0 { @@ -238,6 +238,6 @@ func newTraceAgentConfig(ctx context.Context, params exporter.Settings, cfg *Con if cfg.Traces.ComputeTopLevelBySpanKind { acfg.Features["enable_otlp_compute_top_level_by_span_kind"] = struct{}{} } - tracelog.SetLogger(&zaplogger{params.Logger}) //TODO: This shouldn't be a singleton + tracelog.SetLogger(&zaplogger{params.Logger}) // TODO: This shouldn't be a singleton return acfg, nil } diff --git a/exporter/datadogexporter/traces_exporter_test.go b/exporter/datadogexporter/traces_exporter_test.go index 72821dcd503c..468b6e1560ad 100644 --- a/exporter/datadogexporter/traces_exporter_test.go +++ b/exporter/datadogexporter/traces_exporter_test.go @@ -30,6 +30,8 @@ import ( conventions127 "go.opentelemetry.io/collector/semconv/v1.27.0" semconv "go.opentelemetry.io/collector/semconv/v1.6.1" "google.golang.org/protobuf/proto" + + datadogconfig "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" ) func setupTestMain(m *testing.M) { @@ -138,8 +140,10 @@ func TestTracesSource(t *testing.T) { TCPAddrConfig: confignet.TCPAddrConfig{Endpoint: metricsServer.URL}, }, Traces: TracesConfig{ - TCPAddrConfig: confignet.TCPAddrConfig{Endpoint: tracesServer.URL}, - IgnoreResources: []string{}, + TCPAddrConfig: confignet.TCPAddrConfig{Endpoint: tracesServer.URL}, + TracesConfig: datadogconfig.TracesConfig{ + IgnoreResources: []string{}, + }, }, } @@ -258,11 +262,13 @@ func TestTraceExporter(t *testing.T) { TCPAddrConfig: confignet.TCPAddrConfig{ Endpoint: server.URL, }, - IgnoreResources: []string{}, - flushInterval: 0.1, - TraceBuffer: 2, + TracesConfig: datadogconfig.TracesConfig{ + IgnoreResources: []string{}, + }, + TraceBuffer: 2, }, } + cfg.Traces.SetFlushInterval(0.1) params := exportertest.NewNopSettings() f := NewFactory() @@ -351,9 +357,9 @@ func TestPushTraceData_NewEnvConvention(t *testing.T) { }, Traces: TracesConfig{ TCPAddrConfig: confignet.TCPAddrConfig{Endpoint: server.URL}, - flushInterval: 0.1, }, } + cfg.Traces.SetFlushInterval(0.1) params := exportertest.NewNopSettings() f := NewFactory() diff --git a/pkg/datadog/Makefile b/pkg/datadog/Makefile new file mode 100644 index 000000000000..c1496226e590 --- /dev/null +++ b/pkg/datadog/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common \ No newline at end of file diff --git a/pkg/datadog/config/config.go b/pkg/datadog/config/config.go new file mode 100644 index 000000000000..1b7a68ea4772 --- /dev/null +++ b/pkg/datadog/config/config.go @@ -0,0 +1,346 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package config // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/DataDog/datadog-agent/pkg/util/hostname/validate" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/confignet" + "go.opentelemetry.io/collector/config/configopaque" + "go.opentelemetry.io/collector/config/configretry" + "go.opentelemetry.io/collector/confmap" + "go.opentelemetry.io/collector/exporter/exporterhelper" + "go.uber.org/zap" +) + +var ( + // ErrUnsetAPIKey is returned when the API key is not set. + ErrUnsetAPIKey = errors.New("api.key is not set") + // ErrNoMetadata is returned when only_metadata is enabled but host metadata is disabled or hostname_source is not first_resource. + ErrNoMetadata = errors.New("only_metadata can't be enabled when host_metadata::enabled = false or host_metadata::hostname_source != first_resource") + // ErrInvalidHostname is returned when the hostname is invalid. + ErrEmptyEndpoint = errors.New("endpoint cannot be empty") +) + +const ( + // DefaultSite is the default site of the Datadog intake to send data to + DefaultSite = "datadoghq.com" +) + +// APIConfig defines the API configuration options +type APIConfig struct { + // Key is the Datadog API key to associate your Agent's data with your organization. + // Create a new API key here: https://app.datadoghq.com/account/settings + Key configopaque.String `mapstructure:"key"` + + // Site is the site of the Datadog intake to send data to. + // The default value is "datadoghq.com". + Site string `mapstructure:"site"` + + // FailOnInvalidKey states whether to exit at startup on invalid API key. + // The default value is false. + FailOnInvalidKey bool `mapstructure:"fail_on_invalid_key"` +} + +// TagsConfig defines the tag-related configuration +// It is embedded in the configuration +type TagsConfig struct { + // Hostname is the fallback hostname used for payloads without hostname-identifying attributes. + // This option will NOT change the hostname applied to your metrics, traces and logs if they already have hostname-identifying attributes. + // If unset, the hostname will be determined automatically. See https://docs.datadoghq.com/opentelemetry/schema_semantics/hostname/?tab=datadogexporter#fallback-hostname-logic for details. + // + // Prefer using the `datadog.host.name` resource attribute over using this setting. + // See https://docs.datadoghq.com/opentelemetry/schema_semantics/hostname/?tab=datadogexporter#general-hostname-semantic-conventions for details. + Hostname string `mapstructure:"hostname"` +} + +// Config defines configuration for the Datadog exporter. +type Config struct { + confighttp.ClientConfig `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct. + exporterhelper.QueueSettings `mapstructure:"sending_queue"` + configretry.BackOffConfig `mapstructure:"retry_on_failure"` + + TagsConfig `mapstructure:",squash"` + + // API defines the Datadog API configuration. + API APIConfig `mapstructure:"api"` + + // Metrics defines the Metrics exporter specific configuration + Metrics MetricsConfig `mapstructure:"metrics"` + + // Traces defines the Traces exporter specific configuration + Traces TracesExporterConfig `mapstructure:"traces"` + + // Logs defines the Logs exporter specific configuration + Logs LogsConfig `mapstructure:"logs"` + + // HostMetadata defines the host metadata specific configuration + HostMetadata HostMetadataConfig `mapstructure:"host_metadata"` + + // OnlyMetadata defines whether to only send metadata + // This is useful for agent-collector setups, so that + // metadata about a host is sent to the backend even + // when telemetry data is reported via a different host. + // + // This flag is incompatible with disabling host metadata, + // `use_resource_metadata`, or `host_metadata::hostname_source != first_resource` + OnlyMetadata bool `mapstructure:"only_metadata"` + + // Non-fatal warnings found during configuration loading. + warnings []error +} + +// LogWarnings logs warning messages that were generated on unmarshaling. +func (c *Config) LogWarnings(logger *zap.Logger) { + for _, err := range c.warnings { + logger.Warn(fmt.Sprintf("%v", err)) + } +} + +var _ component.Config = (*Config)(nil) + +// Validate the configuration for errors. This is required by component.Config. +func (c *Config) Validate() error { + if err := validateClientConfig(c.ClientConfig); err != nil { + return err + } + + if c.OnlyMetadata && (!c.HostMetadata.Enabled || c.HostMetadata.HostnameSource != HostnameSourceFirstResource) { + return ErrNoMetadata + } + + if err := validate.ValidHostname(c.Hostname); c.Hostname != "" && err != nil { + return fmt.Errorf("hostname field is invalid: %w", err) + } + + if c.API.Key == "" { + return ErrUnsetAPIKey + } + + if err := c.Traces.Validate(); err != nil { + return err + } + + err := c.Metrics.HistConfig.validate() + if err != nil { + return err + } + + return nil +} + +func validateClientConfig(cfg confighttp.ClientConfig) error { + var unsupported []string + if cfg.Auth != nil { + unsupported = append(unsupported, "auth") + } + if cfg.Endpoint != "" { + unsupported = append(unsupported, "endpoint") + } + if cfg.Compression != "" { + unsupported = append(unsupported, "compression") + } + if cfg.Headers != nil { + unsupported = append(unsupported, "headers") + } + if cfg.HTTP2ReadIdleTimeout != 0 { + unsupported = append(unsupported, "http2_read_idle_timeout") + } + if cfg.HTTP2PingTimeout != 0 { + unsupported = append(unsupported, "http2_ping_timeout") + } + + if len(unsupported) > 0 { + return fmt.Errorf("these confighttp client configs are currently not respected by Datadog exporter: %s", strings.Join(unsupported, ", ")) + } + return nil +} + +var _ error = (*renameError)(nil) + +// renameError is an error related to a renamed setting. +type renameError struct { + // oldName of the configuration option. + oldName string + // newName of the configuration option. + newName string + // issueNumber on opentelemetry-collector-contrib for tracking + issueNumber uint +} + +// List of settings that have been removed, but for which we keep a custom error. +var removedSettings = []renameError{ + { + oldName: "metrics::send_monotonic_counter", + newName: "metrics::sums::cumulative_monotonic_mode", + issueNumber: 8489, + }, + { + oldName: "tags", + newName: "host_metadata::tags", + issueNumber: 9099, + }, + { + oldName: "send_metadata", + newName: "host_metadata::enabled", + issueNumber: 9099, + }, + { + oldName: "use_resource_metadata", + newName: "host_metadata::hostname_source", + issueNumber: 9099, + }, + { + oldName: "metrics::report_quantiles", + newName: "metrics::summaries::mode", + issueNumber: 8845, + }, + { + oldName: "metrics::instrumentation_library_metadata_as_tags", + newName: "metrics::instrumentation_scope_as_tags", + issueNumber: 11135, + }, +} + +// Error implements the error interface. +func (e renameError) Error() string { + return fmt.Sprintf( + "%q was removed in favor of %q. See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/%d", + e.oldName, + e.newName, + e.issueNumber, + ) +} + +func handleRemovedSettings(configMap *confmap.Conf) error { + var errs []error + for _, removedErr := range removedSettings { + if configMap.IsSet(removedErr.oldName) { + errs = append(errs, removedErr) + } + } + + return errors.Join(errs...) +} + +var _ confmap.Unmarshaler = (*Config)(nil) + +// Unmarshal a configuration map into the configuration struct. +func (c *Config) Unmarshal(configMap *confmap.Conf) error { + if err := handleRemovedSettings(configMap); err != nil { + return err + } + + err := configMap.Unmarshal(c) + if err != nil { + return err + } + + // Add deprecation warnings for deprecated settings. + renamingWarnings, err := handleRenamedSettings(configMap, c) + if err != nil { + return err + } + c.warnings = append(c.warnings, renamingWarnings...) + + c.API.Key = configopaque.String(strings.TrimSpace(string(c.API.Key))) + + // If an endpoint is not explicitly set, override it based on the site. + if !configMap.IsSet("metrics::endpoint") { + c.Metrics.TCPAddrConfig.Endpoint = fmt.Sprintf("https://api.%s", c.API.Site) + } + if !configMap.IsSet("traces::endpoint") { + c.Traces.TCPAddrConfig.Endpoint = fmt.Sprintf("https://trace.agent.%s", c.API.Site) + } + if !configMap.IsSet("logs::endpoint") { + c.Logs.TCPAddrConfig.Endpoint = fmt.Sprintf("https://http-intake.logs.%s", c.API.Site) + } + + // Return an error if an endpoint is explicitly set to "" + if c.Metrics.TCPAddrConfig.Endpoint == "" || c.Traces.TCPAddrConfig.Endpoint == "" || c.Logs.TCPAddrConfig.Endpoint == "" { + return ErrEmptyEndpoint + } + + const ( + initialValueSetting = "metrics::sums::initial_cumulative_monotonic_value" + cumulMonoMode = "metrics::sums::cumulative_monotonic_mode" + ) + if configMap.IsSet(initialValueSetting) && c.Metrics.SumConfig.CumulativeMonotonicMode != CumulativeMonotonicSumModeToDelta { + return fmt.Errorf("%q can only be configured when %q is set to %q", + initialValueSetting, cumulMonoMode, CumulativeMonotonicSumModeToDelta) + } + + return nil +} + +func defaultClientConfig() confighttp.ClientConfig { + // do not use NewDefaultClientConfig for backwards-compatibility + return confighttp.ClientConfig{ + Timeout: 15 * time.Second, + } +} + +// CreateDefaultConfig creates the default exporter configuration +func CreateDefaultConfig() component.Config { + return &Config{ + ClientConfig: defaultClientConfig(), + BackOffConfig: configretry.NewDefaultBackOffConfig(), + QueueSettings: exporterhelper.NewDefaultQueueSettings(), + + API: APIConfig{ + Site: "datadoghq.com", + }, + + Metrics: MetricsConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://api.datadoghq.com", + }, + DeltaTTL: 3600, + ExporterConfig: MetricsExporterConfig{ + ResourceAttributesAsTags: false, + InstrumentationScopeMetadataAsTags: false, + }, + HistConfig: HistogramConfig{ + Mode: "distributions", + SendAggregations: false, + }, + SumConfig: SumConfig{ + CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, + InitialCumulativeMonotonicMode: InitialValueModeAuto, + }, + SummaryConfig: SummaryConfig{ + Mode: SummaryModeGauges, + }, + }, + + Traces: TracesExporterConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://trace.agent.datadoghq.com", + }, + TracesConfig: TracesConfig{ + IgnoreResources: []string{}, + }, + }, + + Logs: LogsConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://http-intake.logs.datadoghq.com", + }, + UseCompression: true, + CompressionLevel: 6, + BatchWait: 5, + }, + + HostMetadata: HostMetadataConfig{ + Enabled: true, + HostnameSource: HostnameSourceConfigOrSystem, + }, + } +} diff --git a/pkg/datadog/config/config_test.go b/pkg/datadog/config/config_test.go new file mode 100644 index 000000000000..4af387f18889 --- /dev/null +++ b/pkg/datadog/config/config_test.go @@ -0,0 +1,727 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package config // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" + +import ( + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/config/configauth" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/confignet" + "go.opentelemetry.io/collector/config/configopaque" + "go.opentelemetry.io/collector/config/configretry" + "go.opentelemetry.io/collector/config/configtls" + "go.opentelemetry.io/collector/confmap" + "go.opentelemetry.io/collector/confmap/confmaptest" + "go.opentelemetry.io/collector/exporter/exporterhelper" +) + +func TestValidate(t *testing.T) { + idleConnTimeout := 30 * time.Second + maxIdleConn := 300 + maxIdleConnPerHost := 150 + maxConnPerHost := 250 + ty, err := component.NewType("ty") + assert.NoError(t, err) + auth := configauth.Authentication{AuthenticatorID: component.NewID(ty)} + + tests := []struct { + name string + cfg *Config + err string + }{ + { + name: "no api::key", + cfg: &Config{}, + err: ErrUnsetAPIKey.Error(), + }, + { + name: "invalid hostname", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + TagsConfig: TagsConfig{Hostname: "invalid_host"}, + }, + err: "hostname field is invalid: invalid_host is not RFC1123 compliant", + }, + { + name: "no metadata", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + OnlyMetadata: true, + HostMetadata: HostMetadataConfig{Enabled: false}, + }, + err: ErrNoMetadata.Error(), + }, + { + name: "span name remapping valid", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + Traces: TracesExporterConfig{TracesConfig: TracesConfig{SpanNameRemappings: map[string]string{"old.opentelemetryspan.name": "updated.name"}}}, + }, + }, + { + name: "span name remapping empty val", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + Traces: TracesExporterConfig{TracesConfig: TracesConfig{SpanNameRemappings: map[string]string{"oldname": ""}}}, + }, + err: "'' is not valid value for span name remapping", + }, + { + name: "span name remapping empty key", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + Traces: TracesExporterConfig{TracesConfig: TracesConfig{SpanNameRemappings: map[string]string{"": "newname"}}}, + }, + err: "'' is not valid key for span name remapping", + }, + { + name: "ignore resources valid", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + Traces: TracesExporterConfig{TracesConfig: TracesConfig{IgnoreResources: []string{"[123]"}}}, + }, + }, + { + name: "ignore resources missing bracket", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + Traces: TracesExporterConfig{TracesConfig: TracesConfig{IgnoreResources: []string{"[123"}}}, + }, + err: "'[123' is not valid resource filter regular expression", + }, + { + name: "invalid histogram settings", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + Metrics: MetricsConfig{ + HistConfig: HistogramConfig{ + Mode: HistogramModeNoBuckets, + SendAggregations: false, + }, + }, + }, + err: "'nobuckets' mode and `send_aggregation_metrics` set to false will send no histogram metrics", + }, + { + name: "TLS settings are valid", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + ClientConfig: confighttp.ClientConfig{ + TLSSetting: configtls.ClientConfig{ + InsecureSkipVerify: true, + }, + }, + }, + }, + { + name: "With trace_buffer", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + Traces: TracesExporterConfig{TraceBuffer: 10}, + }, + }, + { + name: "With peer_tags", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + Traces: TracesExporterConfig{ + TracesConfig: TracesConfig{ + PeerTags: []string{"tag1", "tag2"}, + }, + }, + }, + }, + { + name: "With confighttp client configs", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + ClientConfig: confighttp.ClientConfig{ + ReadBufferSize: 100, + WriteBufferSize: 200, + Timeout: 10 * time.Second, + IdleConnTimeout: &idleConnTimeout, + MaxIdleConns: &maxIdleConn, + MaxIdleConnsPerHost: &maxIdleConnPerHost, + MaxConnsPerHost: &maxConnPerHost, + DisableKeepAlives: true, + TLSSetting: configtls.ClientConfig{InsecureSkipVerify: true}, + }, + }, + }, + + { + name: "unsupported confighttp client configs", + cfg: &Config{ + API: APIConfig{Key: "notnull"}, + ClientConfig: confighttp.ClientConfig{ + Endpoint: "endpoint", + Compression: "gzip", + Auth: &auth, + Headers: map[string]configopaque.String{"key": "val"}, + HTTP2ReadIdleTimeout: 250, + HTTP2PingTimeout: 200, + }, + }, + err: "these confighttp client configs are currently not respected by Datadog exporter: auth, endpoint, compression, headers, http2_read_idle_timeout, http2_ping_timeout", + }, + } + for _, testInstance := range tests { + t.Run(testInstance.name, func(t *testing.T) { + err := testInstance.cfg.Validate() + if testInstance.err != "" { + assert.EqualError(t, err, testInstance.err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestUnmarshal(t *testing.T) { + cfgWithHTTPConfigs := CreateDefaultConfig().(*Config) + idleConnTimeout := 30 * time.Second + maxIdleConn := 300 + maxIdleConnPerHost := 150 + maxConnPerHost := 250 + cfgWithHTTPConfigs.ReadBufferSize = 100 + cfgWithHTTPConfigs.WriteBufferSize = 200 + cfgWithHTTPConfigs.Timeout = 10 * time.Second + cfgWithHTTPConfigs.MaxIdleConns = &maxIdleConn + cfgWithHTTPConfigs.MaxIdleConnsPerHost = &maxIdleConnPerHost + cfgWithHTTPConfigs.MaxConnsPerHost = &maxConnPerHost + cfgWithHTTPConfigs.IdleConnTimeout = &idleConnTimeout + cfgWithHTTPConfigs.DisableKeepAlives = true + cfgWithHTTPConfigs.TLSSetting.InsecureSkipVerify = true + cfgWithHTTPConfigs.warnings = nil + + tests := []struct { + name string + configMap *confmap.Conf + cfg *Config + err string + field string + }{ + { + name: "invalid cumulative monotonic mode", + configMap: confmap.NewFromStringMap(map[string]any{ + "metrics": map[string]any{ + "sums": map[string]any{ + "cumulative_monotonic_mode": "invalid_mode", + }, + }, + }), + err: "invalid cumulative monotonic sum mode \"invalid_mode\"", + field: "metrics.sums.cumulative_monotonic_mode", + }, + { + name: "invalid host metadata hostname source", + configMap: confmap.NewFromStringMap(map[string]any{ + "host_metadata": map[string]any{ + "hostname_source": "invalid_source", + }, + }), + err: "invalid host metadata hostname source \"invalid_source\"", + field: "host_metadata.hostname_source", + }, + { + name: "invalid summary mode", + configMap: confmap.NewFromStringMap(map[string]any{ + "metrics": map[string]any{ + "summaries": map[string]any{ + "mode": "invalid_mode", + }, + }, + }), + err: "invalid summary mode \"invalid_mode\"", + field: "metrics.summaries.mode", + }, + { + name: "metrics::send_monotonic_counter custom error", + configMap: confmap.NewFromStringMap(map[string]any{ + "metrics": map[string]any{ + "send_monotonic_counter": true, + }, + }), + err: "\"metrics::send_monotonic_counter\" was removed in favor of \"metrics::sums::cumulative_monotonic_mode\". See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/8489", + }, + { + name: "tags custom error", + configMap: confmap.NewFromStringMap(map[string]any{ + "tags": []string{}, + }), + err: "\"tags\" was removed in favor of \"host_metadata::tags\". See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/9099", + }, + { + name: "send_metadata custom error", + configMap: confmap.NewFromStringMap(map[string]any{ + "send_metadata": false, + }), + err: "\"send_metadata\" was removed in favor of \"host_metadata::enabled\". See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/9099", + }, + { + name: "use_resource_metadata custom error", + configMap: confmap.NewFromStringMap(map[string]any{ + "use_resource_metadata": false, + }), + err: "\"use_resource_metadata\" was removed in favor of \"host_metadata::hostname_source\". See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/9099", + }, + { + name: "metrics::report_quantiles custom error", + configMap: confmap.NewFromStringMap(map[string]any{ + "metrics": map[string]any{ + "report_quantiles": true, + }, + }), + err: "\"metrics::report_quantiles\" was removed in favor of \"metrics::summaries::mode\". See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/8845", + }, + { + name: "instrumentation_library_metadata_as_tags custom error", + configMap: confmap.NewFromStringMap(map[string]any{ + "metrics": map[string]any{ + "instrumentation_library_metadata_as_tags": true, + }, + }), + err: "\"metrics::instrumentation_library_metadata_as_tags\" was removed in favor of \"metrics::instrumentation_scope_as_tags\". See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/11135", + }, + { + name: "Empty metric endpoint", + configMap: confmap.NewFromStringMap(map[string]any{ + "metrics": map[string]any{ + "endpoint": "", + }, + }), + err: ErrEmptyEndpoint.Error(), + }, + { + name: "Empty trace endpoint", + configMap: confmap.NewFromStringMap(map[string]any{ + "traces": map[string]any{ + "endpoint": "", + }, + }), + err: ErrEmptyEndpoint.Error(), + }, + { + name: "Empty log endpoint", + configMap: confmap.NewFromStringMap(map[string]any{ + "logs": map[string]any{ + "endpoint": "", + }, + }), + err: ErrEmptyEndpoint.Error(), + }, + { + name: "invalid initial cumulative monotonic value mode", + configMap: confmap.NewFromStringMap(map[string]any{ + "metrics": map[string]any{ + "sums": map[string]any{ + "initial_cumulative_monotonic_value": "invalid_mode", + }, + }, + }), + err: "invalid initial value mode \"invalid_mode\"", + field: "metrics.sums.initial_cumulative_monotonic_value", + }, + { + name: "initial cumulative monotonic value mode set with raw_value", + configMap: confmap.NewFromStringMap(map[string]any{ + "metrics": map[string]any{ + "sums": map[string]any{ + "cumulative_monotonic_mode": "raw_value", + "initial_cumulative_monotonic_value": "drop", + }, + }, + }), + err: "\"metrics::sums::initial_cumulative_monotonic_value\" can only be configured when \"metrics::sums::cumulative_monotonic_mode\" is set to \"to_delta\"", + }, + { + name: "unmarshall confighttp client configs", + configMap: confmap.NewFromStringMap(map[string]any{ + "read_buffer_size": 100, + "write_buffer_size": 200, + "timeout": "10s", + "max_idle_conns": 300, + "max_idle_conns_per_host": 150, + "max_conns_per_host": 250, + "disable_keep_alives": true, + "idle_conn_timeout": "30s", + "tls": map[string]any{"insecure_skip_verify": true}, + }), + cfg: cfgWithHTTPConfigs, + }, + } + + for _, testInstance := range tests { + t.Run(testInstance.name, func(t *testing.T) { + cfg := CreateDefaultConfig().(*Config) + err := cfg.Unmarshal(testInstance.configMap) + if err != nil || testInstance.err != "" { + assert.ErrorContains(t, err, testInstance.err) + if testInstance.field != "" { + assert.ErrorContains(t, err, testInstance.field) + } + } else { + assert.Equal(t, testInstance.cfg, cfg) + } + }) + } +} + +// Test that the factory creates the default configuration +func TestCreateDefaultConfig(t *testing.T) { + cfg := CreateDefaultConfig() + + assert.Equal(t, &Config{ + ClientConfig: defaultClientConfig(), + BackOffConfig: configretry.NewDefaultBackOffConfig(), + QueueSettings: exporterhelper.NewDefaultQueueSettings(), + + API: APIConfig{ + Site: "datadoghq.com", + }, + + Metrics: MetricsConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://api.datadoghq.com", + }, + DeltaTTL: 3600, + HistConfig: HistogramConfig{ + Mode: "distributions", + SendAggregations: false, + }, + SumConfig: SumConfig{ + CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, + InitialCumulativeMonotonicMode: InitialValueModeAuto, + }, + SummaryConfig: SummaryConfig{ + Mode: SummaryModeGauges, + }, + }, + + Traces: TracesExporterConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://trace.agent.datadoghq.com", + }, + TracesConfig: TracesConfig{ + IgnoreResources: []string{}, + }, + }, + Logs: LogsConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://http-intake.logs.datadoghq.com", + }, + UseCompression: true, + CompressionLevel: 6, + BatchWait: 5, + }, + + HostMetadata: HostMetadataConfig{ + Enabled: true, + HostnameSource: HostnameSourceConfigOrSystem, + }, + OnlyMetadata: false, + }, cfg, "failed to create default config") + + assert.NoError(t, componenttest.CheckConfigStruct(cfg)) +} + +var ddtype = component.MustNewType("datadog") + +func TestLoadConfig(t *testing.T) { + t.Parallel() + + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + + tests := []struct { + id component.ID + expected component.Config + }{ + { + id: component.NewIDWithName(ddtype, "default"), + expected: &Config{ + ClientConfig: defaultClientConfig(), + BackOffConfig: configretry.NewDefaultBackOffConfig(), + QueueSettings: exporterhelper.NewDefaultQueueSettings(), + API: APIConfig{ + Key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + Site: "datadoghq.com", + FailOnInvalidKey: false, + }, + + Metrics: MetricsConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://api.datadoghq.com", + }, + DeltaTTL: 3600, + HistConfig: HistogramConfig{ + Mode: "distributions", + SendAggregations: false, + }, + SumConfig: SumConfig{ + CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, + InitialCumulativeMonotonicMode: InitialValueModeAuto, + }, + SummaryConfig: SummaryConfig{ + Mode: SummaryModeGauges, + }, + }, + + Traces: TracesExporterConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://trace.agent.datadoghq.com", + }, + TracesConfig: TracesConfig{ + IgnoreResources: []string{}, + }, + }, + Logs: LogsConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://http-intake.logs.datadoghq.com", + }, + UseCompression: true, + CompressionLevel: 6, + BatchWait: 5, + }, + HostMetadata: HostMetadataConfig{ + Enabled: true, + HostnameSource: HostnameSourceConfigOrSystem, + }, + OnlyMetadata: false, + }, + }, + { + id: component.NewIDWithName(ddtype, "api"), + expected: &Config{ + ClientConfig: defaultClientConfig(), + BackOffConfig: configretry.NewDefaultBackOffConfig(), + QueueSettings: exporterhelper.NewDefaultQueueSettings(), + TagsConfig: TagsConfig{ + Hostname: "customhostname", + }, + API: APIConfig{ + Key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + Site: "datadoghq.eu", + FailOnInvalidKey: true, + }, + Metrics: MetricsConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://api.datadoghq.eu", + }, + DeltaTTL: 3600, + HistConfig: HistogramConfig{ + Mode: "distributions", + SendAggregations: false, + }, + SumConfig: SumConfig{ + CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, + InitialCumulativeMonotonicMode: InitialValueModeAuto, + }, + SummaryConfig: SummaryConfig{ + Mode: SummaryModeGauges, + }, + }, + Traces: TracesExporterConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://trace.agent.datadoghq.eu", + }, + TracesConfig: TracesConfig{ + SpanNameRemappings: map[string]string{ + "old_name1": "new_name1", + "old_name2": "new_name2", + }, + SpanNameAsResourceName: true, + IgnoreResources: []string{}, + }, + TraceBuffer: 10, + }, + Logs: LogsConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://http-intake.logs.datadoghq.eu", + }, + UseCompression: true, + CompressionLevel: 6, + BatchWait: 5, + }, + OnlyMetadata: false, + HostMetadata: HostMetadataConfig{ + Enabled: true, + HostnameSource: HostnameSourceConfigOrSystem, + }, + }, + }, + { + id: component.NewIDWithName(ddtype, "api2"), + expected: &Config{ + ClientConfig: defaultClientConfig(), + BackOffConfig: configretry.NewDefaultBackOffConfig(), + QueueSettings: exporterhelper.NewDefaultQueueSettings(), + TagsConfig: TagsConfig{ + Hostname: "customhostname", + }, + API: APIConfig{ + Key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + Site: "datadoghq.eu", + FailOnInvalidKey: false, + }, + Metrics: MetricsConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://api.datadoghq.test", + }, + DeltaTTL: 3600, + HistConfig: HistogramConfig{ + Mode: "distributions", + SendAggregations: false, + }, + SumConfig: SumConfig{ + CumulativeMonotonicMode: CumulativeMonotonicSumModeToDelta, + InitialCumulativeMonotonicMode: InitialValueModeAuto, + }, + SummaryConfig: SummaryConfig{ + Mode: SummaryModeGauges, + }, + }, + Traces: TracesExporterConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://trace.agent.datadoghq.test", + }, + TracesConfig: TracesConfig{ + SpanNameRemappings: map[string]string{ + "old_name3": "new_name3", + "old_name4": "new_name4", + }, + IgnoreResources: []string{}, + }, + }, + Logs: LogsConfig{ + TCPAddrConfig: confignet.TCPAddrConfig{ + Endpoint: "https://http-intake.logs.datadoghq.test", + }, + UseCompression: true, + CompressionLevel: 6, + BatchWait: 5, + }, + HostMetadata: HostMetadataConfig{ + Enabled: true, + HostnameSource: HostnameSourceConfigOrSystem, + Tags: []string{"example:tag"}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.id.String(), func(t *testing.T) { + cfg := CreateDefaultConfig() + + sub, err := cm.Sub(tt.id.String()) + require.NoError(t, err) + require.NoError(t, sub.Unmarshal(cfg)) + + assert.NoError(t, component.ValidateConfig(cfg)) + assert.Equal(t, tt.expected, cfg) + }) + } +} + +func TestOverrideEndpoints(t *testing.T) { + tests := []struct { + componentID string + expectedSite string + expectedMetricsEndpoint string + expectedTracesEndpoint string + expectedLogsEndpoint string + }{ + { + componentID: "nositeandnoendpoints", + expectedSite: "datadoghq.com", + expectedMetricsEndpoint: "https://api.datadoghq.com", + expectedTracesEndpoint: "https://trace.agent.datadoghq.com", + expectedLogsEndpoint: "https://http-intake.logs.datadoghq.com", + }, + { + componentID: "nositeandmetricsendpoint", + expectedSite: "datadoghq.com", + expectedMetricsEndpoint: "metricsendpoint:1234", + expectedTracesEndpoint: "https://trace.agent.datadoghq.com", + expectedLogsEndpoint: "https://http-intake.logs.datadoghq.com", + }, + { + componentID: "nositeandtracesendpoint", + expectedSite: "datadoghq.com", + expectedMetricsEndpoint: "https://api.datadoghq.com", + expectedTracesEndpoint: "tracesendpoint:1234", + expectedLogsEndpoint: "https://http-intake.logs.datadoghq.com", + }, + { + componentID: "nositeandlogsendpoint", + expectedSite: "datadoghq.com", + expectedMetricsEndpoint: "https://api.datadoghq.com", + expectedTracesEndpoint: "https://trace.agent.datadoghq.com", + expectedLogsEndpoint: "logsendpoint:1234", + }, + { + componentID: "nositeandallendpoints", + expectedSite: "datadoghq.com", + expectedMetricsEndpoint: "metricsendpoint:1234", + expectedTracesEndpoint: "tracesendpoint:1234", + expectedLogsEndpoint: "logsendpoint:1234", + }, + + { + componentID: "siteandnoendpoints", + expectedSite: "datadoghq.eu", + expectedMetricsEndpoint: "https://api.datadoghq.eu", + expectedTracesEndpoint: "https://trace.agent.datadoghq.eu", + expectedLogsEndpoint: "https://http-intake.logs.datadoghq.eu", + }, + { + componentID: "siteandmetricsendpoint", + expectedSite: "datadoghq.eu", + expectedMetricsEndpoint: "metricsendpoint:1234", + expectedTracesEndpoint: "https://trace.agent.datadoghq.eu", + expectedLogsEndpoint: "https://http-intake.logs.datadoghq.eu", + }, + { + componentID: "siteandtracesendpoint", + expectedSite: "datadoghq.eu", + expectedMetricsEndpoint: "https://api.datadoghq.eu", + expectedTracesEndpoint: "tracesendpoint:1234", + expectedLogsEndpoint: "https://http-intake.logs.datadoghq.eu", + }, + { + componentID: "siteandallendpoints", + expectedSite: "datadoghq.eu", + expectedMetricsEndpoint: "metricsendpoint:1234", + expectedTracesEndpoint: "tracesendpoint:1234", + expectedLogsEndpoint: "logsendpoint:1234", + }, + } + + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "unmarshal.yaml")) + require.NoError(t, err) + + for _, testInstance := range tests { + t.Run(testInstance.componentID, func(t *testing.T) { + cfg := CreateDefaultConfig() + sub, err := cm.Sub(component.NewIDWithName(ddtype, testInstance.componentID).String()) + require.NoError(t, err) + require.NoError(t, sub.Unmarshal(cfg)) + + componentCfg, ok := cfg.(*Config) + require.True(t, ok, "component.Config is not a Datadog exporter config (wrong ID?)") + assert.Equal(t, testInstance.expectedSite, componentCfg.API.Site) + assert.Equal(t, testInstance.expectedMetricsEndpoint, componentCfg.Metrics.Endpoint) + assert.Equal(t, testInstance.expectedTracesEndpoint, componentCfg.Traces.Endpoint) + assert.Equal(t, testInstance.expectedLogsEndpoint, componentCfg.Logs.Endpoint) + }) + } +} diff --git a/exporter/datadogexporter/config_warnings.go b/pkg/datadog/config/config_warnings.go similarity index 95% rename from exporter/datadogexporter/config_warnings.go rename to pkg/datadog/config/config_warnings.go index 8197903d2285..0e5701c99522 100644 --- a/exporter/datadogexporter/config_warnings.go +++ b/pkg/datadog/config/config_warnings.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package datadogexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter" +package config // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" import ( "errors" diff --git a/exporter/datadogexporter/config_warnings_test.go b/pkg/datadog/config/config_warnings_test.go similarity index 96% rename from exporter/datadogexporter/config_warnings_test.go rename to pkg/datadog/config/config_warnings_test.go index 1a92c0486b4b..976ccdd5f45d 100644 --- a/exporter/datadogexporter/config_warnings_test.go +++ b/pkg/datadog/config/config_warnings_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package datadogexporter +package config // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" import ( "testing" @@ -77,8 +77,7 @@ func TestSendAggregations(t *testing.T) { for _, testInstance := range tests { t.Run(testInstance.name, func(t *testing.T) { - f := NewFactory() - cfg := f.CreateDefaultConfig().(*Config) + cfg := CreateDefaultConfig().(*Config) err := testInstance.cfgMap.Unmarshal(cfg) if err != nil || testInstance.err != "" { assert.ErrorContains(t, err, testInstance.err) @@ -92,7 +91,6 @@ func TestSendAggregations(t *testing.T) { } }) } - } func TestPeerTags(t *testing.T) { @@ -154,8 +152,7 @@ func TestPeerTags(t *testing.T) { for _, testInstance := range tests { t.Run(testInstance.name, func(t *testing.T) { - f := NewFactory() - cfg := f.CreateDefaultConfig().(*Config) + cfg := CreateDefaultConfig().(*Config) err := testInstance.cfgMap.Unmarshal(cfg) if err != nil || testInstance.err != "" { assert.ErrorContains(t, err, testInstance.err) @@ -169,5 +166,4 @@ func TestPeerTags(t *testing.T) { } }) } - } diff --git a/pkg/datadog/config/host.go b/pkg/datadog/config/host.go new file mode 100644 index 000000000000..06e3161fdcc7 --- /dev/null +++ b/pkg/datadog/config/host.go @@ -0,0 +1,87 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package config // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" + +import ( + "encoding" + "fmt" + "time" +) + +// HostnameSource is the source for the hostname of host metadata. +type HostnameSource string + +const ( + // HostnameSourceFirstResource picks the host metadata hostname from the resource + // attributes on the first OTLP payload that gets to the exporter. If it is lacking any + // hostname-like attributes, it will fallback to 'config_or_system' behavior (see below). + // + // Do not use this hostname source if receiving data from multiple hosts. + HostnameSourceFirstResource HostnameSource = "first_resource" + + // HostnameSourceConfigOrSystem picks the host metadata hostname from the 'hostname' setting, + // and if this is empty, from available system APIs and cloud provider endpoints. + HostnameSourceConfigOrSystem HostnameSource = "config_or_system" +) + +var _ encoding.TextUnmarshaler = (*HostnameSource)(nil) + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (sm *HostnameSource) UnmarshalText(in []byte) error { + switch mode := HostnameSource(in); mode { + case HostnameSourceFirstResource, + HostnameSourceConfigOrSystem: + *sm = mode + return nil + default: + return fmt.Errorf("invalid host metadata hostname source %q", mode) + } +} + +// HostMetadataConfig defines the host metadata related configuration. +// Host metadata is the information used for populating the infrastructure list, +// the host map and providing host tags functionality. +// +// The exporter will send host metadata for a single host, whose name is chosen +// according to `host_metadata::hostname_source`. +type HostMetadataConfig struct { + // Enabled enables the host metadata functionality. + Enabled bool `mapstructure:"enabled"` + + // HostnameSource is the source for the hostname of host metadata. + // This hostname is used for identifying the infrastructure list, host map and host tag information related to the host where the Datadog exporter is running. + // Changing this setting will not change the host used to tag your metrics, traces and logs in any way. + // For remote hosts, see https://docs.datadoghq.com/opentelemetry/schema_semantics/host_metadata/. + // + // Valid values are 'first_resource' and 'config_or_system': + // - 'first_resource' picks the host metadata hostname from the resource + // attributes on the first OTLP payload that gets to the exporter. + // If the first payload lacks hostname-like attributes, it will fallback to 'config_or_system'. + // **Do not use this hostname source if receiving data from multiple hosts**. + // - 'config_or_system' picks the host metadata hostname from the 'hostname' setting, + // If this is empty it will use available system APIs and cloud provider endpoints. + // + // The default is 'config_or_system'. + HostnameSource HostnameSource `mapstructure:"hostname_source"` + + // Tags is a list of host tags. + // These tags will be attached to telemetry signals that have the host metadata hostname. + // To attach tags to telemetry signals regardless of the host, use a processor instead. + Tags []string `mapstructure:"tags"` + + // sourceTimeout is the timeout to fetch from each provider - for example AWS IMDS. + // If unset, or set to zero duration, there will be no timeout applied. + // Default is no timeout. + sourceTimeout time.Duration +} + +// SetSourceTimeout sets the timeout to fetch from each provider - for example AWS IMDS. +func (h *HostMetadataConfig) SetSourceTimeout(t time.Duration) { + h.sourceTimeout = t +} + +// GetSourceTimeout returns the timeout to fetch from each provider - for example AWS IMDS. +func (h *HostMetadataConfig) GetSourceTimeout() time.Duration { + return h.sourceTimeout +} diff --git a/pkg/datadog/config/logs.go b/pkg/datadog/config/logs.go new file mode 100644 index 000000000000..b1d4cf5a577b --- /dev/null +++ b/pkg/datadog/config/logs.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package config // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" + +import "go.opentelemetry.io/collector/config/confignet" + +// LogsConfig defines logs exporter specific configuration +type LogsConfig struct { + // TCPAddr.Endpoint is the host of the Datadog intake server to send logs to. + // If unset, the value is obtained from the Site. + confignet.TCPAddrConfig `mapstructure:",squash"` + + // DumpPayloads report whether payloads should be dumped when logging level is debug. + // Note: this config option does not apply when the `exporter.datadogexporter.UseLogsAgentExporter` feature flag is enabled (now enabled by default). + // Deprecated: This config option is not supported in the Datadog Agent logs pipeline. + DumpPayloads bool `mapstructure:"dump_payloads"` + + // UseCompression enables the logs agent to compress logs before sending them. + // Note: this config option does not apply when the `exporter.datadogexporter.UseLogsAgentExporter` feature flag is disabled. + UseCompression bool `mapstructure:"use_compression"` + + // CompressionLevel accepts values from 0 (no compression) to 9 (maximum compression but higher resource usage). + // Only takes effect if UseCompression is set to true. + // Note: this config option does not apply when the `exporter.datadogexporter.UseLogsAgentExporter` feature flag is disabled. + CompressionLevel int `mapstructure:"compression_level"` + + // BatchWait represents the maximum time the logs agent waits to fill each batch of logs before sending. + // Note: this config option does not apply when the `exporter.datadogexporter.UseLogsAgentExporter` feature flag is disabled. + BatchWait int `mapstructure:"batch_wait"` +} diff --git a/pkg/datadog/config/metrics.go b/pkg/datadog/config/metrics.go new file mode 100644 index 000000000000..07b95beb4be6 --- /dev/null +++ b/pkg/datadog/config/metrics.go @@ -0,0 +1,208 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package config // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" + +import ( + "encoding" + "fmt" + + "go.opentelemetry.io/collector/config/confignet" +) + +// MetricsConfig defines the metrics exporter specific configuration options +type MetricsConfig struct { + // DeltaTTL defines the time that previous points of a cumulative monotonic + // metric are kept in memory to calculate deltas + DeltaTTL int64 `mapstructure:"delta_ttl"` + + // TCPAddr.Endpoint is the host of the Datadog intake server to send metrics to. + // If unset, the value is obtained from the Site. + confignet.TCPAddrConfig `mapstructure:",squash"` + + ExporterConfig MetricsExporterConfig `mapstructure:",squash"` + + // HistConfig defines the export of OTLP Histograms. + HistConfig HistogramConfig `mapstructure:"histograms"` + + // SumConfig defines the export of OTLP Sums. + SumConfig SumConfig `mapstructure:"sums"` + + // SummaryConfig defines the export for OTLP Summaries. + SummaryConfig SummaryConfig `mapstructure:"summaries"` +} + +type HistogramMode string + +const ( + // HistogramModeNoBuckets reports no bucket histogram metrics. .sum and .count metrics will still be sent + // if `send_count_sum_metrics` is enabled. + HistogramModeNoBuckets HistogramMode = "nobuckets" + // HistogramModeCounters reports histograms as Datadog counts, one metric per bucket. + HistogramModeCounters HistogramMode = "counters" + // HistogramModeDistributions reports histograms as Datadog distributions (recommended). + HistogramModeDistributions HistogramMode = "distributions" +) + +var _ encoding.TextUnmarshaler = (*HistogramMode)(nil) + +func (hm *HistogramMode) UnmarshalText(in []byte) error { + switch mode := HistogramMode(in); mode { + case HistogramModeCounters, HistogramModeDistributions, HistogramModeNoBuckets: + *hm = mode + return nil + default: + return fmt.Errorf("invalid histogram mode %q", mode) + } +} + +// HistogramConfig customizes export of OTLP Histograms. +type HistogramConfig struct { + // Mode for exporting histograms. Valid values are 'distributions', 'counters' or 'nobuckets'. + // - 'distributions' sends histograms as Datadog distributions (recommended). + // - 'counters' sends histograms as Datadog counts, one metric per bucket. + // - 'nobuckets' sends no bucket histogram metrics. Aggregation metrics will still be sent + // if `send_aggregation_metrics` is enabled. + // + // The current default is 'distributions'. + Mode HistogramMode `mapstructure:"mode"` + + // SendCountSum states if the export should send .sum and .count metrics for histograms. + // The default is false. + // Deprecated: [v0.75.0] Use `send_aggregation_metrics` (HistogramConfig.SendAggregations) instead. + SendCountSum bool `mapstructure:"send_count_sum_metrics"` + + // SendAggregations states if the exporter should send .sum, .count, .min and .max metrics for histograms. + // The default is false. + SendAggregations bool `mapstructure:"send_aggregation_metrics"` +} + +func (c *HistogramConfig) validate() error { + if c.Mode == HistogramModeNoBuckets && !c.SendAggregations { + return fmt.Errorf("'nobuckets' mode and `send_aggregation_metrics` set to false will send no histogram metrics") + } + return nil +} + +// CumulativeMonotonicSumMode is the export mode for OTLP Sum metrics. +type CumulativeMonotonicSumMode string + +const ( + // CumulativeMonotonicSumModeToDelta calculates delta for + // cumulative monotonic sum metrics in the client side and reports + // them as Datadog counts. + CumulativeMonotonicSumModeToDelta CumulativeMonotonicSumMode = "to_delta" + + // CumulativeMonotonicSumModeRawValue reports the raw value for + // cumulative monotonic sum metrics as a Datadog gauge. + CumulativeMonotonicSumModeRawValue CumulativeMonotonicSumMode = "raw_value" +) + +var _ encoding.TextUnmarshaler = (*CumulativeMonotonicSumMode)(nil) + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (sm *CumulativeMonotonicSumMode) UnmarshalText(in []byte) error { + switch mode := CumulativeMonotonicSumMode(in); mode { + case CumulativeMonotonicSumModeToDelta, + CumulativeMonotonicSumModeRawValue: + *sm = mode + return nil + default: + return fmt.Errorf("invalid cumulative monotonic sum mode %q", mode) + } +} + +// InitialValueMode defines what the exporter should do with the initial value +// of a time series when transforming from cumulative to delta. +type InitialValueMode string + +const ( + // InitialValueModeAuto reports the initial value if its start timestamp + // is set and it happens after the process was started. + InitialValueModeAuto InitialValueMode = "auto" + + // InitialValueModeDrop always drops the initial value. + InitialValueModeDrop InitialValueMode = "drop" + + // InitialValueModeKeep always reports the initial value. + InitialValueModeKeep InitialValueMode = "keep" +) + +var _ encoding.TextUnmarshaler = (*InitialValueMode)(nil) + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (iv *InitialValueMode) UnmarshalText(in []byte) error { + switch mode := InitialValueMode(in); mode { + case InitialValueModeAuto, + InitialValueModeDrop, + InitialValueModeKeep: + *iv = mode + return nil + default: + return fmt.Errorf("invalid initial value mode %q", mode) + } +} + +// SumConfig customizes export of OTLP Sums. +type SumConfig struct { + // CumulativeMonotonicMode is the mode for exporting OTLP Cumulative Monotonic Sums. + // Valid values are 'to_delta' or 'raw_value'. + // - 'to_delta' calculates delta for cumulative monotonic sums and sends it as a Datadog count. + // - 'raw_value' sends the raw value of cumulative monotonic sums as Datadog gauges. + // + // The default is 'to_delta'. + // See https://docs.datadoghq.com/metrics/otlp/?tab=sum#mapping for details and examples. + CumulativeMonotonicMode CumulativeMonotonicSumMode `mapstructure:"cumulative_monotonic_mode"` + + // InitialCumulativeMonotonicMode defines the behavior of the exporter when receiving the first value + // of a cumulative monotonic sum. + InitialCumulativeMonotonicMode InitialValueMode `mapstructure:"initial_cumulative_monotonic_value"` +} + +// SummaryMode is the export mode for OTLP Summary metrics. +type SummaryMode string + +const ( + // SummaryModeNoQuantiles sends no `.quantile` metrics. `.sum` and `.count` metrics will still be sent. + SummaryModeNoQuantiles SummaryMode = "noquantiles" + // SummaryModeGauges sends `.quantile` metrics as gauges tagged by the quantile. + SummaryModeGauges SummaryMode = "gauges" +) + +var _ encoding.TextUnmarshaler = (*SummaryMode)(nil) + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (sm *SummaryMode) UnmarshalText(in []byte) error { + switch mode := SummaryMode(in); mode { + case SummaryModeNoQuantiles, + SummaryModeGauges: + *sm = mode + return nil + default: + return fmt.Errorf("invalid summary mode %q", mode) + } +} + +// SummaryConfig customizes export of OTLP Summaries. +type SummaryConfig struct { + // Mode is the the mode for exporting OTLP Summaries. + // Valid values are 'noquantiles' or 'gauges'. + // - 'noquantiles' sends no `.quantile` metrics. `.sum` and `.count` metrics will still be sent. + // - 'gauges' sends `.quantile` metrics as gauges tagged by the quantile. + // + // The default is 'gauges'. + // See https://docs.datadoghq.com/metrics/otlp/?tab=summary#mapping for details and examples. + Mode SummaryMode `mapstructure:"mode"` +} + +// MetricsExporterConfig provides options for a user to customize the behavior of the +// metrics exporter +type MetricsExporterConfig struct { + // ResourceAttributesAsTags, if set to true, will use the exporterhelper feature to transform all + // resource attributes into metric labels, which are then converted into tags + ResourceAttributesAsTags bool `mapstructure:"resource_attributes_as_tags"` + + // InstrumentationScopeMetadataAsTags, if set to true, adds the name and version of the + // instrumentation scope that created a metric to the metric tags + InstrumentationScopeMetadataAsTags bool `mapstructure:"instrumentation_scope_metadata_as_tags"` +} diff --git a/pkg/datadog/config/testdata/config.yaml b/pkg/datadog/config/testdata/config.yaml new file mode 100644 index 000000000000..10fea4ec91c0 --- /dev/null +++ b/pkg/datadog/config/testdata/config.yaml @@ -0,0 +1,40 @@ +datadog/api: + hostname: customhostname + api: + key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + site: datadoghq.eu + fail_on_invalid_key: true + + traces: + span_name_remappings: + "old_name1": "new_name1" + "old_name2": "new_name2" + span_name_as_resource_name: true + trace_buffer: 10 + +datadog/api2: + hostname: customhostname + + host_metadata: + tags: [example:tag] + + api: + key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + site: datadoghq.eu + + metrics: + endpoint: https://api.datadoghq.test + + traces: + span_name_remappings: + "old_name3": "new_name3" + "old_name4": "new_name4" + endpoint: https://trace.agent.datadoghq.test + + logs: + endpoint: https://http-intake.logs.datadoghq.test + +datadog/default: + api: + key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + diff --git a/pkg/datadog/config/testdata/unmarshal.yaml b/pkg/datadog/config/testdata/unmarshal.yaml new file mode 100644 index 000000000000..b3c5b76036d4 --- /dev/null +++ b/pkg/datadog/config/testdata/unmarshal.yaml @@ -0,0 +1,53 @@ +datadog/nositeandnoendpoints: + +datadog/nositeandmetricsendpoint: + metrics: + endpoint: "metricsendpoint:1234" + +datadog/nositeandtracesendpoint: + traces: + endpoint: "tracesendpoint:1234" + +datadog/nositeandlogsendpoint: + logs: + endpoint: "logsendpoint:1234" + +datadog/nositeandallendpoints: + metrics: + endpoint: "metricsendpoint:1234" + traces: + endpoint: "tracesendpoint:1234" + logs: + endpoint: "logsendpoint:1234" + +datadog/siteandnoendpoints: + api: + site: datadoghq.eu + +datadog/siteandmetricsendpoint: + api: + site: datadoghq.eu + metrics: + endpoint: "metricsendpoint:1234" + +datadog/siteandtracesendpoint: + api: + site: datadoghq.eu + traces: + endpoint: "tracesendpoint:1234" + +datadog/siteandlogsendpoint: + api: + site: datadoghq.eu + logs: + endpoint: "logsendpoint:1234" + +datadog/siteandallendpoints: + api: + site: datadoghq.eu + metrics: + endpoint: "metricsendpoint:1234" + traces: + endpoint: "tracesendpoint:1234" + logs: + endpoint: "logsendpoint:1234" diff --git a/pkg/datadog/config/traces.go b/pkg/datadog/config/traces.go new file mode 100644 index 000000000000..1e338fa58a0f --- /dev/null +++ b/pkg/datadog/config/traces.go @@ -0,0 +1,167 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package config // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config" + +import ( + "fmt" + "regexp" + "time" + + "go.opentelemetry.io/collector/config/confignet" +) + +// TracesConfig defines the traces exporter specific configuration options +type TracesConfig struct { + // ignored resources + // A blacklist of regular expressions can be provided to disable certain traces based on their resource name + // all entries must be surrounded by double quotes and separated by commas. + // ignore_resources: ["(GET|POST) /healthcheck"] + IgnoreResources []string `mapstructure:"ignore_resources"` + + // 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. + // 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 + SpanNameRemappings map[string]string `mapstructure:"span_name_remappings"` + + // 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. + // The default value is `false`. + SpanNameAsResourceName bool `mapstructure:"span_name_as_resource_name"` + + // If set to true, enables an additional stats computation check on spans to see they have an eligible `span.kind` (server, consumer, client, producer). + // If enabled, a span with an eligible `span.kind` will have stats computed. If disabled, only top-level and measured spans will have stats computed. + // NOTE: For stats computed from OTel traces, only top-level spans are considered when this option is off. + // If you are sending OTel traces and want stats on non-top-level spans, this flag will need to be enabled. + // If you are sending OTel traces and do not want stats computed by span kind, you need to disable this flag and disable `compute_top_level_by_span_kind`. + ComputeStatsBySpanKind bool `mapstructure:"compute_stats_by_span_kind"` + + // If set to true, root spans and spans with a server or consumer `span.kind` will be marked as top-level. + // Additionally, spans with a client or producer `span.kind` will have stats computed. + // Enabling this config option may increase the number of spans that generate trace metrics, and may change which spans appear as top-level in Datadog. + // ComputeTopLevelBySpanKind needs to be enabled in both the Datadog connector and Datadog exporter configs if both components are being used. + // The default value is `false`. + ComputeTopLevelBySpanKind bool `mapstructure:"compute_top_level_by_span_kind"` + + // If set to true, enables `peer.service` aggregation in the exporter. If disabled, aggregated trace stats will not include `peer.service` as a dimension. + // For the best experience with `peer.service`, it is recommended to also enable `compute_stats_by_span_kind`. + // If enabling both causes the datadog exporter to consume too many resources, try disabling `compute_stats_by_span_kind` first. + // If the overhead remains high, it will be due to a high cardinality of `peer.service` values from the traces. You may need to check your instrumentation. + // Deprecated: Please use PeerTagsAggregation instead + PeerServiceAggregation bool `mapstructure:"peer_service_aggregation"` + + // If set to true, enables aggregation of peer related tags (e.g., `peer.service`, `db.instance`, etc.) in the datadog exporter. + // If disabled, aggregated trace stats will not include these tags as dimensions on trace metrics. + // For the best experience with peer tags, Datadog also recommends enabling `compute_stats_by_span_kind`. + // If you are using an OTel tracer, it's best to have both enabled because client/producer spans with relevant peer tags + // may not be marked by the datadog exporter as top-level spans. + // If enabling both causes the datadog exporter to consume too many resources, try disabling `compute_stats_by_span_kind` first. + // A high cardinality of peer tags or APM resources can also contribute to higher CPU and memory consumption. + // You can check for the cardinality of these fields by making trace search queries in the Datadog UI. + // The default list of peer tags can be found in https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/stats/concentrator.go. + PeerTagsAggregation bool `mapstructure:"peer_tags_aggregation"` + + // [BETA] Optional list of supplementary peer tags that go beyond the defaults. The Datadog backend validates all tags + // and will drop ones that are unapproved. The default set of peer tags can be found at + // https://github.com/DataDog/datadog-agent/blob/505170c4ac8c3cbff1a61cf5f84b28d835c91058/pkg/trace/stats/concentrator.go#L55. + PeerTags []string `mapstructure:"peer_tags"` +} + +func (c *TracesConfig) Validate() error { + if c.IgnoreResources != nil { + for _, entry := range c.IgnoreResources { + _, err := regexp.Compile(entry) + if err != nil { + return fmt.Errorf("'%s' is not valid resource filter regular expression", entry) + } + } + } + + if c.SpanNameRemappings != nil { + for key, value := range c.SpanNameRemappings { + if value == "" { + return fmt.Errorf("'%s' is not valid value for span name remapping", value) + } + if key == "" { + return fmt.Errorf("'%s' is not valid key for span name remapping", key) + } + } + } + return nil +} + +// TracesExporterConfig Traces configuration in DD exporter +type TracesExporterConfig struct { + // TCPAddr.Endpoint is the host of the Datadog intake server to send traces to. + // If unset, the value is obtained from the Site. + confignet.TCPAddrConfig `mapstructure:",squash"` + + // TracesConfig contains the common configuration for all traces. + TracesConfig `mapstructure:",squash"` + + // TraceBuffer specifies the number of Datadog Agent TracerPayloads to buffer before dropping. + // The default value is 0, meaning the Datadog Agent TracerPayloads are unbuffered. + TraceBuffer int `mapstructure:"trace_buffer"` + + // flushInterval defines the interval in seconds at which the writer flushes traces + // to the intake; used in tests. + flushInterval float64 +} + +// SetFlushInterval sets the interval in seconds at which the writer flushes traces +func (c *TracesExporterConfig) SetFlushInterval(interval float64) { + c.flushInterval = interval +} + +// GetFlushInterval returns the interval in seconds at which the writer flushes traces +func (c *TracesExporterConfig) GetFlushInterval() float64 { + return c.flushInterval +} + +func (c *TracesExporterConfig) Validate() error { + if err := c.TracesConfig.Validate(); err != nil { + return err + } + if c.TraceBuffer < 0 { + return fmt.Errorf("trace buffer must be non-negative") + } + + return nil +} + +// TracesConnectorConfig Traces configuration in DD connector +type TracesConnectorConfig struct { + // TracesConfig contains the common configuration for all traces. + TracesConfig `mapstructure:",squash"` + + // TraceBuffer specifies the number of Datadog Agent TracerPayloads to buffer before dropping. + // The default value is 1000. + TraceBuffer int `mapstructure:"trace_buffer"` + + // ResourceAttributesAsContainerTags specifies the list of resource attributes to be used as container tags. + ResourceAttributesAsContainerTags []string `mapstructure:"resource_attributes_as_container_tags"` + + // BucketInterval specifies the time interval size of aggregation buckets that aggregate the Datadog trace metrics. + // It is also the time interval that Datadog trace metrics payloads are flushed to the pipeline. + // If you are concerned about the metric volume generated by the Datadog connector and the resulting networking egress, try increasing bucket_interval. + // Default is 10s if unset. + BucketInterval time.Duration `mapstructure:"bucket_interval"` +} + +func (c *TracesConnectorConfig) Validate() error { + if err := c.TracesConfig.Validate(); err != nil { + return err + } + + if c.TraceBuffer < 0 { + return fmt.Errorf("trace buffer must be non-negative") + } + + if c.BucketInterval < 0 { + return fmt.Errorf("bucket interval must be non-negative") + } + return nil +} diff --git a/pkg/datadog/doc.go b/pkg/datadog/doc.go new file mode 100644 index 000000000000..0a7468491e3a --- /dev/null +++ b/pkg/datadog/doc.go @@ -0,0 +1,5 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package datadog contains the shared Datadog exporter and Datadog connector configuration. +package datadog // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog" diff --git a/pkg/datadog/go.mod b/pkg/datadog/go.mod new file mode 100644 index 000000000000..3afa812b7575 --- /dev/null +++ b/pkg/datadog/go.mod @@ -0,0 +1,83 @@ +module github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog + +go 1.22.0 + +require ( + github.com/DataDog/datadog-agent/pkg/util/hostname/validate v0.57.0 + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/collector/component v0.109.0 + go.opentelemetry.io/collector/config/configauth v0.109.0 + go.opentelemetry.io/collector/config/confighttp v0.109.0 + go.opentelemetry.io/collector/config/confignet v0.109.0 + go.opentelemetry.io/collector/config/configopaque v1.15.0 + go.opentelemetry.io/collector/config/configretry v1.15.0 + go.opentelemetry.io/collector/config/configtls v1.15.0 + go.opentelemetry.io/collector/confmap v1.15.0 + go.opentelemetry.io/collector/exporter v0.109.0 + go.uber.org/zap v1.27.0 +) + +require ( + github.com/DataDog/datadog-agent/pkg/util/log v0.57.0 // indirect + github.com/DataDog/datadog-agent/pkg/util/scrubber v0.57.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.1.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/knadh/koanf/maps v0.1.1 // indirect + github.com/knadh/koanf/providers/confmap v0.1.0 // indirect + github.com/knadh/koanf/v2 v2.1.1 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.20.2 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.57.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rs/cors v1.11.1 // indirect + go.opentelemetry.io/collector v0.109.0 // indirect + go.opentelemetry.io/collector/client v1.15.0 // indirect + go.opentelemetry.io/collector/config/configcompression v1.15.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.109.0 // indirect + go.opentelemetry.io/collector/config/internal v0.109.0 // indirect + go.opentelemetry.io/collector/consumer v0.109.0 // indirect + go.opentelemetry.io/collector/consumer/consumerprofiles v0.109.0 // indirect + go.opentelemetry.io/collector/extension v0.109.0 // indirect + go.opentelemetry.io/collector/extension/auth v0.109.0 // indirect + go.opentelemetry.io/collector/extension/experimental/storage v0.109.0 // indirect + go.opentelemetry.io/collector/featuregate v1.15.0 // indirect + go.opentelemetry.io/collector/pdata v1.15.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.109.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.51.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/sdk v1.29.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect + google.golang.org/grpc v1.66.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/pkg/datadog/go.sum b/pkg/datadog/go.sum new file mode 100644 index 000000000000..2f273264c860 --- /dev/null +++ b/pkg/datadog/go.sum @@ -0,0 +1,208 @@ +github.com/DataDog/datadog-agent/pkg/util/hostname/validate v0.57.0 h1:Yd3LQLXehMKqn1t8jaIYRNB4qJ0HvEN2C0VfyiuUXWQ= +github.com/DataDog/datadog-agent/pkg/util/hostname/validate v0.57.0/go.mod h1:hOSTTBSDZtpwElWFyLTbnN09ZEBsqYdplYC3jHnplm0= +github.com/DataDog/datadog-agent/pkg/util/log v0.57.0 h1:KvEk3AhTDgK7k/5qsXwlpu9hhyILnFlbRMlMrAXRbCA= +github.com/DataDog/datadog-agent/pkg/util/log v0.57.0/go.mod h1:hbnC1/6rgDpm4XglmLb+8RR5VejoxorxrQxXTnhpsws= +github.com/DataDog/datadog-agent/pkg/util/scrubber v0.57.0 h1:l90E3CLHb4UtjCeT/lkIPga588gBac+ZZvKdlNikf+g= +github.com/DataDog/datadog-agent/pkg/util/scrubber v0.57.0/go.mod h1:krOxbYZc4KKE7bdEDu10lLSQBjdeSFS/XDSclsaSf1Y= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= +github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= +github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= +github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= +github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= +github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.57.0 h1:Ro/rKjwdq9mZn1K5QPctzh+MA4Lp0BuYk5ZZEVhoNcY= +github.com/prometheus/common v0.57.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/collector v0.109.0 h1:ULnMWuwcy4ix1oP5RFFRcmpEbaU5YabW6nWcLMQQRo0= +go.opentelemetry.io/collector v0.109.0/go.mod h1:gheyquSOc5E9Y+xsPmpA+PBrpPc+msVsIalY76/ZvnQ= +go.opentelemetry.io/collector/client v1.15.0 h1:SMUKTntljRmFvB8nCVf6KjbEQ/qm63wi+huDx+Bc/po= +go.opentelemetry.io/collector/client v1.15.0/go.mod h1:m0MdKbzRIVgyGu70qbJ6TwBmKtblk7cmPqspM45a5yY= +go.opentelemetry.io/collector/component v0.109.0 h1:AU6eubP1htO8Fvm86uWn66Kw0DMSFhgcRM2cZZTYfII= +go.opentelemetry.io/collector/component v0.109.0/go.mod h1:jRVFY86GY6JZ61SXvUN69n7CZoTjDTqWyNC+wJJvzOw= +go.opentelemetry.io/collector/config/configauth v0.109.0 h1:6I2g1dcXD7KCmzXWHaL09I6RSmiCER4b+UARYkmMw3U= +go.opentelemetry.io/collector/config/configauth v0.109.0/go.mod h1:i36T9K3m7pLSlqMFdy+npY7JxfxSg3wQc8bHNpykLLE= +go.opentelemetry.io/collector/config/configcompression v1.15.0 h1:HHzus/ahJW2dA6h4S4vs1MwlbOck27Ivk/L3o0V94UA= +go.opentelemetry.io/collector/config/configcompression v1.15.0/go.mod h1:pnxkFCLUZLKWzYJvfSwZnPrnm0twX14CYj2ADth5xiU= +go.opentelemetry.io/collector/config/confighttp v0.109.0 h1:6R2+zI1LqFarEnCL4k+1DCsFi+aVeUTbfFOQBk0JBh0= +go.opentelemetry.io/collector/config/confighttp v0.109.0/go.mod h1:fzvAO2nCnP9XRUiaCBh1AZ2whUf99iQTkEVFCyH+URk= +go.opentelemetry.io/collector/config/confignet v0.109.0 h1:/sBkAzkNtVFLWb38bfgkmkJXIBi4idayDmP4xaA2BDk= +go.opentelemetry.io/collector/config/confignet v0.109.0/go.mod h1:o3v4joAEjvLwntqexg5ixMqRrU1+Vst+jWuCUaBNgOg= +go.opentelemetry.io/collector/config/configopaque v1.15.0 h1:J1rmPR1WGro7BNCgni3o+VDoyB7ZqH2/SG1YK+6ujCw= +go.opentelemetry.io/collector/config/configopaque v1.15.0/go.mod h1:6zlLIyOoRpJJ+0bEKrlZOZon3rOp5Jrz9fMdR4twOS4= +go.opentelemetry.io/collector/config/configretry v1.15.0 h1:4ZUPrWWh4wiwdlGnss2lZDhvf1xkt8uwHEqmuqovMEs= +go.opentelemetry.io/collector/config/configretry v1.15.0/go.mod h1:KvQF5cfphq1rQm1dKR4eLDNQYw6iI2fY72NMZVa+0N0= +go.opentelemetry.io/collector/config/configtelemetry v0.109.0 h1:ItbYw3tgFMU+TqGcDVEOqJLKbbOpfQg3AHD8b22ygl8= +go.opentelemetry.io/collector/config/configtelemetry v0.109.0/go.mod h1:R0MBUxjSMVMIhljuDHWIygzzJWQyZHXXWIgQNxcFwhc= +go.opentelemetry.io/collector/config/configtls v1.15.0 h1:imUIYDu6lo7juxxgpJhoMQ+LJRxqQzKvjOcWTo4u0IY= +go.opentelemetry.io/collector/config/configtls v1.15.0/go.mod h1:T3pOF5UemLzmYgY7QpiZuDRrihJ8lyXB0cDe6j1F1Ek= +go.opentelemetry.io/collector/config/internal v0.109.0 h1:uAlmO9Gu4Ff5wXXWWn+7XRZKEBjwGE8YdkdJxOlodns= +go.opentelemetry.io/collector/config/internal v0.109.0/go.mod h1:JJJGJTz1hILaaT+01FxbCFcDvPf2otXqMcWk/s2KvlA= +go.opentelemetry.io/collector/confmap v1.15.0 h1:KaNVG6fBJXNqEI+/MgZasH0+aShAU1yAkSYunk6xC4E= +go.opentelemetry.io/collector/confmap v1.15.0/go.mod h1:GrIZ12P/9DPOuTpe2PIS51a0P/ZM6iKtByVee1Uf3+k= +go.opentelemetry.io/collector/consumer v0.109.0 h1:fdXlJi5Rat/poHPiznM2mLiXjcv1gPy3fyqqeirri58= +go.opentelemetry.io/collector/consumer v0.109.0/go.mod h1:E7PZHnVe1DY9hYy37toNxr9/hnsO7+LmnsixW8akLQI= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.109.0 h1:+WZ6MEWQRC6so3IRrW916XK58rI9NnrFHKW/P19jQvc= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.109.0/go.mod h1:spZ9Dn1MRMPDHHThdXZA5TrFhdOL1wsl0Dw45EBVoVo= +go.opentelemetry.io/collector/consumer/consumertest v0.109.0 h1:v4w9G2MXGJ/eabCmX1DvQYmxzdysC8UqIxa/BWz7ACo= +go.opentelemetry.io/collector/consumer/consumertest v0.109.0/go.mod h1:lECt0qOrx118wLJbGijtqNz855XfvJv0xx9GSoJ8qSE= +go.opentelemetry.io/collector/exporter v0.109.0 h1:LsZ8/EB8cYvdgap3a8HWCEHYpVyH9A4d53Hy0W6n9KY= +go.opentelemetry.io/collector/exporter v0.109.0/go.mod h1:yk+qAB1ZJYoUYretkzbNt/onpy/VyQdTpPhvIbyh3Us= +go.opentelemetry.io/collector/exporter/exporterprofiles v0.109.0 h1:px+iViqF0JB6+COJL6cTSa0HLpJRNlPmFUA6zjOCKMk= +go.opentelemetry.io/collector/exporter/exporterprofiles v0.109.0/go.mod h1:Zs5z/fdsRN3v9mChU2aYNGzUAJgY+2D+T7ZRGiZ3lmY= +go.opentelemetry.io/collector/extension v0.109.0 h1:r/WkSCYGF1B/IpUgbrKTyJHcfn7+A5+mYfp5W7+B4U0= +go.opentelemetry.io/collector/extension v0.109.0/go.mod h1:WDE4fhiZnt2haxqSgF/2cqrr5H+QjgslN5tEnTBZuXc= +go.opentelemetry.io/collector/extension/auth v0.109.0 h1:yKUMCUG3IkjuOnHriNj0nqFU2DRdZn3Tvn9eqCI0eTg= +go.opentelemetry.io/collector/extension/auth v0.109.0/go.mod h1:wOIv49JhXIfol8CRmQvLve05ft3nZQUnTfcnuZKxdbo= +go.opentelemetry.io/collector/extension/experimental/storage v0.109.0 h1:kIJiOXHHBgMCvuDNA602dS39PJKB+ryiclLE3V5DIvM= +go.opentelemetry.io/collector/extension/experimental/storage v0.109.0/go.mod h1:6cGr7MxnF72lAiA7nbkSC8wnfIk+L9CtMzJWaaII9vs= +go.opentelemetry.io/collector/featuregate v1.15.0 h1:8KRWaZaE9hLlyMXnMTvnWtUJnzrBuTI0aLIvxqe8QP0= +go.opentelemetry.io/collector/featuregate v1.15.0/go.mod h1:47xrISO71vJ83LSMm8+yIDsUbKktUp48Ovt7RR6VbRs= +go.opentelemetry.io/collector/pdata v1.15.0 h1:q/T1sFpRKJnjDrUsHdJ6mq4uSqViR/f92yvGwDby/gY= +go.opentelemetry.io/collector/pdata v1.15.0/go.mod h1:2wcsTIiLAJSbqBq/XUUYbi+cP+N87d0jEJzmb9nT19U= +go.opentelemetry.io/collector/pdata/pprofile v0.109.0 h1:5lobQKeHk8p4WC7KYbzL6ZqqX3eSizsdmp5vM8pQFBs= +go.opentelemetry.io/collector/pdata/pprofile v0.109.0/go.mod h1:lXIifCdtR5ewO17JAYTUsclMqRp6h6dCowoXHhGyw8Y= +go.opentelemetry.io/collector/pdata/testdata v0.109.0 h1:gvIqy6juvqFET/6zi+zUOH1KZY/vtEDZW55u7gJ/hEo= +go.opentelemetry.io/collector/pdata/testdata v0.109.0/go.mod h1:zRttU/F5QMQ6ZXBMXCoSVG3EORTZLTK+UUS0VoMoT44= +go.opentelemetry.io/collector/receiver v0.109.0 h1:DTOM7xaDl7FUGQIjvjmWZn03JUE+aG4mJzWWfb7S8zw= +go.opentelemetry.io/collector/receiver v0.109.0/go.mod h1:jeiCHaf3PE6aXoZfHF5Uexg7aztu+Vkn9LVw0YDKm6g= +go.opentelemetry.io/collector/receiver/receiverprofiles v0.109.0 h1:KKzdIixE/XJWvqdCcNWAOtsEhNKu4waLKJjawjhnPLw= +go.opentelemetry.io/collector/receiver/receiverprofiles v0.109.0/go.mod h1:FKU+RFkSLWWB3tUUB6vifapZdFp1FoqVYVQ22jpHc8w= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/exporters/prometheus v0.51.0 h1:G7uexXb/K3T+T9fNLCCKncweEtNEBMTO+46hKX5EdKw= +go.opentelemetry.io/otel/exporters/prometheus v0.51.0/go.mod h1:v0mFe5Kk7woIh938mrZBJBmENYquyA0IICrlYm4Y0t4= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= +go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= +go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/versions.yaml b/versions.yaml index 55e036c82f04..27479bf26abd 100644 --- a/versions.yaml +++ b/versions.yaml @@ -148,6 +148,7 @@ module-sets: - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza + - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/azure - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/loki