datadog 2.46.0 → 2.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -2
  3. data/ext/datadog_profiling_native_extension/collectors_stack.c +31 -14
  4. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +1 -1
  5. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -0
  6. data/ext/libdatadog_extconf_helpers.rb +1 -1
  7. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +29 -2
  8. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +8 -7
  9. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +14 -2
  10. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +8 -7
  11. data/lib/datadog/appsec/security_engine/engine.rb +2 -1
  12. data/lib/datadog/core/configuration/components.rb +11 -10
  13. data/lib/datadog/core/configuration/components_state.rb +8 -1
  14. data/lib/datadog/core/configuration/supported_configurations.rb +2 -0
  15. data/lib/datadog/core/remote/client.rb +44 -3
  16. data/lib/datadog/core/remote/dispatcher.rb +34 -3
  17. data/lib/datadog/core/workers/runtime_metrics.rb +1 -1
  18. data/lib/datadog/di/capture_expression_evaluator.rb +1 -1
  19. data/lib/datadog/di/component.rb +70 -0
  20. data/lib/datadog/di/remote.rb +46 -2
  21. data/lib/datadog/di/serializer.rb +27 -11
  22. data/lib/datadog/open_feature/configuration.rb +6 -0
  23. data/lib/datadog/open_feature/hooks/evaluation_telemetry_opt_out_hook.rb +25 -0
  24. data/lib/datadog/open_feature/provider.rb +22 -3
  25. data/lib/datadog/profiling/collectors/info.rb +37 -6
  26. data/lib/datadog/profiling/component.rb +22 -20
  27. data/lib/datadog/profiling/exporter.rb +3 -5
  28. data/lib/datadog/tracing/component.rb +7 -2
  29. data/lib/datadog/tracing/configuration/ext.rb +2 -0
  30. data/lib/datadog/tracing/configuration/settings.rb +11 -0
  31. data/lib/datadog/tracing/context_provider.rb +8 -0
  32. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +10 -5
  33. data/lib/datadog/tracing/contrib/excon/middleware.rb +10 -3
  34. data/lib/datadog/tracing/contrib/faraday/middleware.rb +10 -4
  35. data/lib/datadog/tracing/contrib/http/instrumentation.rb +16 -1
  36. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +8 -2
  37. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +10 -4
  38. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +10 -3
  39. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +102 -0
  40. data/lib/datadog/tracing/remote.rb +50 -19
  41. data/lib/datadog/version.rb +1 -1
  42. metadata +8 -8
  43. data/lib/datadog/appsec/utils/hash_coercion.rb +0 -23
@@ -7,6 +7,7 @@ require_relative "../http"
7
7
  require_relative "../analytics"
8
8
  require_relative "ext"
9
9
  require_relative "../http_annotation_helper"
10
+ require_relative "../utils/quantization/http"
10
11
  require_relative "../../../core/telemetry/logger"
11
12
 
12
13
  module Datadog
@@ -45,7 +46,8 @@ module Datadog
45
46
 
46
47
  # rubocop:disable Metrics/AbcSize
47
48
  def annotate!(span, env, options)
48
- span.resource = resource_name(env)
49
+ path = env[:url].path
50
+ span.resource = resource_name(env, path)
49
51
  span.service = service_name(env[:url].host, options)
50
52
  span.set_tag(Tracing::Metadata::Ext::TAG_SVC_SRC, Ext::TAG_COMPONENT)
51
53
  span.type = Tracing::Metadata::Ext::HTTP::TYPE_OUTBOUND
@@ -69,7 +71,7 @@ module Datadog
69
71
  Contrib::Analytics.set_sample_rate(span, options[:analytics_sample_rate])
70
72
  end
71
73
 
72
- span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, env[:url].path)
74
+ span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, path)
73
75
  span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, env[:method].to_s.upcase)
74
76
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_HOST, env[:url].host)
75
77
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_PORT, env[:url].port)
@@ -103,8 +105,12 @@ module Datadog
103
105
  Contrib::HTTP.inject(trace, env[:request_headers])
104
106
  end
105
107
 
