Skip to content

Commit 9b4caa8

Browse files
songy23mx-psi
andauthored
[connector/datadog] Add trace configs that mirror datadog exporter (#30787)
Co-authored-by: Pablo Baeyens <[email protected]>
1 parent 2f55fec commit 9b4caa8

File tree

13 files changed

+1027
-26
lines changed

13 files changed

+1027
-26
lines changed

.chloggen/dd-connector-configs.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: datadogconnector
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add trace configs that mirror datadog exporter
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [30787]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext: |
19+
ignore_resources: disable certain traces based on their resource name
20+
span_name_remappings: map of datadog span names and preferred name to map to
21+
span_name_as_resource_name: use OTLP span name as datadog operation name
22+
compute_stats_by_span_kind: enables an additional stats computation check based on span kind
23+
peer_tags_aggregation: enables aggregation of peer related tags
24+
trace_buffer: specifies the buffer size for datadog trace payloads
25+
26+
# If your change doesn't affect end users or the exported elements of any package,
27+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
28+
# Optional: The change log or logs in which this entry should be included.
29+
# e.g. '[user]' or '[user, api]'
30+
# Include 'user' if the change is relevant to end users.
31+
# Include 'api' if there is a change to a library API.
32+
# Default: '[user]'
33+
change_logs: []

connector/datadogconnector/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,66 @@ service:
106106
107107
Here we have two traces pipelines that ingest the same data but one is being sampled. The one that is sampled has its data sent to the datadog backend for you to see the sampled subset of the total traces sent across. The other non-sampled pipeline of traces sends its data to the metrics pipeline to be used in the APM stats. This unsampled pipeline gives the full picture of how much data the application emits in traces.
108108
109+
## Configurations
110+
111+
```yaml
112+
connectors:
113+
datadog/connector:
114+
traces:
115+
## @param ignore_resources - list of strings - optional
116+
## A blacklist of regular expressions can be provided to disable certain traces based on their resource name
117+
## all entries must be surrounded by double quotes and separated by commas.
118+
#
119+
# ignore_resources: ["(GET|POST) /healthcheck"]
120+
121+
## @param span_name_remappings - map of key/value pairs - optional
122+
## A map of Datadog span operation name keys and preferred name valuues to update those names to. This can be used to
123+
## automatically map Datadog Span Operation Names to an updated value, and is useful when a user wants to
124+
## shorten or modify span names to something more user friendly in the case of instrumentation libraries with
125+
## particularly verbose names.
126+
#
127+
# span_name_remappings:
128+
# io.opentelemetry.javaagent.spring.client: spring.client
129+
# instrumentation:express.server: express
130+
# go.opentelemetry.io_contrib_instrumentation_net_http_otelhttp.client: http.client
131+
132+
## @param span_name_as_resource_name - use OpenTelemetry semantic convention for span naming - optional
133+
## Option created to maintain similarity with the OpenTelemetry semantic conventions as discussed in the issue below.
134+
## https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
135+
## https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/1909
136+
#
137+
# span_name_as_resource_name: true
138+
139+
## @param compute_stats_by_span_kind - enables APM stats computation based on `span.kind` - optional
140+
## If set to true, enables an additional stats computation check on spans to see they have an eligible `span.kind` (server, consumer, client, producer).
141+
## 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.
142+
## NOTE: For stats computed from OTel traces, only top-level spans are considered when this option is off.
143+
#
144+
# compute_stats_by_span_kind: true
145+
146+
## @param peer_tags_aggregation - enables aggregation of peer related tags in Datadog exporter - optional
147+
## If set to true, enables aggregation of peer related tags (e.g., `peer.service`, `db.instance`, etc.) in Datadog exporter.
148+
## If disabled, aggregated trace stats will not include these tags as dimensions on trace metrics.
149+
## For the best experience with peer tags, Datadog also recommends enabling `compute_stats_by_span_kind`.
150+
## If you are using an OTel tracer, it's best to have both enabled because client/producer spans with relevant peer tags
151+
## may not be marked by Datadog exporter as top-level spans.
152+
## If enabling both causes Datadog exporter to consume too many resources, try disabling `compute_stats_by_span_kind` first.
153+
## A high cardinality of peer tags or APM resources can also contribute to higher CPU and memory consumption.
154+
## You can check for the cardinality of these fields by making trace search queries in the Datadog UI.
155+
## The default list of peer tags can be found in https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/stats/concentrator.go.
156+
#
157+
# peer_tags_aggregation: false
158+
159+
## @param trace_buffer - specifies the number of outgoing trace payloads to buffer before dropping - optional
160+
## If unset, the default value is 1000.
161+
## If you start seeing log messages like `Payload in channel full. Dropped 1 payload.` in the datadog exporter, consider
162+
## setting a higher `trace_buffer` to avoid traces being dropped.
163+
#
164+
# trace_buffer: 1000
165+
```
166+
167+
**NOTE**: `compute_stats_by_span_kind` and `peer_tags_aggregation` only work when the feature gate `connector.datadogconnector.performance` is enabled. See below for details on this feature gate.
168+
109169
## Feature Gate for Performance
110170

111171
In case you are experiencing high memory usage with Datadog Connector, similar to [issue](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/29755), use the feature gate `connector.datadogconnector.performance`. With the feature gate enabled, Datadog Connector takes OTLP traces and produces OTLP metric with the name `dd.internal.stats.payload`. This Metric has an attribute `dd.internal.stats.payload` that contains the bytes for StatsPayload. With the feature gate, we can use Datadog Connector only in conjunction with Datadog Exporter. Please enable the feature only if needed for performance reasons and higher throughput. Enable the feature gate on all collectors (especially in gateway deployment) in the pipeline that sends data to Datadog. We plan to refactor this component in the future so that the signals produced are usable in any metrics pipeline.

connector/datadogconnector/config.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package datadogconnector // import "github.com/open-telemetry/opentelemetry-collector-contrib/connector/datadogconnector"
5+
6+
import (
7+
"fmt"
8+
"regexp"
9+
10+
"go.opentelemetry.io/collector/component"
11+
)
12+
13+
var _ component.Config = (*Config)(nil)
14+
15+
// Config defines configuration for the Datadog connector.
16+
type Config struct {
17+
// Traces defines the Traces specific configuration
18+
Traces TracesConfig `mapstructure:"traces"`
19+
}
20+
21+
// TracesConfig defines the traces specific configuration options
22+
type TracesConfig struct {
23+
// ignored resources
24+
// A blocklist of regular expressions can be provided to disable certain traces based on their resource name
25+
// all entries must be surrounded by double quotes and separated by commas.
26+
// ignore_resources: ["(GET|POST) /healthcheck"]
27+
IgnoreResources []string `mapstructure:"ignore_resources"`
28+
29+
// SpanNameRemappings is the map of datadog span names and preferred name to map to. This can be used to
30+
// automatically map Datadog Span Operation Names to an updated value. All entries should be key/value pairs.
31+
// span_name_remappings:
32+
// io.opentelemetry.javaagent.spring.client: spring.client
33+
// instrumentation:express.server: express
34+
// go.opentelemetry.io_contrib_instrumentation_net_http_otelhttp.client: http.client
35+
SpanNameRemappings map[string]string `mapstructure:"span_name_remappings"`
36+
37+
// If set to true the OpenTelemetry span name will used in the Datadog resource name.
38+
// If set to false the resource name will be filled with the instrumentation library name + span kind.
39+
// The default value is `false`.
40+
SpanNameAsResourceName bool `mapstructure:"span_name_as_resource_name"`
41+
42+
// If set to true, enables an additional stats computation check on spans to see they have an eligible `span.kind` (server, consumer, client, producer).
43+
// 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.
44+
// NOTE: For stats computed from OTel traces, only top-level spans are considered when this option is off.
45+
ComputeStatsBySpanKind bool `mapstructure:"compute_stats_by_span_kind"`
46+
47+
// If set to true, enables aggregation of peer related tags (e.g., `peer.service`, `db.instance`, etc.) in the datadog connector.
48+
// If disabled, aggregated trace stats will not include these tags as dimensions on trace metrics.
49+
// For the best experience with peer tags, Datadog also recommends enabling `compute_stats_by_span_kind`.
50+
// If you are using an OTel tracer, it's best to have both enabled because client/producer spans with relevant peer tags
51+
// may not be marked by the datadog connector as top-level spans.
52+
// If enabling both causes the datadog connector to consume too many resources, try disabling `compute_stats_by_span_kind` first.
53+
// A high cardinality of peer tags or APM resources can also contribute to higher CPU and memory consumption.
54+
// You can check for the cardinality of these fields by making trace search queries in the Datadog UI.
55+
// The default list of peer tags can be found in https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/stats/concentrator.go.
56+
PeerTagsAggregation bool `mapstructure:"peer_tags_aggregation"`
57+
58+
// TraceBuffer specifies the number of Datadog Agent TracerPayloads to buffer before dropping.
59+
// The default value is 1000.
60+
TraceBuffer int `mapstructure:"trace_buffer"`
61+
}
62+
63+
// Validate the configuration for errors. This is required by component.Config.
64+
func (c *Config) Validate() error {
65+
if c.Traces.IgnoreResources != nil {
66+
for _, entry := range c.Traces.IgnoreResources {
67+
_, err := regexp.Compile(entry)
68+
if err != nil {
69+
return fmt.Errorf("%q is not valid resource filter regular expression", entry)
70+
}
71+
}
72+
}
73+
74+
if c.Traces.SpanNameRemappings != nil {
75+
for key, value := range c.Traces.SpanNameRemappings {
76+
if value == "" {
77+
return fmt.Errorf("%q is not valid value for span name remapping", value)
78+
}
79+
if key == "" {
80+
return fmt.Errorf("%q is not valid key for span name remapping", key)
81+
}
82+
}
83+
}
84+
85+
if c.Traces.TraceBuffer < 0 {
86+
return fmt.Errorf("Trace buffer must be non-negative")
87+
}
88+
89+
return nil
90+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package datadogconnector
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestValidate(t *testing.T) {
13+
14+
tests := []struct {
15+
name string
16+
cfg *Config
17+
err string
18+
}{
19+
{
20+
name: "span name remapping valid",
21+
cfg: &Config{
22+
Traces: TracesConfig{
23+
SpanNameRemappings: map[string]string{"old.opentelemetryspan.name": "updated.name"},
24+
},
25+
},
26+
},
27+
{
28+
name: "span name remapping empty val",
29+
cfg: &Config{Traces: TracesConfig{
30+
SpanNameRemappings: map[string]string{"oldname": ""},
31+
}},
32+
err: "\"\" is not valid value for span name remapping",
33+
},
34+
{
35+
name: "span name remapping empty key",
36+
cfg: &Config{Traces: TracesConfig{
37+
SpanNameRemappings: map[string]string{"": "newname"},
38+
}},
39+
err: "\"\" is not valid key for span name remapping",
40+
},
41+
{
42+
name: "ignore resources valid",
43+
cfg: &Config{Traces: TracesConfig{
44+
IgnoreResources: []string{"[123]"},
45+
}},
46+
},
47+
{
48+
name: "ignore resources missing bracket",
49+
cfg: &Config{Traces: TracesConfig{
50+
IgnoreResources: []string{"[123"},
51+
}},
52+
err: "\"[123\" is not valid resource filter regular expression",
53+
},
54+
{
55+
name: "With trace_buffer",
56+
cfg: &Config{Traces: TracesConfig{
57+
TraceBuffer: 10,
58+
}},
59+
},
60+
{
61+
name: "neg trace_buffer",
62+
cfg: &Config{Traces: TracesConfig{
63+
TraceBuffer: -10,
64+
}},
65+
err: "Trace buffer must be non-negative",
66+
},
67+
}
68+
for _, testInstance := range tests {
69+
t.Run(testInstance.name, func(t *testing.T) {
70+
err := testInstance.cfg.Validate()
71+
if testInstance.err != "" {
72+
assert.EqualError(t, err, testInstance.err)
73+
} else {
74+
assert.NoError(t, err)
75+
}
76+
})
77+
}
78+
}

connector/datadogconnector/connector.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99

1010
pb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/trace"
11+
traceconfig "github.com/DataDog/datadog-agent/pkg/trace/config"
1112
"github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes"
1213
"github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics"
1314
"go.opentelemetry.io/collector/component"
@@ -45,7 +46,7 @@ type connectorImp struct {
4546
var _ component.Component = (*connectorImp)(nil) // testing that the connectorImp properly implements the type Component interface
4647

4748
// function to create a new connector
48-
func newConnector(set component.TelemetrySettings, _ component.Config, metricsConsumer consumer.Metrics, tracesConsumer consumer.Traces) (*connectorImp, error) {
49+
func newConnector(set component.TelemetrySettings, cfg component.Config, metricsConsumer consumer.Metrics, tracesConsumer consumer.Traces) (*connectorImp, error) {
4950
set.Logger.Info("Building datadog connector")
5051

5152
in := make(chan *pb.StatsPayload, 100)
@@ -62,7 +63,7 @@ func newConnector(set component.TelemetrySettings, _ component.Config, metricsCo
6263
ctx := context.Background()
6364
return &connectorImp{
6465
logger: set.Logger,
65-
agent: datadog.NewAgent(ctx, in),
66+
agent: datadog.NewAgentWithConfig(ctx, getTraceAgentCfg(cfg.(*Config).Traces), in),
6667
translator: trans,
6768
in: in,
6869
metricsConsumer: metricsConsumer,
@@ -71,6 +72,19 @@ func newConnector(set component.TelemetrySettings, _ component.Config, metricsCo
7172
}, nil
7273
}
7374

75+
func getTraceAgentCfg(cfg TracesConfig) *traceconfig.AgentConfig {
76+
acfg := traceconfig.New()
77+
acfg.OTLPReceiver.SpanNameRemappings = cfg.SpanNameRemappings
78+
acfg.OTLPReceiver.SpanNameAsResourceName = cfg.SpanNameAsResourceName
79+
acfg.Ignore["resource"] = cfg.IgnoreResources
80+
acfg.ComputeStatsBySpanKind = cfg.ComputeStatsBySpanKind
81+
acfg.PeerTagsAggregation = cfg.PeerTagsAggregation
82+
if v := cfg.TraceBuffer; v > 0 {
83+
acfg.TraceBuffer = v
84+
}
85+
return acfg
86+
}
87+
7488
// Start implements the component.Component interface.
7589
func (c *connectorImp) Start(_ context.Context, _ component.Host) error {
7690
c.logger.Info("Starting datadogconnector")
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package datadogconnector
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
"go.opentelemetry.io/collector/connector"
11+
"go.opentelemetry.io/collector/exporter"
12+
"go.opentelemetry.io/collector/exporter/debugexporter"
13+
"go.opentelemetry.io/collector/otelcol"
14+
"go.opentelemetry.io/collector/otelcol/otelcoltest"
15+
"go.opentelemetry.io/collector/processor"
16+
"go.opentelemetry.io/collector/processor/batchprocessor"
17+
"go.opentelemetry.io/collector/receiver"
18+
"go.opentelemetry.io/collector/receiver/otlpreceiver"
19+
20+
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter"
21+
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor"
22+
)
23+
24+
func TestExamples(t *testing.T) {
25+
t.Setenv("DD_API_KEY", "testvalue")
26+
factories := newTestComponents(t)
27+
const configFile = "./examples/config.yaml"
28+
_, err := otelcoltest.LoadConfigAndValidate(configFile, factories)
29+
require.NoError(t, err, "All yaml config must validate. Please ensure that all necessary component factories are added in newTestComponents()")
30+
}
31+
32+
// newTestComponents returns the minimum amount of components necessary for
33+
// running a collector with any of the examples/* yaml configuration files.
34+
func newTestComponents(t *testing.T) otelcol.Factories {
35+
var (
36+
factories otelcol.Factories
37+
err error
38+
)
39+
factories.Receivers, err = receiver.MakeFactoryMap(
40+
[]receiver.Factory{
41+
otlpreceiver.NewFactory(),
42+
}...,
43+
)
44+
require.NoError(t, err)
45+
factories.Processors, err = processor.MakeFactoryMap(
46+
[]processor.Factory{
47+
batchprocessor.NewFactory(),
48+
tailsamplingprocessor.NewFactory(),
49+
}...,
50+
)
51+
require.NoError(t, err)
52+
factories.Connectors, err = connector.MakeFactoryMap(
53+
[]connector.Factory{
54+
NewFactory(),
55+
}...,
56+
)
57+
require.NoError(t, err)
58+
factories.Exporters, err = exporter.MakeFactoryMap(
59+
[]exporter.Factory{
60+
datadogexporter.NewFactory(),
61+
debugexporter.NewFactory(),
62+
}...,
63+
)
64+
require.NoError(t, err)
65+
return factories
66+
}

0 commit comments

Comments
 (0)