ddtrace 1.12.1 → 1.13.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 (264) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +109 -9
  3. data/ext/ddtrace_profiling_native_extension/collectors_thread_context.c +97 -14
  4. data/ext/ddtrace_profiling_native_extension/extconf.rb +6 -0
  5. data/ext/ddtrace_profiling_native_extension/http_transport.c +19 -6
  6. data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +1 -1
  7. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +41 -2
  8. data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +6 -0
  9. data/ext/ddtrace_profiling_native_extension/stack_recorder.c +6 -10
  10. data/ext/ddtrace_profiling_native_extension/time_helpers.c +40 -4
  11. data/ext/ddtrace_profiling_native_extension/time_helpers.h +14 -0
  12. data/lib/datadog/appsec/component.rb +9 -0
  13. data/lib/datadog/appsec/configuration/settings.rb +104 -195
  14. data/lib/datadog/appsec/configuration.rb +0 -79
  15. data/lib/datadog/appsec/contrib/auto_instrument.rb +2 -4
  16. data/lib/datadog/appsec/contrib/devise/event.rb +57 -0
  17. data/lib/datadog/appsec/contrib/devise/ext.rb +13 -0
  18. data/lib/datadog/appsec/contrib/devise/integration.rb +42 -0
  19. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +76 -0
  20. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +52 -0
  21. data/lib/datadog/appsec/contrib/devise/patcher.rb +45 -0
  22. data/lib/datadog/appsec/contrib/devise/resource.rb +35 -0
  23. data/lib/datadog/appsec/contrib/devise/tracking.rb +49 -0
  24. data/lib/datadog/appsec/contrib/rack/ext.rb +2 -1
  25. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +1 -1
  26. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +1 -1
  27. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +1 -1
  28. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +12 -7
  29. data/lib/datadog/appsec/contrib/rails/ext.rb +3 -2
  30. data/lib/datadog/appsec/contrib/rails/framework.rb +1 -3
  31. data/lib/datadog/appsec/contrib/rails/patcher.rb +8 -8
  32. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +1 -1
  33. data/lib/datadog/appsec/contrib/sinatra/ext.rb +2 -1
  34. data/lib/datadog/appsec/contrib/sinatra/framework.rb +1 -3
  35. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +1 -1
  36. data/lib/datadog/appsec/event.rb +1 -1
  37. data/lib/datadog/appsec/extensions.rb +1 -130
  38. data/lib/datadog/appsec/monitor/reactive/set_user.rb +1 -1
  39. data/lib/datadog/appsec/processor.rb +1 -1
  40. data/lib/datadog/appsec/rate_limiter.rb +1 -1
  41. data/lib/datadog/appsec/remote.rb +1 -1
  42. data/lib/datadog/appsec.rb +1 -2
  43. data/lib/datadog/ci/configuration/settings.rb +6 -8
  44. data/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +7 -5
  45. data/lib/datadog/ci/contrib/cucumber/ext.rb +10 -8
  46. data/lib/datadog/ci/contrib/minitest/configuration/settings.rb +35 -0
  47. data/lib/datadog/ci/contrib/minitest/ext.rb +21 -0
  48. data/lib/datadog/ci/contrib/minitest/integration.rb +49 -0
  49. data/lib/datadog/ci/contrib/minitest/patcher.rb +27 -0
  50. data/lib/datadog/ci/contrib/minitest/test_helper.rb +68 -0
  51. data/lib/datadog/ci/contrib/rspec/configuration/settings.rb +7 -5
  52. data/lib/datadog/ci/contrib/rspec/ext.rb +9 -7
  53. data/lib/datadog/ci.rb +1 -0
  54. data/lib/datadog/core/backport.rb +51 -0
  55. data/lib/datadog/core/configuration/base.rb +5 -5
  56. data/lib/datadog/core/configuration/components.rb +6 -1
  57. data/lib/datadog/core/configuration/ext.rb +7 -5
  58. data/lib/datadog/core/configuration/option.rb +269 -19
  59. data/lib/datadog/core/configuration/option_definition.rb +76 -11
  60. data/lib/datadog/core/configuration/options.rb +22 -10
  61. data/lib/datadog/core/configuration/settings.rb +116 -61
  62. data/lib/datadog/core/environment/ext.rb +13 -11
  63. data/lib/datadog/core/environment/yjit.rb +58 -0
  64. data/lib/datadog/core/git/ext.rb +24 -22
  65. data/lib/datadog/core/logging/ext.rb +3 -1
  66. data/lib/datadog/core/metrics/ext.rb +7 -5
  67. data/lib/datadog/core/remote/client/capabilities.rb +5 -0
  68. data/lib/datadog/core/remote/client.rb +3 -0
  69. data/lib/datadog/core/remote/component.rb +25 -34
  70. data/lib/datadog/core/remote/configuration/content.rb +28 -1
  71. data/lib/datadog/core/remote/configuration/repository.rb +3 -1
  72. data/lib/datadog/core/remote/ext.rb +1 -1
  73. data/lib/datadog/core/remote/negotiation.rb +17 -4
  74. data/lib/datadog/core/runtime/ext.rb +22 -12
  75. data/lib/datadog/core/runtime/metrics.rb +43 -0
  76. data/lib/datadog/core/telemetry/client.rb +12 -2
  77. data/lib/datadog/core/telemetry/emitter.rb +4 -2
  78. data/lib/datadog/core/telemetry/event.rb +19 -4
  79. data/lib/datadog/core/telemetry/ext.rb +4 -1
  80. data/lib/datadog/core/telemetry/heartbeat.rb +2 -4
  81. data/lib/datadog/core/telemetry/http/ext.rb +10 -8
  82. data/lib/datadog/core/telemetry/http/transport.rb +1 -0
  83. data/lib/datadog/core/telemetry/v2/app_client_configuration_change.rb +41 -0
  84. data/lib/datadog/core/telemetry/v2/request.rb +29 -0
  85. data/lib/datadog/core/transport/http/client.rb +1 -1
  86. data/lib/datadog/core/transport/http/config.rb +10 -0
  87. data/lib/datadog/core/utils/duration.rb +52 -0
  88. data/lib/datadog/core/utils/hash.rb +47 -0
  89. data/lib/datadog/core/utils/network.rb +1 -1
  90. data/lib/datadog/core/utils/safe_dup.rb +27 -20
  91. data/lib/datadog/core/utils.rb +1 -1
  92. data/lib/datadog/core/workers/async.rb +2 -2
  93. data/lib/datadog/kit/appsec/events.rb +139 -89
  94. data/lib/datadog/kit/identity.rb +80 -65
  95. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +3 -0
  96. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -0
  97. data/lib/datadog/profiling/collectors/thread_context.rb +9 -2
  98. data/lib/datadog/profiling/component.rb +41 -9
  99. data/lib/datadog/profiling/exporter.rb +5 -1
  100. data/lib/datadog/profiling/flush.rb +9 -2
  101. data/lib/datadog/profiling/http_transport.rb +4 -1
  102. data/lib/datadog/profiling/load_native_extension.rb +7 -1
  103. data/lib/datadog/profiling.rb +11 -1
  104. data/lib/datadog/tracing/component.rb +58 -6
  105. data/lib/datadog/tracing/configuration/dynamic/option.rb +71 -0
  106. data/lib/datadog/tracing/configuration/dynamic.rb +64 -0
  107. data/lib/datadog/tracing/configuration/ext.rb +35 -32
  108. data/lib/datadog/tracing/configuration/http.rb +74 -0
  109. data/lib/datadog/tracing/configuration/settings.rb +106 -92
  110. data/lib/datadog/tracing/contrib/action_cable/configuration/settings.rb +9 -6
  111. data/lib/datadog/tracing/contrib/action_cable/ext.rb +20 -18
  112. data/lib/datadog/tracing/contrib/action_mailer/configuration/settings.rb +9 -6
  113. data/lib/datadog/tracing/contrib/action_mailer/ext.rb +20 -18
  114. data/lib/datadog/tracing/contrib/action_pack/configuration/settings.rb +8 -6
  115. data/lib/datadog/tracing/contrib/action_pack/ext.rb +10 -8
  116. data/lib/datadog/tracing/contrib/action_view/configuration/settings.rb +9 -6
  117. data/lib/datadog/tracing/contrib/action_view/ext.rb +12 -10
  118. data/lib/datadog/tracing/contrib/active_job/configuration/settings.rb +13 -7
  119. data/lib/datadog/tracing/contrib/active_job/ext.rb +25 -23
  120. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +1 -1
  121. data/lib/datadog/tracing/contrib/active_job/patcher.rb +1 -1
  122. data/lib/datadog/tracing/contrib/active_model_serializers/configuration/settings.rb +9 -6
  123. data/lib/datadog/tracing/contrib/active_model_serializers/ext.rb +12 -10
  124. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +9 -7
  125. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +0 -8
  126. data/lib/datadog/tracing/contrib/active_record/ext.rb +17 -15
  127. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +0 -5
  128. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +9 -7
  129. data/lib/datadog/tracing/contrib/active_support/ext.rb +18 -16
  130. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +14 -7
  131. data/lib/datadog/tracing/contrib/aws/ext.rb +37 -24
  132. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +9 -5
  133. data/lib/datadog/tracing/contrib/concurrent_ruby/configuration/settings.rb +3 -2
  134. data/lib/datadog/tracing/contrib/concurrent_ruby/ext.rb +4 -2
  135. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +14 -7
  136. data/lib/datadog/tracing/contrib/dalli/ext.rb +19 -11
  137. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +8 -6
  138. data/lib/datadog/tracing/contrib/delayed_job/configuration/settings.rb +13 -7
  139. data/lib/datadog/tracing/contrib/delayed_job/ext.rb +16 -14
  140. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +14 -7
  141. data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +21 -15
  142. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +8 -5
  143. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +16 -9
  144. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +43 -3
  145. data/lib/datadog/tracing/contrib/ethon/ext.rb +19 -11
  146. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +0 -5
  147. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +19 -10
  148. data/lib/datadog/tracing/contrib/excon/ext.rb +16 -8
  149. data/lib/datadog/tracing/contrib/excon/middleware.rb +20 -5
  150. data/lib/datadog/tracing/contrib/ext.rb +23 -1
  151. data/lib/datadog/tracing/contrib/extensions.rb +32 -0
  152. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +20 -10
  153. data/lib/datadog/tracing/contrib/faraday/ext.rb +16 -8
  154. data/lib/datadog/tracing/contrib/faraday/middleware.rb +16 -5
  155. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +8 -6
  156. data/lib/datadog/tracing/contrib/grape/ext.rb +16 -14
  157. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +8 -6
  158. data/lib/datadog/tracing/contrib/graphql/ext.rb +7 -5
  159. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +19 -9
  160. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +29 -20
  161. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +21 -20
  162. data/lib/datadog/tracing/contrib/grpc/ext.rb +16 -13
  163. data/lib/datadog/tracing/contrib/grpc/formatting.rb +127 -0
  164. data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +3 -2
  165. data/lib/datadog/tracing/contrib/hanami/ext.rb +10 -8
  166. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +4 -7
  167. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +33 -11
  168. data/lib/datadog/tracing/contrib/http/ext.rb +16 -9
  169. data/lib/datadog/tracing/contrib/http/instrumentation.rb +17 -5
  170. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +33 -11
  171. data/lib/datadog/tracing/contrib/httpclient/ext.rb +17 -9
  172. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +17 -5
  173. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +33 -11
  174. data/lib/datadog/tracing/contrib/httprb/ext.rb +16 -9
  175. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +17 -5
  176. data/lib/datadog/tracing/contrib/kafka/configuration/settings.rb +9 -6
  177. data/lib/datadog/tracing/contrib/kafka/ext.rb +42 -39
  178. data/lib/datadog/tracing/contrib/lograge/configuration/settings.rb +3 -2
  179. data/lib/datadog/tracing/contrib/lograge/ext.rb +3 -1
  180. data/lib/datadog/tracing/contrib/lograge/instrumentation.rb +1 -0
  181. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +14 -7
  182. data/lib/datadog/tracing/contrib/mongodb/ext.rb +20 -16
  183. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +9 -5
  184. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +17 -14
  185. data/lib/datadog/tracing/contrib/mysql2/ext.rb +15 -10
  186. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +9 -5
  187. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +52 -0
  188. data/lib/datadog/tracing/contrib/opensearch/ext.rb +37 -0
  189. data/lib/datadog/tracing/contrib/opensearch/integration.rb +44 -0
  190. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +128 -0
  191. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +81 -0
  192. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +17 -14
  193. data/lib/datadog/tracing/contrib/pg/ext.rb +22 -19
  194. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +9 -5
  195. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +14 -7
  196. data/lib/datadog/tracing/contrib/presto/ext.rb +25 -20
  197. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +9 -5
  198. data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +12 -10
  199. data/lib/datadog/tracing/contrib/qless/configuration/settings.rb +12 -8
  200. data/lib/datadog/tracing/contrib/qless/ext.rb +14 -12
  201. data/lib/datadog/tracing/contrib/que/configuration/settings.rb +21 -12
  202. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +9 -7
  203. data/lib/datadog/tracing/contrib/racecar/event.rb +0 -5
  204. data/lib/datadog/tracing/contrib/racecar/ext.rb +20 -18
  205. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +16 -12
  206. data/lib/datadog/tracing/contrib/rack/ext.rb +18 -16
  207. data/lib/datadog/tracing/contrib/rack/header_collection.rb +3 -0
  208. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +53 -0
  209. data/lib/datadog/tracing/contrib/rack/middlewares.rb +8 -49
  210. data/lib/datadog/tracing/contrib/rails/configuration/settings.rb +15 -11
  211. data/lib/datadog/tracing/contrib/rails/ext.rb +7 -5
  212. data/lib/datadog/tracing/contrib/rails/log_injection.rb +4 -10
  213. data/lib/datadog/tracing/contrib/rails/patcher.rb +10 -41
  214. data/lib/datadog/tracing/contrib/rails/railtie.rb +3 -3
  215. data/lib/datadog/tracing/contrib/rake/configuration/settings.rb +12 -9
  216. data/lib/datadog/tracing/contrib/rake/ext.rb +14 -12
  217. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +17 -9
  218. data/lib/datadog/tracing/contrib/redis/ext.rb +22 -15
  219. data/lib/datadog/tracing/contrib/redis/tags.rb +9 -5
  220. data/lib/datadog/tracing/contrib/resque/configuration/settings.rb +13 -7
  221. data/lib/datadog/tracing/contrib/resque/ext.rb +9 -7
  222. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +16 -9
  223. data/lib/datadog/tracing/contrib/rest_client/ext.rb +15 -8
  224. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +20 -5
  225. data/lib/datadog/tracing/contrib/roda/configuration/settings.rb +9 -6
  226. data/lib/datadog/tracing/contrib/semantic_logger/configuration/settings.rb +3 -2
  227. data/lib/datadog/tracing/contrib/semantic_logger/ext.rb +3 -1
  228. data/lib/datadog/tracing/contrib/semantic_logger/instrumentation.rb +1 -0
  229. data/lib/datadog/tracing/contrib/sequel/configuration/settings.rb +9 -6
  230. data/lib/datadog/tracing/contrib/sequel/ext.rb +10 -8
  231. data/lib/datadog/tracing/contrib/sequel/utils.rb +2 -7
  232. data/lib/datadog/tracing/contrib/shoryuken/configuration/settings.rb +14 -8
  233. data/lib/datadog/tracing/contrib/shoryuken/ext.rb +14 -12
  234. data/lib/datadog/tracing/contrib/sidekiq/configuration/settings.rb +18 -11
  235. data/lib/datadog/tracing/contrib/sidekiq/ext.rb +32 -30
  236. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +11 -9
  237. data/lib/datadog/tracing/contrib/sinatra/env.rb +0 -17
  238. data/lib/datadog/tracing/contrib/sinatra/ext.rb +21 -19
  239. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -14
  240. data/lib/datadog/tracing/contrib/sneakers/configuration/settings.rb +14 -8
  241. data/lib/datadog/tracing/contrib/sneakers/ext.rb +1 -0
  242. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -1
  243. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +74 -10
  244. data/lib/datadog/tracing/contrib/stripe/configuration/settings.rb +9 -6
  245. data/lib/datadog/tracing/contrib/sucker_punch/configuration/settings.rb +9 -6
  246. data/lib/datadog/tracing/contrib/sucker_punch/ext.rb +15 -13
  247. data/lib/datadog/tracing/contrib/utils/database.rb +5 -3
  248. data/lib/datadog/tracing/correlation.rb +9 -12
  249. data/lib/datadog/tracing/diagnostics/ext.rb +21 -19
  250. data/lib/datadog/tracing/distributed/b3_multi.rb +2 -2
  251. data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
  252. data/lib/datadog/tracing/distributed/trace_context.rb +52 -17
  253. data/lib/datadog/tracing/metadata/ext.rb +9 -6
  254. data/lib/datadog/tracing/remote.rb +78 -0
  255. data/lib/datadog/tracing/sampling/rule_sampler.rb +29 -0
  256. data/lib/datadog/tracing/span_operation.rb +3 -15
  257. data/lib/datadog/tracing/trace_operation.rb +16 -3
  258. data/lib/datadog/tracing/trace_segment.rb +5 -2
  259. data/lib/datadog/tracing/tracer.rb +10 -1
  260. data/lib/ddtrace/transport/ext.rb +15 -9
  261. data/lib/ddtrace/transport/trace_formatter.rb +9 -0
  262. data/lib/ddtrace/version.rb +9 -12
  263. metadata +38 -10
  264. data/lib/datadog/tracing/contrib/sinatra/headers.rb +0 -35