106
- def resource_name(env)
107
- env[:method].to_s.upcase
108
+ def resource_name(env, path)
109
+ Contrib::Utils::Quantization::HTTP.client_resource(
110
+ env[:method].to_s.upcase,
111
+ path,
112
+ enabled: Datadog.configuration.tracing.http_client_resource_name_quantize
113
+ )
108
114
  end
109
115
 
110
116
  def build_request_options!(env)
@@ -34,7 +34,12 @@ module Datadog
34
34
  span.service = service_name(host, request_options, client_config)
35
35
  span.set_tag(Tracing::Metadata::Ext::TAG_SVC_SRC, Ext::TAG_COMPONENT)
36
36
  span.type = Tracing::Metadata::Ext::HTTP::TYPE_OUTBOUND
37
- span.resource = req.method
37
+ quantize_resource = Datadog.configuration.tracing.http_client_resource_name_quantize
38
+ span.resource = Contrib::Utils::Quantization::HTTP.client_resource(
39
+ req.method,
40
+ quantize_resource ? request_uri_path(req) : nil,
41
+ enabled: quantize_resource
42
+ )
38
43
 
39
44
  if Tracing::Distributed::PropagationPolicy.enabled?(
40
45
  pin_config: client_config,
@@ -116,6 +121,16 @@ module Datadog
116
121
 
117
122
  private
118
123
 
124
+ def request_uri_path(request)
125
+ uri = request.uri if request.respond_to?(:uri)
126
+ return uri.path if uri
127
+ return request.path.split(/[?#]/, 2).first if request.path.start_with?("/")
128
+
129
+ URI.parse(request.path).path
130
+ rescue URI::InvalidURIError
131
+ nil
132
+ end
133
+
119
134
  def host_and_port(request)
120
135
  if request.respond_to?(:uri) && request.uri
121
136
  [request.uri.host, request.uri.port]
@@ -4,6 +4,7 @@ require_relative "../../metadata/ext"
4
4
  require_relative "../http"
5
5
  require_relative "../analytics"
6
6
  require_relative "../http_annotation_helper"
7
+ require_relative "../utils/quantization/http"
7
8
  require_relative "../../../core/telemetry/logger"
8
9
 
9
10
  module Datadog
@@ -74,10 +75,15 @@ module Datadog
74
75
 
75
76
  http_method = req.header.request_method.upcase
76
77
  uri = req.header.request_uri
78
+ path = uri.path
77
79
 
78
- span.resource = http_method
80
+ span.resource = Contrib::Utils::Quantization::HTTP.client_resource(
81
+ http_method,
82
+ path,
83
+ enabled: Datadog.configuration.tracing.http_client_resource_name_quantize
84
+ )
79
85
  span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, http_method)
80
- span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, uri.path)
86
+ span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, path)
81
87
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_HOST, uri.host)
82
88
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_PORT, uri.port)
83
89
 
@@ -4,6 +4,7 @@ require_relative "../../metadata/ext"
4
4
  require_relative "../http"
5
5
  require_relative "../analytics"
6
6
  require_relative "../http_annotation_helper"
7
+ require_relative "../utils/quantization/http"
7
8
  require_relative "../../../core/telemetry/logger"
8
9
 
9
10
  module Datadog
@@ -70,17 +71,22 @@ module Datadog
70
71
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
71
72
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_REQUEST)
72
73
 
74
+ uri = req.uri
75
+ path = uri&.path
73
76
  if req.verb&.is_a?(String) || req.verb.is_a?(Symbol)
74
77
  http_method = req.verb.to_s.upcase
75
- span.resource = http_method
78
+ span.resource = Contrib::Utils::Quantization::HTTP.client_resource(
79
+ http_method,
80
+ path,
81
+ enabled: Datadog.configuration.tracing.http_client_resource_name_quantize
82
+ )
76
83
  span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, http_method)
77
84
  else
78
85
  logger.debug("service #{req_options[:service_name]} span #{Ext::SPAN_REQUEST} missing request verb")
79
86
  end
80
87
 
81
- if req.uri
82
- uri = req.uri
83
- span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, uri.path)
88
+ if uri
89
+ span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, path)
84
90
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_HOST, uri.host)
85
91
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_PORT, uri.port)
86
92
 
@@ -6,6 +6,7 @@ require_relative "../../metadata/ext"
6
6
  require_relative "../http"
