Skip to content

Commit

Permalink
Add Datadog Exporter (#25035)
Browse files Browse the repository at this point in the history
* Add Datadog Exporter

* fix lint issues

* use logspipeline

* create module of datadogexporter

* fix lint issues

* update the license

* update the license

* add replace

* update the log source name

* use 0.98

* update replace statements

* run go mod
  • Loading branch information
dineshg13 authored May 6, 2024
1 parent 4bb1c7a commit 4e02f47
Show file tree
Hide file tree
Showing 14 changed files with 2,179 additions and 9 deletions.
437 changes: 437 additions & 0 deletions comp/otelcol/otlp/components/exporter/datadogexporter/config.go

Large diffs are not rendered by default.

353 changes: 353 additions & 0 deletions comp/otelcol/otlp/components/exporter/datadogexporter/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,353 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2021-present Datadog, Inc.

package datadogexporter

import (
"testing"
"time"

"github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/serializerexporter"
"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: serializerexporter.MetricsConfig{
HistConfig: serializerexporter.HistogramConfig{
Mode: "nobuckets",
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",
ProxyURL: "proxy",
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, proxy_url, 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 := NewFactory(nil, nil, nil).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
}{
{
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: "1 error(s) decoding:\n\n* error decoding 'metrics.sums.cumulative_monotonic_mode': invalid cumulative monotonic sum mode \"invalid_mode\"",
},
{
name: "invalid host metadata hostname source",
configMap: confmap.NewFromStringMap(map[string]any{
"host_metadata": map[string]any{
"hostname_source": "invalid_source",
},
}),
err: "1 error(s) decoding:\n\n* error decoding 'host_metadata.hostname_source': invalid host metadata hostname source \"invalid_source\"",
},
{
name: "invalid summary mode",
configMap: confmap.NewFromStringMap(map[string]any{
"metrics": map[string]any{
"summaries": map[string]any{
"mode": "invalid_mode",
},
},
}),
err: "1 error(s) decoding:\n\n* error decoding 'metrics.summaries.mode': invalid summary mode \"invalid_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 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: "1 error(s) decoding:\n\n* error decoding 'metrics.sums.initial_cumulative_monotonic_value': invalid initial value mode \"invalid_mode\"",
},
{
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,
},
}

f := NewFactory(nil, nil, nil)
for _, testInstance := range tests {
t.Run(testInstance.name, func(t *testing.T) {
cfg := f.CreateDefaultConfig().(*Config)
err := cfg.Unmarshal(testInstance.configMap)
if err != nil || testInstance.err != "" {
assert.EqualError(t, err, testInstance.err)
} else {
assert.Equal(t, testInstance.cfg, cfg)
}
})
}
}
Loading

0 comments on commit 4e02f47

Please sign in to comment.