@@ -1,27 +1,113 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../utils/safe_dup'
4
+
3
5
  module Datadog
4
6
  module Core
5
7
  module Configuration
6
8
  # Represents an instance of an integration configuration option
7
9
  # @public_api
8
10
  class Option
9
- attr_reader \
10
- :definition
11
+ attr_reader :definition
12
+
13
+ # Option setting precedence.
14
+ module Precedence
15
+ # Represents an Option precedence level.
16
+ # Each precedence has a `numeric` value; higher values means higher precedence.
17
+ # `name` is for inspection purposes only.
18
+ Value = Struct.new(:numeric, :name) do
19
+ include Comparable
20
+
21
+ def <=>(other)
22
+ return nil unless other.is_a?(Value)
23
+
24
+ numeric <=> other.numeric
25
+ end
26
+ end
27
+
28
+ # Remote configuration provided through the Datadog app.
29
+ REMOTE_CONFIGURATION = Value.new(2, :remote_configuration).freeze
30
+
31
+ # Configuration provided in Ruby code, in this same process
32
+ # or via Environment variable
33
+ PROGRAMMATIC = Value.new(1, :programmatic).freeze
34
+
35
+ # Configuration that comes from default values
36
+ DEFAULT = Value.new(0, :default).freeze
37
+
38
+ # All precedences, sorted from highest to lowest
39
+ LIST = [REMOTE_CONFIGURATION, PROGRAMMATIC, DEFAULT].sort.reverse.freeze
40
+ end
11
41
 
