Skip to content
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

[datadogexporter] Use resource attributes for metadata and generated metrics #2023

Merged
merged 12 commits into from
Jan 29, 2021
15 changes: 12 additions & 3 deletions exporter/datadogexporter/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (

var (
errUnsetAPIKey = errors.New("api.key is not set")
errNoMetadata = errors.New("only_metadata can't be enabled when send_metadata is disabled")
errNoMetadata = errors.New("only_metadata can't be enabled when send_metadata or use_resource_metadata is disabled")
)

const (
Expand Down Expand Up @@ -166,11 +166,20 @@ type Config struct {
// 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
// when telemetry data is reported via a different host.
//
// This flag is incompatible with disabling `send_metadata`
// or `use_resource_metadata`.
OnlyMetadata bool `mapstructure:"only_metadata"`

// UseResourceMetadata defines whether to use resource attributes
// for completing host metadata (such as the hostname or host tags).
//
// By default this is true: the first resource attribute getting to
// the exporter will be used for host metadata.
// Disable this in the Collector if you are using an agent-collector setup.
UseResourceMetadata bool `mapstructure:"use_resource_metadata"`

// onceMetadata ensures only one exporter (metrics/traces) sends host metadata
onceMetadata sync.Once
}
Expand All @@ -185,7 +194,7 @@ func (c *Config) Sanitize() error {
c.TagsConfig.Env = "none"
}

if c.OnlyMetadata && !c.SendMetadata {
if c.OnlyMetadata && (!c.SendMetadata || !c.UseResourceMetadata) {
return errNoMetadata
}

Expand Down
49 changes: 26 additions & 23 deletions exporter/datadogexporter/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ func createDefaultConfig() configmodels.Exporter {
},
},

SendMetadata: true,
SendMetadata: true,
UseResourceMetadata: true,
}
}

Expand All @@ -99,23 +100,24 @@ func createMetricsExporter(
return nil, err
}

ctx, cancel := context.WithCancel(ctx)
var pushMetricsFn exporterhelper.PushMetrics

if cfg.OnlyMetadata {
pushMetricsFn = func(context.Context, pdata.Metrics) (int, error) {
// if only sending metadata ignore all metrics
pushMetricsFn = func(_ context.Context, md pdata.Metrics) (int, error) {
mx-psi marked this conversation as resolved.
Show resolved Hide resolved
// only sending metadata use only metrics
once := cfg.OnceMetadata()
once.Do(func() {
attrs := pdata.NewAttributeMap()
if md.ResourceMetrics().Len() > 0 {
mx-psi marked this conversation as resolved.
Show resolved Hide resolved
attrs = md.ResourceMetrics().At(0).Resource().Attributes()
}
go metadata.Pusher(ctx, params, cfg, attrs)
})
return 0, nil
}
} else {
pushMetricsFn = newMetricsExporter(params, cfg).PushMetricsData
}

ctx, cancel := context.WithCancel(ctx)
if cfg.SendMetadata {
once := cfg.OnceMetadata()
once.Do(func() {
go metadata.Pusher(ctx, params, cfg)
})
pushMetricsFn = newMetricsExporter(ctx, params, cfg).PushMetricsData
}

return exporterhelper.NewMetricsExporter(
Expand Down Expand Up @@ -148,23 +150,24 @@ func createTraceExporter(
return nil, err
}

ctx, cancel := context.WithCancel(ctx)
var pushTracesFn exporterhelper.PushTraces

if cfg.OnlyMetadata {
pushTracesFn = func(context.Context, pdata.Traces) (int, error) {
// if only sending metadata, ignore all traces
pushTracesFn = func(_ context.Context, td pdata.Traces) (int, error) {
// only sending metadata, use only attributes
once := cfg.OnceMetadata()
once.Do(func() {
attrs := pdata.NewAttributeMap()
if td.ResourceSpans().Len() > 0 {
attrs = td.ResourceSpans().At(0).Resource().Attributes()
}
go metadata.Pusher(ctx, params, cfg, attrs)
})
return 0, nil
}
} else {
pushTracesFn = newTraceExporter(params, cfg).pushTraceData
}

ctx, cancel := context.WithCancel(ctx)
if cfg.SendMetadata {
once := cfg.OnceMetadata()
once.Do(func() {
go metadata.Pusher(ctx, params, cfg)
})
pushTracesFn = newTraceExporter(ctx, params, cfg).pushTraceData
}

return exporterhelper.NewTraceExporter(
Expand Down
30 changes: 18 additions & 12 deletions exporter/datadogexporter/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ func TestCreateDefaultConfig(t *testing.T) {
EnvVarTags: "$DD_TAGS",
},

SendMetadata: true,
OnlyMetadata: false,
SendMetadata: true,
OnlyMetadata: false,
UseResourceMetadata: true,
}, cfg, "failed to create default config")

assert.NoError(t, configcheck.ValidateConfig(cfg))
Expand Down Expand Up @@ -132,8 +133,9 @@ func TestLoadConfig(t *testing.T) {
Endpoint: "https://trace.agent.datadoghq.eu",
},
},
SendMetadata: true,
OnlyMetadata: false,
SendMetadata: true,
OnlyMetadata: false,
UseResourceMetadata: true,
}, apiConfig)

