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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ As `fluent-plugin-datadog` is a buffered output plugin, you can set all of the b
| **dd_hostname** | Used by Datadog to identify the host submitting the logs. | `hostname -f` |
| **service** | Used by Datadog to correlate between logs, traces and metrics. | nil |
| **port** | Proxy port when logs are not directly forwarded to Datadog and ssl is not used | 80 |
| **host** | Proxy endpoint when logs are not directly forwarded to Datadog | http-intake.logs.datadoghq.com |
| **site** | The Datadog [site](https://docs.datadoghq.com/getting_started/site/) to send logs to. Used to derive the default `host` when no explicit `host` is provided. Valid values: `datadoghq.com`, `datadoghq.eu`, `us3.datadoghq.com`, `us5.datadoghq.com`, `ap1.datadoghq.com`, `ddog-gov.com`. | datadoghq.com |
| **host** | Proxy endpoint when logs are not directly forwarded to Datadog. When unset, the default is derived from `site` as `http-intake.logs.<site>`. An explicitly configured `host` always wins over `site`. | (derived from `site`) |
| **http_proxy** | HTTP proxy, only takes effect if HTTP forwarding is enabled (`use_http`). Defaults to `HTTP_PROXY`/`http_proxy` env vars. | nil |
| **delete_extracted_tag_attributes** | When true, removes `kubernetes` and `docker` attributes from log records after extracting them as tags. Useful to avoid duplicate data in Datadog logs UI. | false |

Expand Down
37 changes: 32 additions & 5 deletions lib/fluent/plugin/out_datadog.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ class RetryableError < StandardError;
DD_MAX_BATCH_SIZE = 5000000
DD_TRUNCATION_SUFFIX = "...TRUNCATED..."

DD_DEFAULT_HTTP_ENDPOINT = "http-intake.logs.datadoghq.com"
DD_DEFAULT_SITE = "datadoghq.com"
DD_DEFAULT_HTTP_HOST_PREFIX = "http-intake.logs."
DD_DEFAULT_HTTP_ENDPOINT = "#{DD_DEFAULT_HTTP_HOST_PREFIX}#{DD_DEFAULT_SITE}".freeze
DD_DEFAULT_TCP_ENDPOINT = "intake.logs.datadoghq.com"

helpers :compat_parameters
Expand All @@ -45,8 +47,16 @@ class RetryableError < StandardError;
config_param :dd_hostname, :string, :default => nil
config_param :delete_extracted_tag_attributes, :bool, :default => false

# Datadog site used to derive the default intake host. Valid values include:
# "datadoghq.com" (default), "datadoghq.eu", "us3.datadoghq.com",
# "us5.datadoghq.com", "ap1.datadoghq.com", "ddog-gov.com". Any value
# explicitly set for `host` takes precedence over the site-derived default.
config_param :site, :string, :default => DD_DEFAULT_SITE
Comment thread
ddrthall marked this conversation as resolved.

# Connection settings
config_param :host, :string, :default => DD_DEFAULT_HTTP_ENDPOINT
# `host` defaults to nil so we can tell whether the user explicitly set it.
# When nil, the host is derived from `site` during `configure`.
config_param :host, :string, :default => nil
config_param :use_ssl, :bool, :default => true
config_param :port, :integer, :default => 80
config_param :ssl_port, :integer, :default => 443
Expand Down Expand Up @@ -77,12 +87,29 @@ def initialize
def configure(conf)
compat_parameters_convert(conf, :buffer)
super
return if @dd_hostname

if not @use_http and @host == DD_DEFAULT_HTTP_ENDPOINT
@host = DD_DEFAULT_TCP_ENDPOINT
# Derive default host from `site` only for HTTP transport.
# TCP users with a non-default site must set `host` explicitly;
# TCP users with no host and the default site get the legacy TCP endpoint.
if @host.nil? || @host.empty?
if @use_http
@host = "#{DD_DEFAULT_HTTP_HOST_PREFIX}#{@site}"
elsif @site == DD_DEFAULT_SITE
@host = DD_DEFAULT_TCP_ENDPOINT
else
# TCP + non-default site: we cannot safely derive a TCP intake hostname
# from `site` (the HTTP-intake prefix is HTTP-only), and leaving @host
# nil would surface as a cryptic connect-time error. Fail fast at
# configure time with an actionable message.
raise Fluent::ConfigError,
"`host` is required when `use_http false` is combined with a non-default `site` " \
"(`site #{@site.inspect}`). Set `host` explicitly to your TCP intake (e.g. " \
"`intake.logs.#{@site}`)."
end
end
Comment thread
gh123man marked this conversation as resolved.

return if @dd_hostname

# Set dd_hostname if not already set (can be set when using fluentd as aggregator)
@dd_hostname = %x[hostname -f 2> /dev/null].strip
@dd_hostname = Socket.gethostname if @dd_hostname.empty?
Expand Down
97 changes: 97 additions & 0 deletions test/plugin/test_out_datadog.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,103 @@ def create_valid_subject
end
end
end

test "default site yields the default HTTP host" do
plugin = create_driver(%[
api_key foo
]).instance
assert_equal "datadoghq.com", plugin.site
assert_equal "http-intake.logs.datadoghq.com", plugin.host
end

test "EU site overrides the default HTTP host" do
plugin = create_driver(%[
api_key foo
site datadoghq.eu
]).instance
assert_equal "datadoghq.eu", plugin.site
assert_equal "http-intake.logs.datadoghq.eu", plugin.host
end

test "explicit host overrides site-derived default" do
plugin = create_driver(%[
api_key foo
site datadoghq.eu
host my-custom-intake.example.com
]).instance
assert_equal "datadoghq.eu", plugin.site
assert_equal "my-custom-intake.example.com", plugin.host
end

test "TCP transport with non-default site does not derive an HTTP host" do
# When use_http is false and a non-default site is set without an explicit
# host, @host must remain nil/empty so the caller is forced to set it
# explicitly. The old code would silently set an HTTP intake hostname
# and hand it to the TCP client.
plugin = create_driver(%[
api_key foo
use_http false
site datadoghq.eu
host intake.logs.datadoghq.eu
]).instance
# The explicit host is used as-is; the HTTP-intake prefix must NOT appear.
assert_equal "intake.logs.datadoghq.eu", plugin.host
assert_false plugin.host.start_with?("http-intake.logs.")
end
Comment thread
gh123man marked this conversation as resolved.

test "TCP transport with no host and default site falls back to TCP endpoint" do
# Legacy behaviour: TCP + no host + default site => DD_DEFAULT_TCP_ENDPOINT
plugin = create_driver(%[
api_key foo
use_http false
]).instance
assert_equal Fluent::DatadogOutput::DD_DEFAULT_TCP_ENDPOINT, plugin.host
end

test "TCP transport with non-default site and no host raises ConfigError" do
# This is the genuinely new and risky case: `use_http false` + a non-default
# `site` + no explicit `host`. The HTTP-intake prefix cannot be used for TCP,
# and we want a clear error at configure time instead of a cryptic connect-time
# failure later.
assert_raise(Fluent::ConfigError) do
create_driver(%[
api_key foo
use_http false
site datadoghq.eu
])
end
end

test "gov site is accepted" do
plugin = create_driver(%[
api_key foo
site ddog-gov.com
]).instance
assert_equal "ddog-gov.com", plugin.site
assert_equal "http-intake.logs.ddog-gov.com", plugin.host
end

test "unknown site is accepted (no validation, matches agent behavior)" do
# The Datadog Agent does not validate `site` (see pkg/config/utils/endpoints.go:
# GetMainEndpoint uses `prefix + strings.TrimSpace(c.GetString("site"))` directly).
# Mirror that behaviour here — typos surface at DNS resolution time, consistent
# with the rest of the Datadog product surface.
plugin = create_driver(%[
api_key foo
site future-region.datadoghq.com
]).instance
assert_equal "future-region.datadoghq.com", plugin.site
assert_equal "http-intake.logs.future-region.datadoghq.com", plugin.host
end

test "valid subdomain site is accepted" do
plugin = create_driver(%[
api_key foo
site us3.datadoghq.com
]).instance
assert_equal "us3.datadoghq.com", plugin.site
assert_equal "http-intake.logs.us3.datadoghq.com", plugin.host
end
end

sub_test_case "enrich_record" do
Expand Down
Loading