Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ You can use the following arguments with `otelcol.receiver.datadog`:
| `keep_alives_enabled` | `boolean` | Whether or not HTTP keep-alives are enabled | `true` | no |
| `max_request_body_size` | `string` | Maximum request body size the server will allow. | `"20MiB"` | no |
| `read_timeout` | `duration` | Read timeout for requests of the HTTP server. | `"60s"` | no |
| `trace_id_cache_size` | `int` | Cache size for mapping 64-bit to 128-bit trace IDs. | `0` | no |



Expand All @@ -59,13 +60,16 @@ To expose the HTTP server to other machines on your network, configure `endpoint

You can use the following blocks with `otelcol.receiver.datadog`:

| Block | Description | Required |
| -------------------------------- | -------------------------------------------------------------------------- | -------- |
| [`output`][output] | Configures where to send received telemetry data. | yes |
| [`cors`][cors] | Configures CORS for the HTTP server. | no |
| [`debug_metrics`][debug_metrics] | Configures the metrics that this component generates to monitor its state. | no |
| [`tls`][tls] | Configures TLS for the HTTP server. | no |
| `tls` > [`tpm`][tpm] | Configures TPM settings for the TLS `key_file`. | no |
| Block | Description | Required |
| ------------------------------------------- | -------------------------------------------------------------------------- | -------- |
| [`output`][output] | Configures where to send received telemetry data. | yes |
| [`cors`][cors] | Configures CORS for the HTTP server. | no |
| [`debug_metrics`][debug_metrics] | Configures the metrics that this component generates to monitor its state. | no |
| [`intake`][intake] | Configures the `/intake` endpoint behavior. | no |
| `intake` > [`proxy`][proxy] | Configures the proxy for the `/intake` endpoint. | no |
| `intake` > `proxy` > [`api`][api] | Configures the Datadog API connection for the intake proxy. | conditional |
| [`tls`][tls] | Configures TLS for the HTTP server. | no |
| `tls` > [`tpm`][tpm] | Configures TPM settings for the TLS `key_file`. | no |
Comment thread
thampiotr marked this conversation as resolved.

The > symbol indicates deeper levels of nesting.
For example, `tls` > `tpm` refers to a `tpm` block defined inside a `tls` block.
Expand All @@ -74,6 +78,9 @@ For example, `tls` > `tpm` refers to a `tpm` block defined inside a `tls` block.
[tpm]: #tpm
[cors]: #cors
[debug_metrics]: #debug_metrics
[intake]: #intake
[proxy]: #proxy
[api]: #api
[output]: #output

### `output`
Expand Down Expand Up @@ -104,6 +111,41 @@ The following headers are always implicitly allowed:

If `allowed_headers` includes `"*"`, all headers are permitted.

### `intake`

The `intake` block configures how the `/intake` endpoint behaves.
The Datadog Agent uses this endpoint to submit host tags and other metadata.

The following arguments are supported:

| Name | Type | Description | Default | Required |
|------------|----------|---------------------------------------------------------|---------|----------|
| `behavior` | `string` | How the `/intake` endpoint behaves: `"disable"` or `"proxy"`. | | yes |

Set `behavior` to `"proxy"` to forward `/intake` requests to Datadog's API.
Proxying requires a nested `proxy { api { ... } }` block with a Datadog API key.
When set to `"disable"`, the endpoint returns an error for any incoming request.

### `proxy`

The `proxy` block configures how the `/intake` proxy operates.
It's only used when `behavior` is set to `"proxy"`.
If `behavior` isn't `"proxy"`, this block is ignored.

This block has no arguments and is configured with the nested [`api`][api] block.
Comment thread
thampiotr marked this conversation as resolved.

### `api`

The `api` block configures the Datadog API connection used by the intake proxy.

The following arguments are supported:

| Name | Type | Description | Default | Required |
|-----------------------|------------|--------------------------------------------------|--------------------|----------|
| `key` | `secret` | Datadog API key. | | yes |
| `site` | `string` | Datadog site to send data to. | `"datadoghq.com"` | no |
| `fail_on_invalid_key` | `bool` | Exit on startup if the API key is invalid. | `false` | no |

### `debug_metrics`

{{< docs/shared lookup="reference/components/otelcol-debug-metrics-block.md" source="alloy" version="<ALLOY_VERSION>" >}}
Expand Down Expand Up @@ -159,6 +201,29 @@ otelcol.exporter.otlphttp "default" {
}
```

## Proxy `/intake` requests to Datadog

You can configure the receiver to forward `/intake` requests (host tags and metadata) to Datadog's API while processing metrics and traces locally:

```alloy
otelcol.receiver.datadog "default" {
intake {
behavior = "proxy"
proxy {
api {
key = sys.env("DD_API_KEY")
site = "datadoghq.eu"
}
}
}

output {
metrics = [otelcol.processor.batch.default.input]
traces = [otelcol.processor.batch.default.input]
}
}
```

## Enable authentication