7
7
  require_relative "../analytics"
8
8
  require_relative "ext"
9
+ require_relative "../utils/quantization/http"
9
10
 
10
11
  module Datadog
11
12
  module Tracing
@@ -38,7 +39,13 @@ module Datadog
38
39
 
39
40
  def datadog_tag_request(uri, span)
40
41
  span.set_tag(Tracing::Metadata::Ext::TAG_SVC_SRC, Ext::TAG_COMPONENT)
41
- span.resource = method.to_s.upcase
42
+ http_method = method.to_s.upcase
43
+ path = uri.path
44
+ span.resource = Contrib::Utils::Quantization::HTTP.client_resource(
45
+ http_method,
46
+ path,
47
+ enabled: Datadog.configuration.tracing.http_client_resource_name_quantize
48
+ )
42
49
 
43
50
  if datadog_configuration[:peer_service]
44
51
  span.set_tag(
@@ -57,8 +64,8 @@ module Datadog
57
64
  # Set analytics sample rate
58
65
  Contrib::Analytics.set_sample_rate(span, analytics_sample_rate) if analytics_enabled?
59
66
 
60
- span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, uri.path)
61
- span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, method.to_s.upcase)
67
+ span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, path)
68
+ span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, http_method)
62
69
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_HOST, uri.host)
63
70
  span.set_tag(Tracing::Metadata::Ext::NET::TAG_TARGET_PORT, uri.port)
64
71
  span.set_tags(
@@ -20,6 +20,96 @@ module Datadog
20
20
 
21
21
  module_function
22
22
 
23
+ def client_resource(method, path, enabled:)
24
+ return method unless enabled && path
25
+
26
+ resource = method.dup
27
+ resource << (+" ").force_encoding(resource.encoding)
28
+ quantized_path = quantize_path(decode_path(path))
29
+ resource << quantized_path.force_encoding(resource.encoding)
30
+ end
31
+
32
+ def decode_path(path)
33
+ decoded = String.new(encoding: Encoding::BINARY)
34
+ index = 0
35
+
36
+ while index < path.bytesize
37
+ byte = path.getbyte(index)
38
+ break unless byte
39
+
40
+ if byte == 37 && index + 2 < path.bytesize
41
+ high_byte = path.getbyte(index + 1)
42
+ low_byte = path.getbyte(index + 2)
43
+ if high_byte && low_byte
44
+ high = decode_hex_byte(high_byte)
45
+ low = decode_hex_byte(low_byte)
46
+ if high && low
47
+ decoded << high * 16 + low
48
+ index += 3
49
+ next
50
+ end
51
+ end
52
+ end
53
+
54
+ decoded << byte
55
+ index += 1
56
+ end
57
+
58
+ decoded.force_encoding(path.encoding)
59
+ end
60
+
61
+ def quantize_path(path)
62
+ slash = (+"/").force_encoding(path.encoding)
63
+ normalized = if path.getbyte(0) == 47
64
+ path
65
+ else
66
+ String.new(encoding: path.encoding) << slash << path
67
+ end
68
+
69
+ output = String.new(encoding: path.encoding)
70
+ index = 1
71
+ replacements = 0
72
+
73
+ while index < normalized.bytesize
74
+ segment_end = index
75
+ allowed = 0
76
+ digits = 0
77
+ specials = 0
78
+
79
+ while segment_end < normalized.bytesize && normalized.getbyte(segment_end) != 47
80
+ byte = normalized.getbyte(segment_end)
81
+ break unless byte
82
+
83
+ if byte.between?(48, 57)
84
+ digits += 1
85
+ elsif byte.between?(65, 90) || byte.between?(97, 122) || byte == 45 || byte == 95
86
+ allowed += 1
87
+ else
88
+ specials += 1
89
+ end
90
+ segment_end += 1
91
+ end
92
+
93
+ output << slash
94
+ segment = normalized.byteslice(index, segment_end - index) || String.new(encoding: path.encoding)
95
+ if normalized.getbyte(index) == 118 && allowed == 1 && digits > 0 && specials.zero?
96
+ output << segment
97
+ elsif specials > 0 || digits > 0
98
+ output << (+"*").force_encoding(path.encoding)
99
+ replacements += 1
100
+ else
101
+ output << segment
102
+ end
103
+
104
+ index = segment_end + 1
105
+ end
106
+
107
+ return normalized.dup if replacements.zero?
108
+
109
+ output << slash if normalized.getbyte(normalized.bytesize - 1) == 47
110
+ output
111
+ end
112
+
23
113
  def url(url, options = {})
24
114
  url!(url, options)
25
115
  rescue
@@ -118,6 +208,18 @@ module Datadog
118
208
 
119
209
  private_class_method :collect_query
120
210
 
211
+ def decode_hex_byte(byte)
212
+ if byte.between?(48, 57)
213
+ byte - 48
214
+ elsif byte.between?(65, 70)
215
+ byte - 55
216
+ elsif byte.between?(97, 102)
217
+ byte - 87
218
+ end
219
+ end
220
+
221
+ private_class_method :decode_hex_byte
222
+
121
223
  # Scans over the query string and obfuscates sensitive data by
122
224
  # replacing matches with an opaque value
123
225
  def obfuscate_query(query, options = {})
@@ -62,6 +62,8 @@ module Datadog
62
62
 
63
63
  # @type var parsed: Array[[::Datadog::Core::Remote::Configuration::Content, ::Hash[::String, untyped]]]
64
64
  parsed = []
65
+ # @type var unparseable_contents: Array[Datadog::Core::Remote::Configuration::Content]
66
+ unparseable_contents = []
65
67
  repository.contents.each do |content|
66
68
  next unless content.path.product == PRODUCT
67
69
 
@@ -76,9 +78,8 @@ module Datadog
76
78
  # A present but non-object lib_config (e.g. {"lib_config": []}) is a
77
79
  # schema violation the customer can fix. Acknowledging it as applied
78
80
  # would tell the backend the config was successfully applied when it
79
- # was not, and an empty merge would revert active tracing overrides
80
- # to their non-RC values. Reject it per content so the malformed
81
- # payload is reported errored instead. A null or absent lib_config
81
+ # was not. Reject it per content so the malformed payload is
82
+ # reported errored instead. A null or absent lib_config
82
83
  # carries no overrides and is left as a no-op.
83
84
  lib_config = config["lib_config"]
84
85
  if !lib_config.nil? && !lib_config.is_a?(Hash)
@@ -92,6 +93,7 @@ module Datadog
92
93
  description: "Failed to parse APM_TRACING remote config",
93
94
  )
