-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add Datadog metrics exporter #900
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
168b319
Create initial outline for Datadog exporter (#1)
mx-psi 695430c
Fix field name error
mx-psi 58be9a4
Address linter
mx-psi e848a60
Bump collector version
mx-psi fdc98b5
Initial DogStatsD implementation (#15)
mx-psi 20afb0e
Refactor configuration (#45)
mx-psi c95adc4
Update dependencies and `make gofmt`
mx-psi a660b56
Add support for summary and distribution metric types (#65)
mx-psi c366603
Disable Queue and Retry settings (#72)
mx-psi 35c9fa8
Keep only DogStatsD parts
mx-psi addb478
Merge remote-tracking branch 'upstream/master' into dogstatsd
mx-psi 33d6662
Update core dependency and re-run style checks
mx-psi 5d5903d
Add some more tests to make CodeCov happier
mx-psi f67398a
Add final test for unspecified metrics
mx-psi 4b4564d
Address review comment
mx-psi 0d27616
Clarify comment on telemetry configuration
mx-psi a10e5cf
Further clarify comment
mx-psi f263c2d
Improve test coverage further
mx-psi 424ca8c
Merge remote-tracking branch 'upstream/master' into dogstatsd
mx-psi ed7d29d
Remove usage of pdatautil package
mx-psi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
include ../../Makefile.Common |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Datadog Exporter | ||
|
||
This exporter sends metric data to [Datadog](https://datadoghq.com) using DogStatsD. | ||
|
||
## Configuration | ||
|
||
There are no required settings. | ||
The hostname, environment, service and version can be set in the configuration for unified service tagging. | ||
|
||
See the sample configuration file under the `example` folder for all available options. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package datadogexporter | ||
|
||
import ( | ||
"fmt" | ||
|
||
"go.opentelemetry.io/collector/config/configmodels" | ||
) | ||
|
||
// DogStatsDConfig defines the DogStatsd related configuration | ||
type DogStatsDConfig struct { | ||
// Endpoint is the DogStatsD address. | ||
// The default value is 127.0.0.1:8125 | ||
// A Unix address is supported | ||
Endpoint string `mapstructure:"endpoint"` | ||
|
||
// Telemetry states whether to send internal telemetry metrics from the statsd client | ||
Telemetry bool `mapstructure:"telemetry"` | ||
mx-psi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// MetricsConfig defines the metrics exporter specific configuration options | ||
type MetricsConfig struct { | ||
// Namespace is the namespace under which the metrics are sent | ||
// By default metrics are not namespaced | ||
Namespace string `mapstructure:"namespace"` | ||
|
||
// Percentiles states whether to report percentiles for summary metrics, | ||
// including the minimum and maximum | ||
Percentiles bool `mapstructure:"report_percentiles"` | ||
|
||
// Buckets states whether to report buckets from distribution metrics | ||
Buckets bool `mapstructure:"report_buckets"` | ||
|
||
// DogStatsD defines the DogStatsD configuration options. | ||
DogStatsD DogStatsDConfig `mapstructure:"dogstatsd"` | ||
} | ||
|
||
// TagsConfig defines the tag-related configuration | ||
// It is embedded in the configuration | ||
type TagsConfig struct { | ||
// Hostname is the host name for unified service tagging. | ||
// If unset, it is determined automatically. | ||
// See https://docs.datadoghq.com/agent/faq/how-datadog-agent-determines-the-hostname | ||
// for more details. | ||
Hostname string `mapstructure:"hostname"` | ||
|
||
// Env is the environment for unified service tagging. | ||
// It can also be set through the `DD_ENV` environment variable. | ||
Env string `mapstructure:"env"` | ||
|
||
// Service is the service for unified service tagging. | ||
// It can also be set through the `DD_SERVICE` environment variable. | ||
Service string `mapstructure:"service"` | ||
|
||
// Version is the version for unified service tagging. | ||
// It can also be set through the `DD_VERSION` version variable. | ||
Version string `mapstructure:"version"` | ||
|
||
// Tags is the list of default tags to add to every metric or trace. | ||
Tags []string `mapstructure:"tags"` | ||
} | ||
|
||
// GetTags gets the default tags extracted from the configuration | ||
func (t *TagsConfig) GetTags() []string { | ||
tags := make([]string, 0, 4) | ||
|
||
if t.Hostname != "" { | ||
tags = append(tags, fmt.Sprintf("host:%s", t.Hostname)) | ||
} | ||
|
||
if t.Env != "" { | ||
tags = append(tags, fmt.Sprintf("env:%s", t.Env)) | ||
} | ||
|
||
if t.Service != "" { | ||
tags = append(tags, fmt.Sprintf("service:%s", t.Service)) | ||
} | ||
|
||
if t.Version != "" { | ||
tags = append(tags, fmt.Sprintf("version:%s", t.Version)) | ||
} | ||
|
||
if len(t.Tags) > 0 { | ||
tags = append(tags, t.Tags...) | ||
} | ||
|
||
return tags | ||
} | ||
|
||
// Config defines configuration for the Datadog exporter. | ||
type Config struct { | ||
configmodels.ExporterSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct. | ||
|
||
TagsConfig `mapstructure:",squash"` | ||
|
||
// Metrics defines the Metrics exporter specific configuration | ||
Metrics MetricsConfig `mapstructure:"metrics"` | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package datadogexporter | ||
|
||
import ( | ||
"path" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.opentelemetry.io/collector/component/componenttest" | ||
"go.opentelemetry.io/collector/config/configmodels" | ||
"go.opentelemetry.io/collector/config/configtest" | ||
) | ||
|
||
// TestLoadConfig tests that the configuration is loaded correctly | ||
func TestLoadConfig(t *testing.T) { | ||
factories, err := componenttest.ExampleComponents() | ||
assert.NoError(t, err) | ||
|
||
factory := NewFactory() | ||
factories.Exporters[typeStr] = factory | ||
cfg, err := configtest.LoadConfigFile(t, path.Join(".", "testdata", "config.yaml"), factories) | ||
|
||
require.NoError(t, err) | ||
require.NotNil(t, cfg) | ||
|
||
apiConfig := cfg.Exporters["datadog/dogstatsd"].(*Config) | ||
|
||
assert.Equal(t, apiConfig, &Config{ | ||
ExporterSettings: configmodels.ExporterSettings{ | ||
NameVal: "datadog/dogstatsd", | ||
TypeVal: "datadog", | ||
}, | ||
|
||
Metrics: MetricsConfig{ | ||
Percentiles: true, | ||
|
||
DogStatsD: DogStatsDConfig{ | ||
Endpoint: "127.0.0.1:8125", | ||
Telemetry: true, | ||
}, | ||
}, | ||
}) | ||
|
||
dogstatsdConfig := cfg.Exporters["datadog/dogstatsd/config"].(*Config) | ||
|
||
assert.Equal(t, dogstatsdConfig, &Config{ | ||
ExporterSettings: configmodels.ExporterSettings{ | ||
NameVal: "datadog/dogstatsd/config", | ||
TypeVal: "datadog", | ||
}, | ||
|
||
TagsConfig: TagsConfig{ | ||
Hostname: "customhostname", | ||
Env: "prod", | ||
Service: "myservice", | ||
Version: "myversion", | ||
Tags: []string{"example:tag"}, | ||
}, | ||
|
||
Metrics: MetricsConfig{ | ||
Namespace: "opentelemetry", | ||
Percentiles: false, | ||
Buckets: true, | ||
DogStatsD: DogStatsDConfig{ | ||
Endpoint: "localhost:5000", | ||
Telemetry: false, | ||
}, | ||
}, | ||
}) | ||
|
||
} | ||
|
||
func TestTags(t *testing.T) { | ||
tc := TagsConfig{ | ||
Hostname: "customhost", | ||
Env: "customenv", | ||
Service: "customservice", | ||
Version: "customversion", | ||
Tags: []string{"key1:val1", "key2:val2"}, | ||
} | ||
|
||
assert.Equal(t, | ||
tc.GetTags(), | ||
[]string{ | ||
"host:customhost", | ||
"env:customenv", | ||
"service:customservice", | ||
"version:customversion", | ||
"key1:val1", | ||
"key2:val2", | ||
}, | ||
) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package datadogexporter | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/DataDog/datadog-go/statsd" | ||
"go.opentelemetry.io/collector/consumer/pdata" | ||
"go.opentelemetry.io/collector/exporter/exporterhelper" | ||
"go.opentelemetry.io/collector/translator/internaldata" | ||
"go.uber.org/zap" | ||
) | ||
|
||
type dogStatsDExporter struct { | ||
logger *zap.Logger | ||
cfg *Config | ||
client *statsd.Client | ||
} | ||
|
||
func newDogStatsDExporter(logger *zap.Logger, cfg *Config) (*dogStatsDExporter, error) { | ||
|
||
options := []statsd.Option{ | ||
statsd.WithNamespace(cfg.Metrics.Namespace), | ||
statsd.WithTags(cfg.TagsConfig.GetTags()), | ||
} | ||
|
||
if !cfg.Metrics.DogStatsD.Telemetry { | ||
options = append(options, statsd.WithoutTelemetry()) | ||
} | ||
|
||
client, err := statsd.New( | ||
cfg.Metrics.DogStatsD.Endpoint, | ||
options..., | ||
) | ||
|
||
if err != nil { | ||
return nil, fmt.Errorf("failed to initialize DogStatsD client: %s", err) | ||
} | ||
|
||
return &dogStatsDExporter{logger, cfg, client}, nil | ||
} | ||
|
||
func (exp *dogStatsDExporter) PushMetricsData(_ context.Context, md pdata.Metrics) (int, error) { | ||
data := internaldata.MetricsToOC(md) | ||
metrics, droppedTimeSeries := MapMetrics(exp, data) | ||
|
||
for name, data := range metrics { | ||
for _, metric := range data { | ||
|
||
tags := metric.GetTags() | ||
|
||
// Send the hostname if it has not been overridden | ||
if exp.GetConfig().Hostname == "" && metric.GetHost() != "" { | ||
tags = append(tags, fmt.Sprintf("host:%s", metric.GetHost())) | ||
} | ||
|
||
var err error | ||
switch metric.GetType() { | ||
case Gauge: | ||
err = exp.client.Gauge(name, metric.GetValue(), tags, metric.GetRate()) | ||
} | ||
|
||
if err != nil { | ||
exp.GetLogger().Warn("Could not send metric to statsd", zap.String("metric", name), zap.Error(err)) | ||
} | ||
} | ||
} | ||
|
||
return droppedTimeSeries, nil | ||
} | ||
|
||
func (exp *dogStatsDExporter) GetLogger() *zap.Logger { | ||
return exp.logger | ||
} | ||
|
||
func (exp *dogStatsDExporter) GetConfig() *Config { | ||
return exp.cfg | ||
} | ||
|
||
func (exp *dogStatsDExporter) GetQueueSettings() exporterhelper.QueueSettings { | ||
return exporterhelper.QueueSettings{Enabled: false} | ||
} | ||
|
||
func (exp *dogStatsDExporter) GetRetrySettings() exporterhelper.RetrySettings { | ||
return exporterhelper.RetrySettings{Enabled: false} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use the https://github.com/open-telemetry/opentelemetry-collector/blob/master/config/confignet/confignet.go#L44 or https://github.com/open-telemetry/opentelemetry-collector/blob/master/config/confignet/confignet.go#L22?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
statsd
library picks up which protocol to use automatically (udp
orunixgram
) based on theendpoint
field shape, so thetransport
field would have to be ignored in some cases (in particular, ifendpoint
starts withunix://
it's considered UDS, and if certain environment variables are set it would be UDP). Would that behavior be okay you? Otherwise we would have to leave it as it is right now.