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
7 changes: 5 additions & 2 deletions lib/datadog/core/configuration/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -757,12 +757,15 @@ def initialize(*_)
val ||= ""
# maps OpenTelemetry semantic attributes to Datadog tags
key = case key.downcase
when "deployment.environment" then "env"
when "deployment.environment.name"
result["env"] = val
nil
when "deployment.environment" then "env" unless result.key?("env")
when "service.version" then "version"
when "service.name" then "service"
else key
end
result[key] = val unless key.empty?
result[key] = val unless key.nil? || key.empty?
end
end
result
Expand Down
22 changes: 13 additions & 9 deletions lib/datadog/opentelemetry/signal_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,24 @@ module SignalConfiguration
def create_resource
resource_attributes = {}

@settings.tags&.each do |key, value| # steep:ignore
@settings.tags&.each do |key, value|
otel_key = case key
when "service" then "service.name"
when "env" then "deployment.environment"
when "env", "deployment.environment.name", "deployment.environment" then next
when "version" then "service.version"
else key
end
resource_attributes[otel_key] = value
end

resource_attributes["service.name"] = @settings.service_without_fallback || resource_attributes["service.name"] || Datadog::Core::Environment::Ext::FALLBACK_SERVICE_NAME # steep:ignore
resource_attributes["deployment.environment"] = @settings.env if @settings.env # steep:ignore
resource_attributes["service.version"] = @settings.version if @settings.version # steep:ignore
resource_attributes["service.name"] = @settings.service_without_fallback || resource_attributes["service.name"] || Datadog::Core::Environment::Ext::FALLBACK_SERVICE_NAME
environment = @settings.env || @settings.tags["env"] || @settings.tags["deployment.environment.name"] || @settings.tags["deployment.environment"]
resource_attributes["deployment.environment.name"] = environment if environment
resource_attributes["service.version"] = @settings.version if @settings.version

hostname = Datadog::Core::Environment::Socket.resolved_hostname(@settings) # steep:ignore
hostname = Datadog::Core::Environment::Socket.resolved_hostname(@settings)
if hostname
if hostname == @settings.hostname # steep:ignore
if hostname == @settings.hostname
resource_attributes["host.name"] = hostname
elsif !resource_attributes.key?("host.name")
resource_attributes["host.name"] = hostname
Expand All @@ -41,9 +42,12 @@ def create_resource
# Returns the signal-specific option value when explicitly set,
# otherwise falls back to the general OTLP exporter config or computed_default.
def config_or_exporter_fallback(signal:, option_name:, computed_default: nil)
signal_settings = @settings.opentelemetry.public_send(signal) # steep:ignore
signal_settings = case signal
when :logs then @settings.opentelemetry.logs
when :metrics then @settings.opentelemetry.metrics
end
if signal_settings.using_default?(option_name)
@settings.opentelemetry.exporter.public_send(option_name) || computed_default # steep:ignore
@settings.opentelemetry.exporter.public_send(option_name) || computed_default
else
signal_settings.public_send(option_name)
end
Expand Down
4 changes: 3 additions & 1 deletion sig/datadog/opentelemetry/signal_configuration.rbs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
module Datadog
module OpenTelemetry
module SignalConfiguration
@settings: Core::Configuration::Settings

private

def create_resource: () -> ::OpenTelemetry::SDK::Resources::Resource

def config_or_exporter_fallback: (signal: ::Symbol, option_name: ::Symbol, ?computed_default: any) -> any
def config_or_exporter_fallback: (signal: (:logs | :metrics), option_name: ::Symbol, ?computed_default: any) -> any
end
end
end
54 changes: 51 additions & 3 deletions spec/datadog/core/configuration/settings_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1382,22 +1382,70 @@
around do |example|
ClimateControl.modify(
"OTEL_RESOURCE_ATTRIBUTES" => otel_tags,
Datadog::Core::Environment::Ext::ENV_TAGS => dd_tags
Datadog::Core::Environment::Ext::ENV_TAGS => dd_tags,
Datadog::Core::Environment::Ext::ENV_ENVIRONMENT => dd_env,
) do
example.run
end
end