defaultConfig := cfg.Exporters["datadog/default"].(*config.Config)
Expand Down Expand Up @@ -173,8 +175,9 @@ func TestLoadConfig(t *testing.T) {
Endpoint: "https://trace.agent.datadoghq.com",
},
},
SendMetadata: true,
OnlyMetadata: false,
SendMetadata: true,
OnlyMetadata: false,
UseResourceMetadata: true,
}, defaultConfig)

invalidConfig := cfg.Exporters["datadog/invalid"].(*config.Config)
Expand Down Expand Up @@ -256,8 +259,9 @@ func TestLoadConfigEnvVariables(t *testing.T) {
Endpoint: "https://trace.agent.datadoghq.test",
},
},
SendMetadata: true,
OnlyMetadata: false,
SendMetadata: true,
OnlyMetadata: false,
UseResourceMetadata: true,
}, apiConfig)

defaultConfig := cfg.Exporters["datadog/default2"].(*config.Config)
Expand Down Expand Up @@ -300,8 +304,9 @@ func TestLoadConfigEnvVariables(t *testing.T) {
Endpoint: "https://trace.agent.datadoghq.com",
},
},
SendMetadata: true,
OnlyMetadata: false,
SendMetadata: true,
OnlyMetadata: false,
UseResourceMetadata: true,
}, defaultConfig)
}

Expand Down Expand Up @@ -385,8 +390,9 @@ func TestOnlyMetadata(t *testing.T) {
Key: "notnull",
Site: "example.com",
},
SendMetadata: true,
OnlyMetadata: true,
SendMetadata: true,
OnlyMetadata: true,
UseResourceMetadata: true,
}

expTraces, err := factory.CreateTracesExporter(
Expand Down
30 changes: 29 additions & 1 deletion exporter/datadogexporter/metadata/ec2/ec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package ec2

import (
"fmt"
"strings"

"github.com/aws/aws-sdk-go/aws/ec2metadata"
Expand All @@ -23,11 +24,15 @@ import (
"go.uber.org/zap"
)

var defaultPrefixes = [3]string{"ip-", "domu", "ec2amaz-"}
var (
defaultPrefixes = [3]string{"ip-", "domu", "ec2amaz-"}
ec2TagPrefix = "ec2.tag."
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to match the prefix defined in #1899, see here

)

type HostInfo struct {
InstanceID string
EC2Hostname string
EC2Tags []string
}

// isDefaultHostname checks if a hostname is an EC2 default
Expand Down Expand Up @@ -95,3 +100,26 @@ func HostnameFromAttributes(attrs pdata.AttributeMap) (string, bool) {

return "", false
}

// HostInfoFromAttributes gets EC2 host info from attributes following
// OpenTelemetry semantic conventions
func HostInfoFromAttributes(attrs pdata.AttributeMap) (hostInfo *HostInfo) {
hostInfo = &HostInfo{}

if hostID, ok := attrs.Get(conventions.AttributeHostID); ok {
hostInfo.InstanceID = hostID.StringVal()
}

if hostName, ok := attrs.Get(conventions.AttributeHostName); ok {
hostInfo.EC2Hostname = hostName.StringVal()
}

attrs.ForEach(func(k string, v pdata.AttributeValue) {
if strings.HasPrefix(k, ec2TagPrefix) {
tag := fmt.Sprintf("%s:%s", strings.TrimPrefix(k, ec2TagPrefix), v.StringVal())
hostInfo.EC2Tags = append(hostInfo.EC2Tags, tag)
}
})

return
}
17 changes: 17 additions & 0 deletions exporter/datadogexporter/metadata/ec2/ec2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,20 @@ func TestHostnameFromAttributes(t *testing.T) {
assert.True(t, ok)
assert.Equal(t, hostname, testInstanceID)
}

func TestHostInfoFromAttributes(t *testing.T) {
attrs := testutils.NewAttributeMap(map[string]string{
conventions.AttributeCloudProvider: conventions.AttributeCloudProviderAWS,
conventions.AttributeHostID: testInstanceID,
conventions.AttributeHostName: testIP,
"ec2.tag.tag1": "val1",
"ec2.tag.tag2": "val2",
"ignored": "ignored",
})

hostInfo := HostInfoFromAttributes(attrs)

assert.Equal(t, hostInfo.InstanceID, testInstanceID)
assert.Equal(t, hostInfo.EC2Hostname, testIP)
assert.Equal(t, hostInfo.EC2Tags, []string{"tag1:val1", "tag2:val2"})
}
Loading