12
42
  def initialize(definition, context)
13
43
  @definition = definition
14
44
  @context = context
15
45
  @value = nil
16
46
  @is_set = false
47
+
48
+ # One value is stored per precedence, to allow unsetting a higher
49
+ # precedence value and falling back to a lower precedence one.
50
+ @value_per_precedence = Hash.new(UNSET)
51
+
52
+ # Lowest precedence, to allow for `#set` to always succeed for a brand new `Option` instance.
53
+ @precedence_set = Precedence::DEFAULT
17
54
  end
18
55
 
19
- def set(value)
20
- old_value = @value
21
- (@value = context_exec(value, old_value, &definition.setter)).tap do |v|
22
- @is_set = true
23
- context_exec(v, old_value, &definition.on_set) if definition.on_set
56
+ # Overrides the current value for this option if the `precedence` is equal or higher than
57
+ # the previously set value.
58
+ # The first call to `#set` will always store the value regardless of precedence.
59
+ #
60
+ # @param value [Object] the new value to be associated with this option
61
+ # @param precedence [Precedence] from what precedence order this new value comes from
62
+ def set(value, precedence: Precedence::PROGRAMMATIC)
63
+ # Is there a higher precedence value set?
64
+ if @precedence_set > precedence
65
+ # This should be uncommon, as higher precedence values tend to
66
+ # happen later in the application lifecycle.
67
+ Datadog.logger.info do
68
+ "Option '#{definition.name}' not changed to '#{value}' (precedence: #{precedence.name}) because the higher " \
69
+ "precedence value '#{@value}' (precedence: #{@precedence_set.name}) was already set."
70
+ end
71
+
72
+ # But if it happens, we have to store the lower precedence value `value`
73
+ # because it's possible to revert to it by `#unset`ting
74
+ # the existing, higher-precedence value.
75
+ # Effectively, we always store one value pre precedence.
76
+ @value_per_precedence[precedence] = value
77
+
78
+ return @value
24
79
  end