let(:dd_env) { nil }

context "is defined and DD_TAGS is set" do
let(:otel_tags) { "deployment.environment=prod,service.name=bleh,service.version=1.0,mkey=val1" }
let(:dd_tags) { "service:moon-test,version:v42069,env:prod,token:gg" }
it { is_expected.to include("service" => "moon-test", "version" => "v42069", "env" => "prod", "token" => "gg") }
end

context "is defined and DD_TAGS is not set" do
let(:otel_tags) { "deployment.environment=prod,service.name=bleh,service.version=1.0,mkey=val1" }
let(:dd_tags) { nil }
it { is_expected.to include("env" => "prod", "service" => "bleh", "version" => "1.0", "mkey" => "val1") }

context "with only deployment.environment.name" do
let(:otel_tags) { "deployment.environment.name=stable" }

it { is_expected.to eq("env" => "stable") }
end

context "with only deployment.environment" do
let(:otel_tags) { "deployment.environment=legacy" }

it { is_expected.to eq("env" => "legacy") }
end

context "with deployment.environment.name before deployment.environment" do
let(:otel_tags) { "deployment.environment.name=stable,deployment.environment=legacy" }

it { is_expected.to eq("env" => "stable") }
end

context "with deployment.environment before deployment.environment.name" do
let(:otel_tags) { "deployment.environment=legacy,deployment.environment.name=stable" }

it { is_expected.to eq("env" => "stable") }
end

context "with both deployment environment attributes and an unrelated attribute" do
let(:otel_tags) do
"deployment.environment.name=stable,deployment.environment=legacy,custom.attribute=value"
end

it "consumes the deployment environment attributes and preserves the unrelated attribute" do
is_expected.to eq("env" => "stable", "custom.attribute" => "value")
end
end

context "with service, version, and an unrelated attribute" do
let(:otel_tags) { "deployment.environment=prod,service.name=bleh,service.version=1.0,mkey=val1" }

it { is_expected.to include("env" => "prod", "service" => "bleh", "version" => "1.0", "mkey" => "val1") }
end

context "and DD_ENV is set" do
let(:otel_tags) { "deployment.environment.name=stable,deployment.environment=legacy" }
let(:dd_env) { "datadog" }

it { is_expected.to eq("env" => "datadog") }
end
end

context "is not defined and DD_TAGS is not set" do
Expand Down
49 changes: 47 additions & 2 deletions spec/datadog/opentelemetry/logs_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,49 @@

describe "Resource Attributes" do
subject(:resource_attributes) { attributes }

context "with deployment.environment.name provided by OpenTelemetry resource attributes" do
let(:env_overrides) { {"OTEL_RESOURCE_ATTRIBUTES" => "deployment.environment.name=production"} }

it "emits the canonical deployment environment attribute" do
expect(resource_attributes["deployment.environment.name"]).to eq("production")
expect(resource_attributes).not_to have_key("deployment.environment")
end
end

context "with programmatic deployment environment tags" do
let(:configuration) do
proc do |c|
c.tags = {
"deployment.environment" => "legacy",
"deployment.environment.name" => "stable",
}
end
end

it "prioritizes the stable attribute when it follows the legacy attribute" do
expect(resource_attributes["deployment.environment.name"]).to eq("stable")
expect(resource_attributes).not_to have_key("deployment.environment")
end

context "when DD_ENV is set and the legacy attribute follows the stable attribute" do
let(:env_overrides) { {"DD_ENV" => "datadog"} }
let(:configuration) do
proc do |c|
c.tags = {
"deployment.environment.name" => "stable",
"deployment.environment" => "legacy",
}
end
end

