From 0fb0a9f7efcbd032af2c8361da673ae79d8f2a73 Mon Sep 17 00:00:00 2001 From: Tim Reddehase Date: Wed, 27 May 2026 13:21:13 +0200 Subject: [PATCH 1/2] align valid_value semantics with AnyValue spec According to https://opentelemetry.io/docs/specs/otel/common/#anyvalue (Introduced in v1.51.0 of the OTel Spec: https://github.com/open-telemetry/opentelemetry-specification/tree/v1.51.0/specification/common) an attribute value is defined as `AnyValue` with AnyValue being allowed to be a simple value as well as an Array of AnyValue (which includes nested arrays and mixed array), as well as map. Empty values are allowed as well. --- sdk/lib/opentelemetry/sdk/internal.rb | 23 ++++++++----------- sdk/test/opentelemetry/sdk/trace/span_test.rb | 12 +++++----- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/sdk/lib/opentelemetry/sdk/internal.rb b/sdk/lib/opentelemetry/sdk/internal.rb index fae806960c..2e13a57068 100644 --- a/sdk/lib/opentelemetry/sdk/internal.rb +++ b/sdk/lib/opentelemetry/sdk/internal.rb @@ -25,27 +25,24 @@ def numeric?(value) end def valid_simple_value?(value) - value.instance_of?(String) || boolean?(value) || numeric?(value) + value.instance_of?(String) || boolean?(value) || numeric?(value) || value.nil? end def valid_array_value?(value) return false unless value.is_a?(Array) - return true if value.empty? - case value.first - when String - value.all? { |v| v.instance_of?(String) } - when TrueClass, FalseClass - value.all? { |v| boolean?(v) } - when Numeric - value.all? { |v| numeric?(v) } - else - false - end + value.all? { |v| valid_value?(v) } + end + + def valid_mapping_value?(value) + return false unless value.is_a?(Hash) + return false unless value.keys.all? { |k| valid_key?(k) } + + value.values.all? { |v| valid_value?(v) } end def valid_value?(value) - valid_simple_value?(value) || valid_array_value?(value) + valid_simple_value?(value) || valid_array_value?(value) || valid_mapping_value?(value) end def valid_attributes?(owner, kind, attrs) diff --git a/sdk/test/opentelemetry/sdk/trace/span_test.rb b/sdk/test/opentelemetry/sdk/trace/span_test.rb index 396d9e3ee4..aac2e3c506 100644 --- a/sdk/test/opentelemetry/sdk/trace/span_test.rb +++ b/sdk/test/opentelemetry/sdk/trace/span_test.rb @@ -114,11 +114,11 @@ end end - it 'reports an error for a NilClass value, which is invalid' do + it 'does not report an error for a NilClass value, as empty values are valid' do OpenTelemetry::TestHelpers.with_test_logger do |log_stream| span.set_attribute('foo', nil) span.finish - _(log_stream.string).must_match(/invalid span attribute value type NilClass for key 'foo' on span 'name'/) + _(log_stream.string.length).must_equal(0) end end @@ -223,12 +223,12 @@ _(events.first.attributes).must_equal(attrs) end - it 'does not keep nil-valued attributes' do + it 'keeps nil-valued attributes' do attrs = { 'foo' => nil } span.add_event('added', attributes: attrs) events = span.events _(events.size).must_equal(1) - _(events.first.attributes).must_equal({}) + _(events.first.attributes).must_equal({ 'foo' => nil }) end it 'accepts array-valued attributes' do @@ -247,12 +247,12 @@ _(events.first.attributes).must_equal({}) end - it 'does not accept array-valued attributes if the elements are different types' do + it 'accepts array-valued attributes even if the elements are different types' do attrs = { 'foo' => [1, 2, 'bar'] } span.add_event('added', attributes: attrs) events = span.events _(events.size).must_equal(1) - _(events.first.attributes).must_equal({}) + _(events.first.attributes).must_equal({ 'foo' => [1, 2, 'bar'] }) end it 'accepts array-valued attributes if the elements are true and false' do From 6f83a68b16e8444afb502e5daeb9b8d8730a1f08 Mon Sep 17 00:00:00 2001 From: Tim Reddehase Date: Thu, 2 Jul 2026 13:23:31 +0200 Subject: [PATCH 2/2] avoid recursive valid value check. --- sdk/lib/opentelemetry/sdk/internal.rb | 32 ++++++++++++++++++--------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/sdk/lib/opentelemetry/sdk/internal.rb b/sdk/lib/opentelemetry/sdk/internal.rb index 2e13a57068..bd338b78fa 100644 --- a/sdk/lib/opentelemetry/sdk/internal.rb +++ b/sdk/lib/opentelemetry/sdk/internal.rb @@ -28,21 +28,31 @@ def valid_simple_value?(value) value.instance_of?(String) || boolean?(value) || numeric?(value) || value.nil? end - def valid_array_value?(value) - return false unless value.is_a?(Array) + def valid_value?(value) + to_check = [value] + seen = Set.new + until to_check.empty? + current = to_check.pop + next if valid_simple_value?(current) - value.all? { |v| valid_value?(v) } - end + return false if seen.include?(current.object_id) - def valid_mapping_value?(value) - return false unless value.is_a?(Hash) - return false unless value.keys.all? { |k| valid_key?(k) } + seen << current.object_id - value.values.all? { |v| valid_value?(v) } - end + case current + when Array + current.each { |v| to_check << v } + when Hash + current.each do |k, v| + return false unless valid_key?(k) - def valid_value?(value) - valid_simple_value?(value) || valid_array_value?(value) || valid_mapping_value?(value) + to_check << v + end + else + return false + end + end + true end def valid_attributes?(owner, kind, attrs)