80
+
81
+ internal_set(value, precedence)
82
+ end
83
+
84
+ def unset(precedence)
85
+ @value_per_precedence[precedence] = UNSET
86
+
87
+ # If we are unsetting the currently active value, we have to restore
88
+ # a lower precedence one...
89
+ if precedence == @precedence_set
90
+ # Find a lower precedence value that is already set.
91
+ Precedence::LIST.each do |p|
92
+ # DEV: This search can be optimized, but the list is small, and unset is
93
+ # DEV: only called from direct user interaction in the Datadog UI.
94
+ next unless p < precedence
95
+
96
+ # Look for value that is set.
97
+ # The hash `@value_per_precedence` has a custom default value of `UNSET`.
98
+ if (value = @value_per_precedence[p]) != UNSET
99
+ internal_set(value, p)
100
+ return nil
101
+ end
102
+ end
103
+
104
+ # If no value is left to fall back on, reset this option
105
+ reset
106
+ end
107
+
108
+ # ... otherwise, we are either unsetting a higher precedence value that is not
109
+ # yet set, thus there's nothing to do; or we are unsetting a lower precedence
110
+ # value, which also does not change the current value.
25
111
  end
26
112
 
27
113
  def get
@@ -30,32 +116,160 @@ module Datadog
30
116
  elsif definition.delegate_to