94
95
  content.errored("#{e.class}: #{e.message}: #{Array(e.backtrace).join("\n")}")
96
+ unparseable_contents << content
95
97
  end
96
98
  end
97
99
 
@@ -117,10 +119,26 @@ module Datadog
117
119
  # deterministic merge.
118
120
  ordered = applicable.sort_by { |content, config| [-config_priority(config), content.path.config_id] }
119
121
  merged = merge_lib_configs(ordered.map { |_content, config| config })
122
+ # The applicable configs carrying a non-null dynamic_instrumentation_enabled
123
+ # value: any of their paths can still supply the signal after a later
124
+ # replacement, so the DI component records them all.
125
+ # @type var enable_signal_paths: Array[Datadog::Core::Remote::Configuration::Path]
126
+ enable_signal_paths = Core::Utils::EnumerableCompat.filter_map(ordered) do |pair|
127
+ content, config = pair
128
+ lib_config = config["lib_config"]
129
+ if lib_config.is_a?(Hash) && !lib_config["dynamic_instrumentation_enabled"].nil?
130
+ content.path
131
+ end
132
+ end
120
133
 
121
134
  # Applied even when the set is empty: an emptied repository (last config
122
135
  # removed) reverts the tracing overrides to their non-RC values.
123
- apply_lib_config(merged, repository)
136
+ apply_lib_config(
137
+ merged,
138
+ repository,
139
+ unparseable_contents: unparseable_contents,
140
+ enable_signal_paths: enable_signal_paths,
141
+ )
124
142
 
125
143
  parsed.each { |content, _config| content.applied }
126
144
  nil
@@ -205,8 +223,10 @@ module Datadog
205
223
  #