You can create a `otelcol.receiver.datadog` component that requires authentication for requests.
Expand Down
91 changes: 86 additions & 5 deletions internal/component/otelcol/receiver/datadog/datadog.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@
package datadog

import (
"fmt"
"time"

"github.com/grafana/alloy/internal/component"
"github.com/grafana/alloy/internal/component/otelcol"
otelcolCfg "github.com/grafana/alloy/internal/component/otelcol/config"
"github.com/grafana/alloy/internal/component/otelcol/receiver"
"github.com/grafana/alloy/internal/featuregate"
"github.com/grafana/alloy/syntax/alloytypes"
datadogconfig "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog/config"
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/datadogreceiver"
otelcomponent "go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configopaque"
"go.opentelemetry.io/collector/pipeline"
)

Expand All @@ -31,7 +35,10 @@ func init() {
type Arguments struct {
HTTPServer otelcol.HTTPServerArguments `alloy:",squash"`

ReadTimeout time.Duration `alloy:"read_timeout,attr,optional"`
ReadTimeout time.Duration `alloy:"read_timeout,attr,optional"`
TraceIDCacheSize int `alloy:"trace_id_cache_size,attr,optional"`

Intake *IntakeArguments `alloy:"intake,block,optional"`

// DebugMetrics configures component internal metrics. Optional.
DebugMetrics otelcolCfg.DebugMetricsArguments `alloy:"debug_metrics,block,optional"`
Expand All @@ -40,8 +47,54 @@ type Arguments struct {
Output *otelcol.ConsumerArguments `alloy:"output,block"`
}

// IntakeArguments controls the /intake endpoint behavior.
type IntakeArguments struct {
// Behavior is required; allowed values are "disable" or "proxy".
Behavior string `alloy:"behavior,attr"`
Proxy *ProxyArguments `alloy:"proxy,block,optional"`
}

// ProxyArguments controls how the /intake proxy operates.
type ProxyArguments struct {
API APIArguments `alloy:"api,block"`
}

// APIArguments configures the Datadog API connection for the intake proxy.
type APIArguments struct {
Key alloytypes.Secret `alloy:"key,attr"`
Site string `alloy:"site,attr,optional"`
FailOnInvalidKey bool `alloy:"fail_on_invalid_key,attr,optional"`
Comment thread
thampiotr marked this conversation as resolved.
}

var _ receiver.Arguments = Arguments{}

// Validate implements syntax.Validator.
func (args *Arguments) Validate() error {
if args.Intake != nil {
if err := args.Intake.Validate(); err != nil {
return err
}
}

// Validate the converted upstream config. The upstream Validate() is not
// called automatically by the Alloy receiver framework, so we call it
// explicitly here. This also avoids duplicating upstream validation logic
// (e.g. allowed intake behavior values) which may evolve over time.
cfg, err := args.Convert()
if err != nil {
return err
}
return cfg.(*datadogreceiver.Config).Validate()
}

// Validate checks IntakeArguments constraints documented in the component reference.
func (args *IntakeArguments) Validate() error {
if args.Behavior == "proxy" && args.Proxy == nil {
return fmt.Errorf("a proxy block with an api block is required when intake behavior is %q", args.Behavior)
}
return nil
}

// SetToDefault implements syntax.Defaulter.
func (args *Arguments) SetToDefault() {
*args = Arguments{
Expand All @@ -61,10 +114,38 @@ func (args Arguments) Convert() (otelcomponent.Config, error) {
return nil, err
}

return &datadogreceiver.Config{
ServerConfig: *convertedHttpServer,
ReadTimeout: args.ReadTimeout,
}, nil
cfg := &datadogreceiver.Config{
ServerConfig: *convertedHttpServer,
ReadTimeout: args.ReadTimeout,
TraceIDCacheSize: args.TraceIDCacheSize,
}
Comment thread
thampiotr marked this conversation as resolved.

if args.Intake != nil {
cfg.Intake = args.Intake.Convert()
}
Comment thread
thampiotr marked this conversation as resolved.

return cfg, nil
}

func (args *IntakeArguments) Convert() datadogreceiver.IntakeConfig {
ic := datadogreceiver.IntakeConfig{
Behavior: args.Behavior,
}
if args.Behavior == "proxy" && args.Proxy != nil {
Comment thread
thampiotr marked this conversation as resolved.
apiSite := args.Proxy.API.Site
if apiSite == "" {
apiSite = datadogconfig.DefaultSite
}

ic.Proxy = datadogreceiver.ProxyConfig{
API: datadogconfig.APIConfig{
Key: configopaque.String(args.Proxy.API.Key),
Site: apiSite,
FailOnInvalidKey: args.Proxy.API.FailOnInvalidKey,
},
Comment thread
thampiotr marked this conversation as resolved.
}
}
Comment thread
thampiotr marked this conversation as resolved.
return ic
}

// Extensions implements receiver.Arguments.
Expand Down
Loading
Loading