31
117
  context_eval(&definition.delegate_to)
32
118
  else
33
- set(default_value)
119
+ set_value_from_env_or_default
34
120
  end
35
121
  end
36
122
 
37
123
  def reset
38
- @value = if definition.resetter
39
- # Don't change @is_set to false; custom resetters are
40
- # responsible for changing @value back to a good state.
41
- # Setting @is_set = false would cause a default to be applied.
42
- context_exec(@value, &definition.resetter)
43
- else
44
- @is_set = false
45
- nil
46
- end
124
+ @value = if definition.resetter
125
+ # Don't change @is_set to false; custom resetters are
126
+ # responsible for changing @value back to a good state.
127
+ # Setting @is_set = false would cause a default to be applied.
128
+ context_exec(@value, &definition.resetter)
129
+ else
130
+ @is_set = false
131
+ nil
132
+ end
133
+
134
+ # Reset back to the lowest precedence, to allow all `set`s to succeed right after a reset.
135
+ @precedence_set = Precedence::DEFAULT
136
+ # Reset all stored values
137
+ @value_per_precedence = Hash.new(UNSET)
47
138
  end
48
139
 
49
140
  def default_value
50
- if definition.lazy
141
+ if definition.default.instance_of?(Proc)
51
142
  context_eval(&definition.default)
52
143
  else
53
- definition.default
144
+ definition.experimental_default_proc || Core::Utils::SafeDup.frozen_or_dup(definition.default)
54
145
  end
55
146
  end
56
147
 
148
+ def default_precedence?
149
+ precedence_set == Precedence::DEFAULT
150
+ end
151
+
57
152
  private
58
153
 