it "prioritizes DD_ENV" do
expect(resource_attributes["deployment.environment.name"]).to eq("datadog")
expect(resource_attributes).not_to have_key("deployment.environment")
end
end
end

context "with service metadata provided by environment variables" do
let(:env_overrides) do
{
Expand All @@ -105,9 +148,10 @@
expect(resource_attributes).to include(
"service.name" => "custom-service",
"service.version" => "2.0.0",
"deployment.environment" => "production",
"deployment.environment.name" => "production",
"host.name" => Datadog::Core::Environment::Socket.hostname,
)
expect(resource_attributes).not_to have_key("deployment.environment")
end
end

Expand Down Expand Up @@ -178,11 +222,12 @@
expect(resource_attributes).to include(
"service.name" => "test-service",
"service.version" => "1.0.0",
"deployment.environment" => "test",
"deployment.environment.name" => "test",
"host.name" => "myhost",
"team" => "backend",
"region" => "us-east-1",
)
expect(resource_attributes).not_to have_key("deployment.environment")
end
end

Expand Down
38 changes: 36 additions & 2 deletions spec/datadog/opentelemetry/metrics_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,38 @@ def setup_metrics(env_overrides = {}, &config_block)
end

describe "Resource Attributes" do
it "emits the canonical deployment environment attribute from OpenTelemetry resource attributes" do
setup_metrics("OTEL_RESOURCE_ATTRIBUTES" => "deployment.environment.name=production")

expect(attributes["deployment.environment.name"]).to eq("production")
expect(attributes).not_to have_key("deployment.environment")
end

it "prioritizes a programmatic stable attribute when the legacy attribute follows it" do
setup_metrics do |c|
c.tags = {
"deployment.environment.name" => "stable",
"deployment.environment" => "legacy",
}
end

expect(attributes["deployment.environment.name"]).to eq("stable")
expect(attributes).not_to have_key("deployment.environment")
end

it "prioritizes the configured environment when the stable attribute follows the legacy attribute" do
setup_metrics do |c|
c.env = "datadog"
c.tags = {
"deployment.environment" => "legacy",
"deployment.environment.name" => "stable",
}
end

expect(attributes["deployment.environment.name"]).to eq("datadog")
expect(attributes).not_to have_key("deployment.environment")
end

it "includes service name, version, and environment from Datadog config" do
setup_metrics(
"DD_SERVICE" => "custom-service",
Expand All @@ -164,7 +196,8 @@ def setup_metrics(env_overrides = {}, &config_block)

expect(attributes["service.name"]).to eq("custom-service")
expect(attributes["service.version"]).to eq("2.0.0")
expect(attributes["deployment.environment"]).to eq("production")
expect(attributes["deployment.environment.name"]).to eq("production")
expect(attributes).not_to have_key("deployment.environment")
expect(attributes["host.name"]).to eq(Datadog::Core::Environment::Socket.hostname)
end

Expand Down Expand Up @@ -203,7 +236,8 @@ def setup_metrics(env_overrides = {}, &config_block)

expect(attributes["service.name"]).to eq("test-service")
expect(attributes["service.version"]).to eq("1.0.0")
expect(attributes["deployment.environment"]).to eq("test")
expect(attributes["deployment.environment.name"]).to eq("test")
expect(attributes).not_to have_key("deployment.environment")
expect(attributes["host.name"]).to eq("myhost")
expect(attributes["team"]).to eq("backend")
expect(attributes["region"]).to eq("us-east-1")
Expand Down
6 changes: 6 additions & 0 deletions unreleased/20260930140422.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "Changed",
"product": "OpenTelemetry",
"pull_request": "https://github.com/DataDog/dd-trace-rb/pull/6261",
"message": "Emit `deployment.environment.name` instead of `deployment.environment` for Logs and Metrics resources, and accept it as the Datadog environment while retaining `deployment.environment` as a fallback."
}
Loading