206
224
  # @param lib_config [Hash[String, untyped]] the lib_config to apply
207
225
  # @param repository [Core::Remote::Configuration::Repository, nil] forwarded to DI enablement
226
+ # @param unparseable_contents [Array<Core::Remote::Configuration::Content>] the APM_TRACING contents that failed to parse
227
+ # @param enable_signal_paths [Array<Core::Remote::Configuration::Path>] the paths of the applicable configs carrying the field
208
228
  # @return [nil]
209
- def apply_lib_config(lib_config, repository)
229
+ def apply_lib_config(lib_config, repository, unparseable_contents:, enable_signal_paths:)
210
230
  env_vars = Datadog::Tracing::Configuration::Dynamic::OPTIONS.map do |name, env_var, option|
211
231
  value = lib_config[name]
212
232
 
@@ -218,13 +238,30 @@ module Datadog
218
238
  [env_var, telemetry_value]
219
239
  end
220
240
 
221
- if (di_enabled = lib_config["dynamic_instrumentation_enabled"]) != nil # rubocop:disable Style/NonNilCheck
222
- # repository is forwarded so that an enable signal can reconcile DI
223
- # against probes delivered in an earlier poll while DI was stopped
224
- # (see Datadog::DI::Remote.handle_rc_enablement).
241
+ di_enabled = lib_config["dynamic_instrumentation_enabled"]
242
+ # allow_initialization: false because this runs on the remote-config worker
243
+ # thread, and the default would build the component tree from that thread.
244
+ components = Datadog.send(:components, allow_initialization: false)
245
+ di_component = components&.dynamic_instrumentation
246
+ if di_enabled.nil? && di_component&.rc_enablement_signal_withdrawn?(unparseable_contents)
247
+ di_enabled = false
248
+ end
249
+
250
+ # Records the parsed carrier paths and the previously recorded paths whose
251
+ # replacements remain unparseable, on every merge including merges that keep
252
+ # the DI state.
253
+ if di_component
254
+ unparseable_recorded_paths = di_component.rc_enablement_signal_paths.select do |path|
255
+ unparseable_contents.any? { |content| content.path == path }
256
+ end
257
+ di_component.record_rc_enablement_signal_paths(enable_signal_paths + unparseable_recorded_paths)
258
+ end
259
+
260
+ unless di_enabled.nil?
261
+ # repository is forwarded for the stopped->started replay in
262
+ # Datadog::DI::Remote.handle_rc_enablement.
225
263
  Datadog::DI::Remote.handle_rc_enablement(di_enabled, repository)
226
264
 
227
- components = Datadog.send(:components, allow_initialization: false)
228
265
  di_products = Datadog::DI::Remote.products +
229
266
  Datadog::SymbolDatabase::Remote.deferred_products(Datadog.configuration)
230
267
 
@@ -236,7 +273,7 @@ module Datadog
236
273
  # by DD_DYNAMIC_INSTRUMENTATION_ENABLED=false. Advertising then would
237
274
  # report DI as in use when it is not and invite probe configs the
238
275
  # tracer must refuse; withdraw the products otherwise.
239
- if components&.dynamic_instrumentation&.started?
276
+ if di_component&.started?
240
277
  components&.remote&.add_products(*di_products)
241
278
  else
242
279
  components&.remote&.remove_products(*di_products)
@@ -249,13 +286,7 @@ module Datadog
249
286
  Datadog.logger.debug { "APM_TRACING RC: merged dynamic_instrumentation_enabled=#{di_enabled}" }
250
287
  end
251
288
 
252
- # allow_initialization: false because this runs on the remote-config
253
- # worker thread. If components haven't been built yet (e.g. during a
254
- # teardown/reset window), the default value would synchronously build
255
- # the entire component tree from this thread. The &. chain matches the
256
- # pattern used by DI::Remote.handle_rc_enablement in the same dispatch
257
- # path.
258
- Datadog.send(:components, allow_initialization: false)&.telemetry&.client_configuration_change!(env_vars)
289
+ components&.telemetry&.client_configuration_change!(env_vars)
259
290
  nil
260
291
  end
261
292
 
@@ -269,7 +300,7 @@ module Datadog
269
300
 