154
+ def coerce_env_variable(value)
155
+ return context_exec(value, &@definition.env_parser) if @definition.env_parser
156
+
157
+ case @definition.type
158
+ when :hash
159
+ values = value.split(',') # By default we only want to support comma separated strings
160
+
161
+ values.map! do |v|
162
+ v.gsub!(/\A[\s,]*|[\s,]*\Z/, '')
163
+
164
+ v.empty? ? nil : v
165
+ end
166
+
167
+ values.compact!
168
+ values.each.with_object({}) do |v, hash|
169
+ pair = v.split(':', 2)
170
+ hash[pair[0]] = pair[1]
171
+ end
172
+ when :int
173
+ # DEV-2.0: Change to a more strict coercion method. Integer(value).
174
+ value.to_i
175
+ when :float
176
+ # DEV-2.0: Change to a more strict coercion method. Float(value).
177
+ value.to_f
178
+ when :array
179
+ values = value.split(',')
180
+
181
+ values.map! do |v|
182
+ v.gsub!(/\A[\s,]*|[\s,]*\Z/, '')
183
+
184
+ v.empty? ? nil : v
185
+ end
186
+
187
+ values.compact!
188
+ values
189
+ when :bool
190
+ string_value = value.strip
191
+ string_value = string_value.downcase
192
+ string_value == 'true' || string_value == '1' # rubocop:disable Style/MultipleComparison
193
+ when :string, NilClass
194
+ value
195
+ else
196
+ raise ArgumentError,
197
+ "The option #{@definition.name} is using an unsupported type option for env coercion `#{@definition.type}`"
198
+ end
199
+ end
200
+
201
+ def validate_type(value)
202
+ return value if skip_validation?
203
+
204
+ raise_error = false
205
+
206
+ valid_type = validate(@definition.type, value)
207
+
208
+ unless valid_type
209
+ raise_error = if @definition.type_options[:nilable]
210
+ !value.is_a?(NilClass)
211
+ else
212
+ true
213
+ end
214
+ end
215
+
216
+ if raise_error
217
+ error_msg = if @definition.type_options[:nilable]
218
+ "The setting `#{@definition.name}` inside your app's `Datadog.configure` block expects a "\
219
+ "#{@definition.type} or `nil`, but a `#{value.class}` was provided (#{value.inspect})."\
220
+ else
221
+ "The setting `#{@definition.name}` inside your app's `Datadog.configure` block expects a "\
222
+ "#{@definition.type}, but a `#{value.class}` was provided (#{value.inspect})."\
223
+ end
224
+
225
+ error_msg = "#{error_msg} Please update your `configure` block. "\
226
+ 'Alternatively, you can disable this validation using the '\
227
+ '`DD_EXPERIMENTAL_SKIP_CONFIGURATION_VALIDATION=true`environment variable. '\
228
+ 'For help, please open an issue on <https://github.com/datadog/dd-trace-rb/issues/new/choose>.'
229
+
230
+ raise ArgumentError, error_msg
231
+ end
232
+
233
+ value
234
+ end
235
+
236
+ def validate(type, value)
237
+ case type
238
+ when :string
239
+ value.is_a?(String)
240
+ when :int, :float
241
+ value.is_a?(Numeric)
242
+ when :array
243
+ value.is_a?(Array)
244
+ when :hash
245
+ value.is_a?(Hash)
246
+ when :bool
247
+ value.is_a?(TrueClass) || value.is_a?(FalseClass)
248
+ when :proc
249
+ value.is_a?(Proc)
250
+ when :symbol
251
+ value.is_a?(Symbol)
252
+ when NilClass
253
+ true # No validation is performed when option is typeless
254
+ else
255
+ raise ArgumentError, "The option #{@definition.name} is using an unsupported type option `#{@definition.type}`"
256
+ end
257
+ end
258
+
259
+ # Directly manipulates the current value and currently set precedence.
260
+ def internal_set(value, precedence)
261
+ old_value = @value
262
+ (@value = context_exec(validate_type(value), old_value, &definition.setter)).tap do |v|
263
+ @is_set = true
264
+ @precedence_set = precedence
265
+ # Store original value to ensure we can always safely call `#internal_set`
266
+ # when restoring a value from `@value_per_precedence`, and we are only running `definition.setter`
267
+ # on the original value, not on a valud that has already been processed by `definition.setter`.
268
+ @value_per_precedence[precedence] = value
269
+ context_exec(v, old_value, &definition.on_set) if definition.on_set
270
+ end
271
+ end
272
+
59
273
  def context_exec(*args, &block)
60
274
  @context.instance_exec(*args, &block)
61
275
  end
@@ -63,6 +277,42 @@ module Datadog
63
277
  def context_eval(&block)
64
278
  @context.instance_eval(&block)
65
279
  end
280
+
281
+ def set_value_from_env_or_default
282
+ value = nil
283
+ precedence = nil
284
+
285
+ if definition.env && ENV[definition.env]
286
+ value = coerce_env_variable(ENV[definition.env])
287
+ precedence = Precedence::PROGRAMMATIC
288
+ end
289
+
290
+ if value.nil? && definition.deprecated_env && ENV[definition.deprecated_env]
291
+ value = coerce_env_variable(ENV[definition.deprecated_env])
292
+ precedence = Precedence::PROGRAMMATIC
293
+
294
+ Datadog::Core.log_deprecation do
295
+ "#{definition.deprecated_env} environment variable is deprecated, use #{definition.env} instead."
296
+ end
297
+ end
298
+
299
+ option_value = value.nil? ? default_value : value
300
+
301
+ set(option_value, precedence: precedence || Precedence::DEFAULT)
302
+ end
303
+
304
+ def skip_validation?
305
+ ['true', '1'].include?(ENV.fetch('DD_EXPERIMENTAL_SKIP_CONFIGURATION_VALIDATION', '').strip)
306
+ end
307
+
308
+ # Used for testing
309
+ attr_reader :precedence_set
310
+ private :precedence_set
311
+
312
+ # Anchor object that represents a value that is not set.
313
+ # This is necessary because `nil` is a valid value to be set.
314
+ UNSET = Object.new
315
+ private_constant :UNSET
66
316
  end
67
317
  end
68
318
  end
@@ -11,25 +11,37 @@ module Datadog
11
11
 
12
12
  attr_reader \
13
13
  :default,
14
+ # experimental_default_proc is used when we want to store a block as part of the option value.
15
+ # Since this new option is experimental and we might not need it in the near future, I gave it a name that is
16
+ # clear to the reader that they should not rely on it and that is subject to change.
17
+ # Currently is only use internally.
18
+ :experimental_default_proc,
19
+ :env,
20
+ :deprecated_env,
21
+ :env_parser,
14
22
  :delegate_to,
15
23
  :depends_on,
16
- :lazy,
17
24
  :name,
18
25
  :on_set,
19
26
  :resetter,
20
27
  :setter,
21
- :type
28
+ :type,
29
+ :type_options
22
30
 
23
31
  def initialize(name, meta = {}, &block)
24
32
  @default = meta[:default]
33
+ @experimental_default_proc = meta[:experimental_default_proc]
34
+ @env = meta[:env]
35
+ @deprecated_env = meta[:deprecated_env]
36
+ @env_parser = meta[:env_parser]
25
37
  @delegate_to = meta[:delegate_to]
26
38
  @depends_on = meta[:depends_on] || []
27
- @lazy = meta[:lazy] || false
28
39
  @name = name.to_sym
29
40
  @on_set = meta[:on_set]
30
41
  @resetter = meta[:resetter]
31
42
  @setter = meta[:setter] || block || IDENTITY
32
43
  @type = meta[:type]
44
+ @type_options = meta[:type_options]
33
45
  end
34
46
 
35
47
  # Creates a new Option, bound to the context provided.
@@ -40,36 +52,59 @@ module Datadog
40
52
  # Acts as DSL for building OptionDefinitions
41
53
  # @public_api
42
54
  class Builder
55
+ class InvalidOptionError < StandardError; end
56
+
43
57
  attr_reader \
44
58
  :helpers
45
59
 
46
60
  def initialize(name, options = {})
61
+ @env = nil
62
+ @deprecated_env = nil
63
+ @env_parser = nil
47
64
  @default = nil
65
+ @experimental_default_proc = nil
48
66
  @delegate_to = nil
49
67
  @depends_on = []
50
68
  @helpers = {}
51
- @lazy = false
52
69
  @name = name.to_sym
53
70
  @on_set = nil
54
71
  @resetter = nil
55
72
  @setter = OptionDefinition::IDENTITY
56
73
  @type = nil
57
-
74
+ @type_options = {}
58
75
  # If options were supplied, apply them.
59
76
  apply_options!(options)
60
77
 
61
78
  # Apply block if given.
62
79
  yield(self) if block_given?
80
+
81
+ validate_options!
63
82
  end
64
83
 
65
84
  def depends_on(*values)
66
85
  @depends_on = values.flatten
67
86
  end
68
87
 
88
+ def env(value)
89
+ @env = value
90
+ end
91
+
92
+ def deprecated_env(value)
93
+ @deprecated_env = value
94
+ end
95
+
96
+ def env_parser(&block)
97
+ @env_parser = block
98
+ end
99
+
69
100
  def default(value = nil, &block)
70
101
  @default = block || value
71
102
  end
72
103
 
104
+ def experimental_default_proc(&block)
105
+ @experimental_default_proc = block
106
+ end
107
+
73
108
  def delegate_to(&block)
74
109
  @delegate_to = block
75
110
  end
@@ -78,8 +113,14 @@ module Datadog
78
113
  @helpers[name] = block
79
114
  end
80
115
 
81
- def lazy(value = true)
82
- @lazy = value
116
+ def lazy(_value = true)
117
+ Datadog::Core.log_deprecation do
118
+ 'Defining an option as lazy is deprecated for removal. Options now always behave as lazy. '\
119
+ "Please remove all references to the lazy setting.\n"\
120
+ 'Non-lazy options that were previously stored as blocks are no longer supported. '\
121
+ 'If you used this feature, please let us know by opening an issue on: '\
122
+ 'https://github.com/datadog/dd-trace-rb/issues/new so we can better understand and support your use case.'
123
+ end
83
124
  end
84
125
 
85
126
  def on_set(&block)
@@ -94,23 +135,32 @@ module Datadog
94
135
  @setter = block
95
136
  end
96
137
 
97
- def type(value = nil)
138
+ def type(value, nilable: false)
98
139
  @type = value