270
301
  def receiver(products = [PRODUCT], &block)
271
302
  matcher = Core::Remote::Dispatcher::Matcher::Product.new(products)
272
- [Core::Remote::Dispatcher::Receiver.new(matcher, &block)]
303
+ [Core::Remote::Dispatcher::Receiver.new(matcher, initial_reconcile: true, &block)]
273
304
  end
274
305
 
275
306
  private
@@ -3,7 +3,7 @@
3
3
  module Datadog
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 46
6
+ MINOR = 47
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
  BUILD = nil
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.46.0
4
+ version: 2.47.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
@@ -49,28 +49,28 @@ dependencies:
49
49
  requirements:
50
50
  - - "~>"
51
51
  - !ruby/object:Gem::Version
52
- version: 1.30.0.0.0
52
+ version: 1.30.0.1.0
53
53
  type: :runtime
54
54
  prerelease: false
55
55
  version_requirements: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: 1.30.0.0.0
59
+ version: 1.30.0.1.0
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: libdatadog
62
62
  requirement: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - "~>"
65
65
  - !ruby/object:Gem::Version
66
- version: 46.0.0.1.1
66
+ version: 46.1.0.1.1
67
67
  type: :runtime
68
68
  prerelease: false
69
69
  version_requirements: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
- version: 46.0.0.1.1
73
+ version: 46.1.0.1.1
74
74
  - !ruby/object:Gem::Dependency
75
75
  name: logger
76
76
  requirement: !ruby/object:Gem::Requirement
@@ -342,7 +342,6 @@ files:
342
342
  - lib/datadog/appsec/thread_safe_ref.rb
343
343
  - lib/datadog/appsec/trace_keeper.rb
344
344
  - lib/datadog/appsec/utils.rb
345
- - lib/datadog/appsec/utils/hash_coercion.rb
346
345
  - lib/datadog/appsec/utils/http.rb
347
346
  - lib/datadog/appsec/utils/http/body.rb
348
347
  - lib/datadog/appsec/utils/http/body_reader.rb
@@ -611,6 +610,7 @@ files:
611
610
  - lib/datadog/open_feature/ext.rb
612
611
  - lib/datadog/open_feature/flag_evaluation/aggregator.rb
613
612
  - lib/datadog/open_feature/flag_evaluation/writer.rb
613
+ - lib/datadog/open_feature/hooks/evaluation_telemetry_opt_out_hook.rb
614
614
  - lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb
615
615
  - lib/datadog/open_feature/hooks/flag_eval_metrics_hook.rb
616
616
  - lib/datadog/open_feature/hooks/span_enrichment_hook.rb
@@ -1199,8 +1199,8 @@ licenses:
1199
1199
  - Apache-2.0
1200
1200
  metadata:
1201
1201
  allowed_push_host: https://rubygems.org
1202
- changelog_uri: https://github.com/DataDog/dd-trace-rb/blob/v2.46.0/CHANGELOG.md
1203
- source_code_uri: https://github.com/DataDog/dd-trace-rb/tree/v2.46.0
1202
+ changelog_uri: https://github.com/DataDog/dd-trace-rb/blob/v2.47.0/CHANGELOG.md
1203
+ source_code_uri: https://github.com/DataDog/dd-trace-rb/tree/v2.47.0
1204
1204
  post_install_message: 'JRuby support in the datadog gem is deprecated. Details: https://dtdg.co/jruby-deprecation'
1205
1205
  rdoc_options: []
1206
1206
  require_paths:
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Datadog
4
- module AppSec
5
- module Utils
6
- # A module for coercing arbitrary objects into hashes.
7
- module HashCoercion
8
- # A best effort to coerce an object to a hash with methods known to various
9
- # frameworks with a fallback to standard library.
10
- #
11
- # @param object [Object] The object to coerce.
12
- # @return [Hash, nil] The coerced `Hash` or `nil` if the object is not coercible.
13
- def self.coerce(object)
14
- return object.as_json if object.respond_to?(:as_json)
15
- return object.to_hash if object.respond_to?(:to_hash)
16
- return object.to_h if object.respond_to?(:to_h)
17
-
18
- nil
19
- end
20
- end
21
- end
22
- end
23
- end