140
+ @type_options = { nilable: nilable }
141
+
142
+ value
99
143
  end
100
144
 
101
145
  # For applying options for OptionDefinition
146
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
102
147
  def apply_options!(options = {})
103
148
  return if options.nil? || options.empty?
104
149
 
105
150
  default(options[:default]) if options.key?(:default)
151
+ experimental_default_proc(&options[:experimental_default_proc]) if options.key?(:experimental_default_proc)
152
+ env(options[:env]) if options.key?(:env)
153
+ deprecated_env(options[:deprecated_env]) if options.key?(:deprecated_env)
154
+ env_parser(&options[:env_parser]) if options.key?(:env_parser)
106
155
  delegate_to(&options[:delegate_to]) if options.key?(:delegate_to)
107
156
  depends_on(*options[:depends_on]) if options.key?(:depends_on)
108
157
  lazy(options[:lazy]) if options.key?(:lazy)
109
158
  on_set(&options[:on_set]) if options.key?(:on_set)
110
159
  resetter(&options[:resetter]) if options.key?(:resetter)
111
160
  setter(&options[:setter]) if options.key?(:setter)
112
- type(&options[:type]) if options.key?(:type)
161
+ type(options[:type], **(options[:type_options] || {})) if options.key?(:type)
113
162
  end
163
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
114
164
 
115
165
  def to_definition
116
166
  OptionDefinition.new(@name, meta)
@@ -119,15 +169,30 @@ module Datadog
119
169
  def meta
120
170
  {
121
171
  default: @default,
172
+ experimental_default_proc: @experimental_default_proc,
173
+ env: @env,
174
+ deprecated_env: @deprecated_env,
175
+ env_parser: @env_parser,
122
176
  delegate_to: @delegate_to,
123
177
  depends_on: @depends_on,
124
- lazy: @lazy,
125
178
  on_set: @on_set,
126
179
  resetter: @resetter,
127
180
  setter: @setter,
128
- type: @type
181
+ type: @type,
182
+ type_options: @type_options
129
183
  }
130
184
  end
185
+
186
+ private
187
+
188
+ def validate_options!
189
+ if !@default.nil? && @experimental_default_proc
190
+ raise InvalidOptionError,
191
+ 'Using `default` and `experimental_default_proc` is not allowed. Please use one or the other.' \
192
+ 'If you want to store a block as the default value use `experimental_default_proc`'\
193
+ ' otherwise use `default`'
194
+ end
195
+ end
131
196
  end
132
197
  end
133
198
  end
@@ -24,7 +24,9 @@ module Datadog
24
24
  protected
25
25
 
26
26
  def option(name, meta = {}, &block)
27
- builder = OptionDefinition::Builder.new(name, meta, &block)
27
+ settings_name = defined?(@settings_name) && @settings_name
28
+ option_name = settings_name ? "#{settings_name}.#{name}" : name
29
+ builder = OptionDefinition::Builder.new(option_name, meta, &block)
28
30
  options[name] = builder.to_definition.tap do
29
31
  # Resolve and define helper functions
30
32
  helpers = default_helpers(name)
@@ -65,14 +67,16 @@ module Datadog
65
67
  @options ||= OptionSet.new
66
68
  end
67
69
 
68
- def set_option(name, value)
69
- add_option(name) unless options.key?(name)
70
- options[name].set(value)
70
+ def set_option(name, value, precedence: Configuration::Option::Precedence::PROGRAMMATIC)
71
+ resolve_option(name).set(value, precedence: precedence)
72
+ end
73
+
74
+ def unset_option(name, precedence: Configuration::Option::Precedence::PROGRAMMATIC)
75
+ resolve_option(name).unset(precedence)
71
76
  end
72
77
 
73
78
  def get_option(name)
74
- add_option(name) unless options.key?(name)
75
- options[name].get
79
+ resolve_option(name).get
76
80
  end
77
81
 
78
82
  def reset_option(name)
@@ -84,6 +88,12 @@ module Datadog
84
88
  self.class.options.key?(name)
85
89
  end
86
90
 
91
+ # Is this option's value the default fallback value?
92
+ def using_default?(name)
93
+ get_option(name) # Resolve value check if environment variable overwrote the default
94
+ options[name].default_precedence?
95
+ end
96
+
87
97
  def options_hash
88
98
  self.class.options.merge(options).each_with_object({}) do |(key, _), hash|
89
99
  hash[key] = get_option(key)
@@ -96,12 +106,14 @@ module Datadog
96
106
 
97
107
  private
98
108
 
99
- def add_option(name)
109
+ # Ensure option DSL is loaded
110
+ def resolve_option(name)
111
+ option = options[name]
112
+ return option if option
113
+
100
114
  assert_valid_option!(name)
101
115
  definition = self.class.options[name]
102
- definition.build(self).tap do |option|
103
- options[name] = option
104
- end
116
+ options[name] = definition.build(self)
105
117
  end
106
118
 
107
119
  def assert_valid_option!(name)