datadog 2.22.0 → 2.26.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 (245) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +140 -1
  3. data/ext/LIBDATADOG_DEVELOPMENT.md +1 -58
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +93 -23
  5. data/ext/datadog_profiling_native_extension/collectors_stack.c +21 -5
  6. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  7. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  8. data/ext/datadog_profiling_native_extension/extconf.rb +9 -4
  9. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  10. data/ext/datadog_profiling_native_extension/http_transport.c +1 -0
  11. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
  12. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
  13. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  14. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  15. data/ext/libdatadog_api/feature_flags.c +554 -0
  16. data/ext/libdatadog_api/feature_flags.h +5 -0
  17. data/ext/libdatadog_api/init.c +2 -0
  18. data/ext/libdatadog_api/library_config.c +12 -11
  19. data/ext/libdatadog_extconf_helpers.rb +1 -1
  20. data/lib/datadog/ai_guard/api_client.rb +82 -0
  21. data/lib/datadog/ai_guard/component.rb +42 -0
  22. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  23. data/lib/datadog/ai_guard/configuration/settings.rb +98 -0
  24. data/lib/datadog/ai_guard/configuration.rb +11 -0
  25. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  26. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +34 -0
  27. data/lib/datadog/ai_guard/evaluation/request.rb +81 -0
  28. data/lib/datadog/ai_guard/evaluation/result.rb +43 -0
  29. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  30. data/lib/datadog/ai_guard/evaluation.rb +72 -0
  31. data/lib/datadog/ai_guard/ext.rb +16 -0
  32. data/lib/datadog/ai_guard.rb +153 -0
  33. data/lib/datadog/appsec/api_security/route_extractor.rb +23 -6
  34. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  35. data/lib/datadog/appsec/assets/blocked.html +8 -0
  36. data/lib/datadog/appsec/assets/blocked.json +1 -1
  37. data/lib/datadog/appsec/assets/blocked.text +3 -1
  38. data/lib/datadog/appsec/assets.rb +1 -1
  39. data/lib/datadog/appsec/context.rb +2 -1
  40. data/lib/datadog/appsec/remote.rb +9 -12
  41. data/lib/datadog/appsec/response.rb +18 -4
  42. data/lib/datadog/appsec/security_engine/engine.rb +3 -3
  43. data/lib/datadog/appsec/security_engine/result.rb +2 -1
  44. data/lib/datadog/appsec/security_engine/runner.rb +2 -2
  45. data/lib/datadog/core/configuration/components.rb +37 -3
  46. data/lib/datadog/core/configuration/config_helper.rb +2 -2
  47. data/lib/datadog/core/configuration/deprecations.rb +2 -2
  48. data/lib/datadog/core/configuration/option_definition.rb +4 -2
  49. data/lib/datadog/core/configuration/options.rb +8 -5
  50. data/lib/datadog/core/configuration/settings.rb +28 -3
  51. data/lib/datadog/core/configuration/supported_configurations.rb +338 -302
  52. data/lib/datadog/core/ddsketch.rb +0 -2
  53. data/lib/datadog/core/environment/cgroup.rb +52 -25
  54. data/lib/datadog/core/environment/container.rb +140 -46
  55. data/lib/datadog/core/environment/ext.rb +7 -0
  56. data/lib/datadog/core/environment/process.rb +87 -0
  57. data/lib/datadog/core/error.rb +6 -6
  58. data/lib/datadog/core/feature_flags.rb +61 -0
  59. data/lib/datadog/core/pin.rb +4 -0
  60. data/lib/datadog/core/rate_limiter.rb +9 -1
  61. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  62. data/lib/datadog/core/remote/client.rb +14 -6
  63. data/lib/datadog/core/remote/component.rb +6 -4
  64. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  65. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  66. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  67. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  68. data/lib/datadog/core/remote/transport/config.rb +4 -25
  69. data/lib/datadog/core/remote/transport/http/config.rb +10 -50
  70. data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
  71. data/lib/datadog/core/remote/transport/http.rb +15 -24
  72. data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
  73. data/lib/datadog/core/remote/worker.rb +25 -37
  74. data/lib/datadog/core/runtime/metrics.rb +11 -1
  75. data/lib/datadog/core/semaphore.rb +1 -4
  76. data/lib/datadog/core/tag_builder.rb +0 -4
  77. data/lib/datadog/core/tag_normalizer.rb +84 -0
  78. data/lib/datadog/core/telemetry/component.rb +59 -16
  79. data/lib/datadog/core/telemetry/event/app_started.rb +88 -50
  80. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  81. data/lib/datadog/core/telemetry/logger.rb +2 -2
  82. data/lib/datadog/core/telemetry/logging.rb +2 -8
  83. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  84. data/lib/datadog/core/telemetry/request.rb +17 -3
  85. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +3 -34
  86. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  87. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -11
  88. data/lib/datadog/core/telemetry/worker.rb +88 -32
  89. data/lib/datadog/core/transport/ext.rb +2 -0
  90. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  91. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  92. data/lib/datadog/core/transport/http/builder.rb +9 -5
  93. data/lib/datadog/core/transport/http/client.rb +80 -0
  94. data/lib/datadog/core/transport/http.rb +22 -19
  95. data/lib/datadog/core/transport/response.rb +12 -1
  96. data/lib/datadog/core/transport/transport.rb +90 -0
  97. data/lib/datadog/core/utils/array.rb +29 -0
  98. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  99. data/lib/datadog/core/utils/network.rb +3 -1
  100. data/lib/datadog/core/utils/only_once_successful.rb +8 -2
  101. data/lib/datadog/core/utils/safe_dup.rb +2 -2
  102. data/lib/datadog/core/utils/sequence.rb +2 -0
  103. data/lib/datadog/core/utils/time.rb +1 -1
  104. data/lib/datadog/core/utils.rb +2 -0
  105. data/lib/datadog/core/workers/async.rb +10 -1
  106. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  107. data/lib/datadog/core/workers/polling.rb +2 -0
  108. data/lib/datadog/core/workers/queue.rb +100 -1
  109. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  110. data/lib/datadog/data_streams/configuration.rb +11 -0
  111. data/lib/datadog/data_streams/ext.rb +11 -0
  112. data/lib/datadog/data_streams/extensions.rb +16 -0
  113. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  114. data/lib/datadog/data_streams/processor.rb +509 -0
  115. data/lib/datadog/data_streams/transport/http/stats.rb +52 -0
  116. data/lib/datadog/data_streams/transport/http.rb +40 -0
  117. data/lib/datadog/data_streams/transport/stats.rb +46 -0
  118. data/lib/datadog/data_streams.rb +100 -0
  119. data/lib/datadog/di/boot.rb +4 -2
  120. data/lib/datadog/di/component.rb +0 -16
  121. data/lib/datadog/di/contrib/active_record.rb +30 -5
  122. data/lib/datadog/di/el/compiler.rb +8 -4
  123. data/lib/datadog/di/el/evaluator.rb +1 -1
  124. data/lib/datadog/di/error.rb +9 -0
  125. data/lib/datadog/di/instrumenter.rb +102 -37
  126. data/lib/datadog/di/logger.rb +2 -2
  127. data/lib/datadog/di/probe.rb +20 -0
  128. data/lib/datadog/di/probe_builder.rb +2 -1
  129. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  130. data/lib/datadog/di/probe_manager.rb +47 -33
  131. data/lib/datadog/di/probe_notification_builder.rb +77 -25
  132. data/lib/datadog/di/probe_notifier_worker.rb +5 -5
  133. data/lib/datadog/di/proc_responder.rb +32 -0
  134. data/lib/datadog/di/remote.rb +89 -84
  135. data/lib/datadog/di/transport/diagnostics.rb +8 -36
  136. data/lib/datadog/di/transport/http/diagnostics.rb +1 -33
  137. data/lib/datadog/di/transport/http/input.rb +1 -33
  138. data/lib/datadog/di/transport/http.rb +32 -17
  139. data/lib/datadog/di/transport/input.rb +67 -34
  140. data/lib/datadog/di.rb +61 -5
  141. data/lib/datadog/error_tracking/filters.rb +2 -2
  142. data/lib/datadog/kit/appsec/events/v2.rb +2 -3
  143. data/lib/datadog/open_feature/component.rb +60 -0
  144. data/lib/datadog/open_feature/configuration.rb +27 -0
  145. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  146. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  147. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  148. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  149. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  150. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  151. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  152. data/lib/datadog/open_feature/ext.rb +14 -0
  153. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  154. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  155. data/lib/datadog/open_feature/provider.rb +141 -0
  156. data/lib/datadog/open_feature/remote.rb +67 -0
  157. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  158. data/lib/datadog/open_feature/transport.rb +70 -0
  159. data/lib/datadog/open_feature.rb +19 -0
  160. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  161. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  162. data/lib/datadog/opentelemetry/metrics.rb +117 -0
  163. data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
  164. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +35 -0
  165. data/lib/datadog/opentelemetry.rb +3 -0
  166. data/lib/datadog/profiling/collectors/code_provenance.rb +41 -7
  167. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +3 -2
  168. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  169. data/lib/datadog/profiling/collectors/info.rb +5 -4
  170. data/lib/datadog/profiling/component.rb +12 -11
  171. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +18 -0
  172. data/lib/datadog/profiling/http_transport.rb +4 -1
  173. data/lib/datadog/profiling/profiler.rb +4 -0
  174. data/lib/datadog/profiling/tag_builder.rb +36 -3
  175. data/lib/datadog/profiling.rb +1 -2
  176. data/lib/datadog/single_step_instrument.rb +1 -1
  177. data/lib/datadog/tracing/configuration/ext.rb +9 -0
  178. data/lib/datadog/tracing/configuration/settings.rb +74 -0
  179. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  180. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  181. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  182. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  183. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  184. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  185. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  186. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  187. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  188. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  189. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +22 -17
  190. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  191. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  192. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  193. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  194. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  195. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  196. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  197. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  198. data/lib/datadog/tracing/contrib/karafka/patcher.rb +35 -4
  199. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  200. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  201. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  202. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  203. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  204. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  205. data/lib/datadog/tracing/contrib/status_range_matcher.rb +9 -1
  206. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  207. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  208. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  209. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  210. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  211. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  212. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  213. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  214. data/lib/datadog/tracing/contrib/waterdrop.rb +41 -0
  215. data/lib/datadog/tracing/contrib.rb +1 -0
  216. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  217. data/lib/datadog/tracing/distributed/baggage.rb +3 -2
  218. data/lib/datadog/tracing/metadata/ext.rb +1 -1
  219. data/lib/datadog/tracing/remote.rb +1 -9
  220. data/lib/datadog/tracing/sampling/priority_sampler.rb +3 -1
  221. data/lib/datadog/tracing/span.rb +1 -1
  222. data/lib/datadog/tracing/span_event.rb +2 -2
  223. data/lib/datadog/tracing/span_operation.rb +20 -9
  224. data/lib/datadog/tracing/trace_operation.rb +44 -6
  225. data/lib/datadog/tracing/tracer.rb +42 -16
  226. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  227. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  228. data/lib/datadog/tracing/transport/http.rb +15 -9
  229. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  230. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  231. data/lib/datadog/tracing/transport/traces.rb +9 -71
  232. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  233. data/lib/datadog/tracing/writer.rb +1 -0
  234. data/lib/datadog/version.rb +2 -2
  235. data/lib/datadog.rb +3 -0
  236. metadata +93 -23
  237. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  238. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  239. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  240. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  241. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  242. data/lib/datadog/di/transport/http/api.rb +0 -42
  243. data/lib/datadog/di/transport/http/client.rb +0 -47
  244. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  245. data/lib/datadog/tracing/transport/http/api.rb +0 -44
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../stats'
4
+ require_relative '../../../core/transport/http/api/endpoint'
5
+ require_relative '../../../core/transport/http/response'
6
+
7
+ module Datadog
8
+ module DataStreams
9
+ module Transport
10
+ module HTTP
11
+ # HTTP transport behavior for Data Streams stats
12
+ module Stats
13
+ # Response from HTTP transport for DSM stats
14
+ class Response
15
+ include Datadog::Core::Transport::HTTP::Response
16
+
17
+ def initialize(http_response)
18
+ super
19
+ end
20
+ end
21
+
22
+ module API
23
+ # Endpoint for submitting DSM stats data
24
+ class Endpoint < Core::Transport::HTTP::API::Endpoint
25
+ def initialize(path)
26
+ super(:post, path)
27
+ end
28
+
29
+ def call(env, &block)
30
+ # Build request
31
+ env.verb = verb
32
+ env.path = path
33
+ env.body = env.request.parcel.data
34
+
35
+ # Send request
36
+ http_response = yield(env)
37
+
38
+ # Build response
39
+ Response.new(http_response)
40
+ end
41
+
42
+ def encoder
43
+ # DSM handles encoding in the transport layer
44
+ nil
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../core/transport/http'
4
+ require_relative 'http/stats'
5
+ require_relative 'stats'
6
+
7
+ module Datadog
8
+ module DataStreams
9
+ module Transport
10
+ # HTTP transport for Data Streams Monitoring
11
+ module HTTP
12
+ V01 = Stats::API::Endpoint.new(
13
+ '/v0.1/pipeline_stats'
14
+ )
15
+
16
+ module_function
17
+
18
+ # Builds a new Transport::HTTP::Client with default settings
19
+ def default(
20
+ agent_settings:,
21
+ logger:
22
+ )
23
+ Core::Transport::HTTP.build(
24
+ agent_settings: agent_settings,
25
+ logger: logger,
26
+ headers: {
27
+ 'Content-Type' => 'application/msgpack',
28
+ 'Content-Encoding' => 'gzip'
29
+ }
30
+ ) do |transport|
31
+ transport.api 'v0.1', V01, default: true
32
+
33
+ # Call block to apply any customization, if provided
34
+ yield(transport) if block_given?
35
+ end.to_transport(Transport::Stats::Transport)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'msgpack'
4
+ require 'zlib'
5
+ require_relative '../../core/transport/parcel'
6
+ require_relative '../../core/transport/request'
7
+ require_relative '../../core/transport/transport'
8
+
9
+ module Datadog
10
+ module DataStreams
11
+ module Transport
12
+ module Stats
13
+ # Parcel for encoded DSM stats payload
14
+ class EncodedParcel
15
+ include Datadog::Core::Transport::Parcel
16
+
17
+ def initialize(data)
18
+ @data = data
19
+ end
20
+
21
+ attr_reader :data
22
+ end
23
+
24
+ # Request for DSM stats
25
+ class Request < Datadog::Core::Transport::Request
26
+ end
27
+
28
+ # Transport for Data Streams Monitoring stats
29
+ class Transport < Core::Transport::Transport
30
+ def send_stats(payload)
31
+ # MessagePack encode and gzip compress the payload
32
+ msgpack_data = MessagePack.pack(payload)
33
+ compressed_data = Zlib.gzip(msgpack_data)
34
+
35
+ # Create parcel and request
36
+ parcel = EncodedParcel.new(compressed_data)
37
+ request = Request.new(parcel)
38
+
39
+ # Send to agent
40
+ client.send_request(:stats, request)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'data_streams/processor'
4
+ require_relative 'data_streams/pathway_context'
5
+ require_relative 'data_streams/configuration/settings'
6
+ require_relative 'data_streams/extensions'
7
+ require_relative 'core/utils/time'
8
+
9
+ module Datadog
10
+ # Datadog Data Streams Monitoring public API.
11
+ #
12
+ # The Datadog team ensures that public methods in this module
13
+ # only receive backwards compatible changes, and breaking changes
14
+ # will only occur in new major versions releases.
15
+ # @public_api
16
+ module DataStreams
17
+ class << self
18
+ # Set a produce checkpoint for Data Streams Monitoring
19
+ #
20
+ # @param type [String] The type of the checkpoint (e.g., 'kafka', 'kinesis', 'sns')
21
+ # @param destination [String] The destination (e.g., topic, exchange, stream name)
22
+ # @param auto_instrumentation [Boolean] Whether this checkpoint was set by auto-instrumentation (default: false)
23
+ # @param tags [Hash] Additional tags to include
24
+ # @yield [key, value] Block to inject context into carrier
25
+ # @return [String, nil] Base64 encoded pathway context or nil if disabled
26
+ # @public_api
27
+ def set_produce_checkpoint(type:, destination:, auto_instrumentation: false, tags: {}, &block)
28
+ processor&.set_produce_checkpoint(
29
+ type: type,
30
+ destination: destination,
31
+ manual_checkpoint: !auto_instrumentation,
32
+ tags: tags,
33
+ &block
34
+ )
35
+ end
36
+
37
+ # Set a consume checkpoint for Data Streams Monitoring
38
+ #
39
+ # @param type [String] The type of the checkpoint (e.g., 'kafka', 'kinesis', 'sns')
40
+ # @param source [String] The source (e.g., topic, exchange, stream name)
41
+ # @param auto_instrumentation [Boolean] Whether this checkpoint was set by auto-instrumentation (default: false)
42
+ # @param tags [Hash] Additional tags to include
43
+ # @yield [key] Block to extract context from carrier
44
+ # @return [String, nil] Base64 encoded pathway context or nil if disabled
45
+ # @public_api
46
+ def set_consume_checkpoint(type:, source:, auto_instrumentation: false, tags: {}, &block)
47
+ processor&.set_consume_checkpoint(
48
+ type: type,
49
+ source: source,
50
+ manual_checkpoint: !auto_instrumentation,
51
+ tags: tags,
52
+ &block
53
+ )
54
+ end
55
+
56
+ # Track Kafka produce offset for lag monitoring
57
+ #
58
+ # @param topic [String] The Kafka topic name
59
+ # @param partition [Integer] The partition number
60
+ # @param offset [Integer] The offset of the produced message
61
+ # @return [Boolean, nil] true if tracking succeeded, nil if disabled
62
+ # @!visibility private
63
+ def track_kafka_produce(topic, partition, offset)
64
+ processor&.track_kafka_produce(topic, partition, offset, Core::Utils::Time.now)
65
+ end
66
+
67
+ # Track Kafka message consumption for consumer lag monitoring
68
+ #
69
+ # @param topic [String] The Kafka topic name
70
+ # @param partition [Integer] The partition number
71
+ # @param offset [Integer] The offset of the consumed message
72
+ # @return [Boolean, nil] true if tracking succeeded, nil if disabled
73
+ # @!visibility private
74
+ def track_kafka_consume(topic, partition, offset)
75
+ processor&.track_kafka_consume(topic, partition, offset, Core::Utils::Time.now)
76
+ end
77
+
78
+ # Check if Data Streams Monitoring is enabled and available
79
+ #
80
+ # @return [Boolean] true if the processor is available
81
+ # @public_api
82
+ def enabled?
83
+ !processor.nil?
84
+ end
85
+
86
+ private
87
+
88
+ def processor
89
+ components.data_streams
90
+ end
91
+
92
+ def components
93
+ Datadog.send(:components)
94
+ end
95
+ end
96
+
97
+ # Expose Data Streams to global shared objects
98
+ Extensions.activate!
99
+ end
100
+ end
@@ -17,7 +17,8 @@ require_relative 'serializer'
17
17
  require_relative 'transport/http'
18
18
  require_relative 'utils'
19
19
 
20
- if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore
20
+ # Steep: https://github.com/ruby/rbs/pull/2715
21
+ if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore ArgumentTypeMismatch
21
22
 
22
23
  # For initial release of Dynamic Instrumentation, activate code tracking
23
24
  # only if DI is explicitly requested in the environment.
@@ -35,7 +36,8 @@ require_relative 'contrib'
35
36
 
36
37
  Datadog::DI::Contrib.load_now_or_later
37
38
 
38
- if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore
39
+ # Steep: https://github.com/ruby/rbs/pull/2715
40
+ if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore ArgumentTypeMismatch
39
41
  if Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE']
40
42
  require_relative 'probe_file_loader'
41
43
  Datadog::DI::ProbeFileLoader.load_now_or_later
@@ -29,22 +29,6 @@ module Datadog
29
29
  end
30
30
  end
31
31
 
32
- def build!(settings, agent_settings, logger, telemetry: nil)
33
- unless settings.respond_to?(:dynamic_instrumentation) && settings.dynamic_instrumentation.enabled
34
- raise "Requested DI component but DI is not enabled in settings"
35
- end
36
-
37
- unless settings.respond_to?(:remote) && settings.remote.enabled
38
- raise "Requested DI component but remote config is not enabled in settings"
39
- end
40
-
41
- unless environment_supported?(settings, logger)
42
- raise "DI does not support the environment (development or Ruby version too low or not MRI)"
43
- end
44
-
45
- new(settings, agent_settings, logger, code_tracker: DI.code_tracker, telemetry: telemetry)
46
- end
47
-
48
32
  # Checks whether the runtime environment is supported by
49
33
  # dynamic instrumentation. Currently we only require that, if Rails
50
34
  # is used, that Rails environment is not development because
@@ -1,12 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Datadog::DI::Serializer.register(condition: lambda { |value| ActiveRecord::Base === value }) do |serializer, value, name:, depth:| # steep:ignore
4
- # steep thinks all of the arguments are nil here
5
- # steep:ignore:start
3
+ Datadog::DI::Serializer.register(
4
+ # This serializer uses a dynamic condition to determine its applicability
5
+ # to a particular value. A simpler case could have been a serializer for
6
+ # a particular class, but in this case any ActiveRecord model is covered
7
+ # and they all have different classes.
8
+ #
9
+ # An alternative could have been to make DI specifically provide lookup
10
+ # logic for "instances of classes derived from X", but a condition Proc
11
+ # is more universal.
12
+ condition: lambda { |value| ActiveRecord::Base === value }
13
+ ) do |serializer, value, name:, depth:|
14
+ # +serializer+ is an instance of DI::Serializer.
15
+ # Use it to perform the serialization to primitive values.
16
+ #
17
+ # +value+ is the value to serialize. It should match the condition
18
+ # provided above, meaning it would be an ActiveRecord::Base instance.
19
+ #
20
+ # +name+ is the name of the (local/instance) variable being serialized.
21
+ # The name is used by DI for redaction (upstream of serialization logic),
22
+ # and could potentially be used for redaction here also.
23
+ #
24
+ # +depth+ is the remaining depth for serializing collections and objects.
25
+ # It should always be an integer.
26
+ # Reduce it by 1 when invoking +serialize_value+ on the contents of +value+.
27
+ # This serializer could also potentially do its own depth limiting.
28
+ #
29
+ # Steep: steep thinks all of the arguments are nil here
30
+ # Looks like it cannot handle kwargs in lambdas
31
+ # @type var depth: Integer
6
32
  value_to_serialize = {
7
33
  attributes: value.attributes,
8
34
  new_record: value.new_record?,
9
35
  }
10
- serializer.serialize_value(value_to_serialize, depth: depth ? depth - 1 : nil, type: value.class)
11
- # steep:ignore:end
36
+ serializer.serialize_value(value_to_serialize, depth: depth - 1, type: value.class)
12
37
  end
@@ -22,7 +22,8 @@ module Datadog
22
22
 
23
23
  private
24
24
 
25
- OPERATORS = {
25
+ # Steep: https://github.com/soutaro/steep/issues/363
26
+ OPERATORS = { # steep:ignore IncompatibleAssignment
26
27
  'eq' => '==',
27
28
  'ne' => '!=',
28
29
  'ge' => '>=',
@@ -31,16 +32,19 @@ module Datadog
31
32
  'lt' => '<',
32
33
  }.freeze
33
34
 
35
+ # Steep: https://github.com/soutaro/steep/issues/363
34
36
  SINGLE_ARG_METHODS = %w[
35
37
  len isEmpty isUndefined
36
- ].freeze
38
+ ].freeze # steep:ignore IncompatibleAssignment
37
39
 
40
+ # Steep: https://github.com/soutaro/steep/issues/363
38
41
  TWO_ARG_METHODS = %w[
39
42
  startsWith endsWith contains matches
40
43
  getmember index instanceof
41
- ].freeze
44
+ ].freeze # steep:ignore IncompatibleAssignment
42
45
 
43
- MULTI_ARG_METHODS = {
46
+ # Steep: https://github.com/soutaro/steep/issues/363
47
+ MULTI_ARG_METHODS = { # steep:ignore IncompatibleAssignment
44
48
  'and' => '&&',
45
49
  'or' => '||',
46
50
  }.freeze
@@ -17,7 +17,7 @@ module Datadog
17
17
 
18
18
  def len(var, var_name)
19
19
  case var
20
- when Array, String
20
+ when Array, String, Hash
21
21
  var.length
22
22
  else
23
23
  raise DI::Error::ExpressionEvaluationError, "Unsupported type for length: #{var.class}: #{var_name}"
@@ -10,6 +10,10 @@ module Datadog
10
10
  #
11
11
  # @api private
12
12
  class Error < StandardError
13
+ # Internal Dynamic Instrumentation error ("should never happen").
14
+ class InternalError < Error
15
+ end
16
+
13
17
  # Probe does not contain a line number (i.e., is not a line probe).
14
18
  class MissingLineNumber < Error
15
19
  end
@@ -38,6 +42,11 @@ module Datadog
38
42
  class ProbePreviouslyFailed < Error
39
43
  end
40
44
 
45
+ # Raised when trying to instrument a probe when there is existing
46
+ # instrumentation for the same probe id.
47
+ class AlreadyInstrumented < Error
48
+ end
49
+
41
50
  # Raised when installing a line probe and multiple files match the
42
51
  # specified path suffix.
43
52
  # A probe must be installed into one file only, since UI only
@@ -89,11 +89,7 @@ module Datadog
89
89
  # from the method but from outside of the method).
90
90
  Location = Struct.new(:path, :lineno, :label)
91
91
 
92
- def hook_method(probe, &block)
93
- unless block
94
- raise ArgumentError, 'block is required'
95
- end
96
-
92
+ def hook_method(probe, responder)
97
93
  lock.synchronize do
98
94
  if probe.instrumentation_module
99
95
  # Already instrumented, warn?
@@ -118,7 +114,9 @@ module Datadog
118
114
  settings = self.settings
119
115
 
120
116
  mod = Module.new do
121
- define_method(method_name) do |*args, **kwargs, &target_block| # steep:ignore
117
+ define_method(method_name) do |*args, **kwargs, &target_block| # steep:ignore NoMethod
118
+ # Steep: Unsure why it cannot detect kwargs in this block. Workaround:
119
+ # @type var kwargs: ::Hash[::Symbol, untyped]
122
120
  continue = true
123
121
  if condition = probe.condition
124
122
  begin
@@ -130,10 +128,34 @@ module Datadog
130
128
  caller_locations: caller_locations,
131
129
  )
132
130
  continue = condition.satisfied?(context)
133
- rescue
134
- raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
131
+ rescue => exc
132
+ # Evaluation error exception can be raised for "expected"
133
+ # errors, we probably need another setting to control whether
134
+ # these exceptions are propagated.
135
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions &&
136
+ !exc.is_a?(DI::Error::ExpressionEvaluationError)
137
+
138
+ if context
139
+ # We want to report evaluation errors for conditions
140
+ # as probe snapshots. However, if we failed to create
141
+ # the context, we won't be able to report anything as
142
+ # the probe notifier builder requires a context.
143
+ begin
144
+ responder.probe_condition_evaluation_failed_callback(context, exc)
145
+ rescue
146
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
147
+
148
+ # TODO log / report via telemetry?
149
+ end
150
+ else
151
+ _ = 42 # stop standard from wrecking this code
152
+
153
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
154
+
155
+ # TODO log / report via telemetry?
156
+ # If execution gets here, there is probably a bug in the tracer.
157
+ end
135
158
 
136
- # TODO log / report via telemetry?
137
159
  continue = false
138
160
  end
139
161
  end
@@ -146,7 +168,10 @@ module Datadog
146
168
  depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
147
169
  attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
148
170
  end
149
- start_time = Core::Utils::Time.get_time
171
+ # We intentionally do not use Core::Utils::Time.get_time
172
+ # here because the time provider may be overridden by the
173
+ # customer, and DI is not allowed to invoke customer code.
174
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
150
175
 
151
176
  rv = nil
152
177
  begin
@@ -170,7 +195,7 @@ module Datadog
170
195
  # the instrumentation callback runs.
171
196
  end
172
197
 
173
- duration = Core::Utils::Time.get_time - start_time
198
+ duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
174
199
  # The method itself is not part of the stack trace because
175
200
  # we are getting the stack trace from outside of the method.
176
201
  # Add the method in manually as the top frame.
@@ -186,7 +211,8 @@ module Datadog
186
211
  # that location here.
187
212
  []
188
213
  end
189
- caller_locs = method_frame + caller_locations # steep:ignore
214
+ # Steep: https://github.com/ruby/rbs/pull/2745
215
+ caller_locs = method_frame + caller_locations # steep:ignore ArgumentTypeMismatch
190
216
  # TODO capture arguments at exit
191
217
 
192
218
  context = Context.new(locals: nil, target_self: self,
@@ -195,8 +221,7 @@ module Datadog
195
221
  caller_locations: caller_locs,
196
222
  return_value: rv, duration: duration, exception: exc,)
197
223
 
198
- # & is to stop steep complaints, block is always present here.
199
- block&.call(context)
224
+ responder.probe_executed_callback(context)
200
225
  if exc
201
226
  raise exc
202
227
  else
@@ -237,6 +262,8 @@ module Datadog
237
262
 
238
263
  probe.instrumentation_module = mod
239
264
  cls.send(:prepend, mod)
265
+
266
+ DI.instrumented_count_inc(:method)
240
267
  end
241
268
  end
242
269
 
@@ -249,6 +276,8 @@ module Datadog
249
276
  if mod = probe.instrumentation_module
250
277
  mod.send(:remove_method, probe.method_name)
251
278
  probe.instrumentation_module = nil
279
+
280
+ DI.instrumented_count_dec(:method)
252
281
  end
253
282
  end
254
283
  end
@@ -258,11 +287,7 @@ module Datadog
258
287
  # not for eval'd code, unless the eval'd code is associated with
259
288
  # a file name and client invokes this method with the correct
260
289
  # file name for the eval'd code.
261
- def hook_line(probe, &block)
262
- unless block
263
- raise ArgumentError, 'No block given to hook_line'
264
- end
265
-
290
+ def hook_line(probe, responder)
266
291
  lock.synchronize do
267
292
  if probe.instrumentation_trace_point
268
293
  # Already instrumented, warn?
@@ -283,7 +308,10 @@ module Datadog
283
308
 
284
309
  iseq = nil
285
310
  if code_tracker
286
- ret = code_tracker.iseqs_for_path_suffix(probe.file) # steep:ignore
311
+ # Steep: Complex type narrowing (before calling hook_line,
312
+ # we check that probe.line? is true which itself checks that probe.file is not nil)
313
+ # Annotation do not work here as `file` is a method on probe, not a local variable.
314
+ ret = code_tracker.iseqs_for_path_suffix(probe.file) # steep:ignore ArgumentTypeMismatch
287
315
  unless ret
288
316
  if permit_untargeted_trace_points
289
317
  # Continue withoout targeting the trace point.
@@ -367,14 +395,44 @@ module Datadog
367
395
 
368
396
  if continue
369
397
  if condition = probe.condition
370
- context = Context.new(
371
- locals: Instrumenter.get_local_variables(tp),
372
- target_self: tp.self,
373
- probe: probe, settings: settings, serializer: serializer,
374
- path: tp.path,
375
- caller_locations: caller_locations,
376
- )
377
- continue = condition.satisfied?(context)
398
+ begin
399
+ context = Context.new(
400
+ locals: Instrumenter.get_local_variables(tp),
401
+ target_self: tp.self,
402
+ probe: probe, settings: settings, serializer: serializer,
403
+ path: tp.path,
404
+ caller_locations: caller_locations,
405
+ )
406
+ continue = condition.satisfied?(context)
407
+ rescue => exc
408
+ # Evaluation error exception can be raised for "expected"
409
+ # errors, we probably need another setting to control whether
410
+ # these exceptions are propagated.
411
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions &&
412
+ !exc.is_a?(DI::Error::ExpressionEvaluationError)
413
+
414
+ continue = false
415
+ if context
416
+ # We want to report evaluation errors for conditions
417
+ # as probe snapshots. However, if we failed to create
418
+ # the context, we won't be able to report anything as
419
+ # the probe notifier builder requires a context.
420
+ begin
421
+ responder.probe_condition_evaluation_failed_callback(context, condition, exc)
422
+ rescue
423
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
424
+
425
+ # TODO log / report via telemetry?
426
+ end
427
+ else
428
+ _ = 42 # stop standard from wrecking this code
429
+
430
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
431
+
432
+ # TODO log / report via telemetry?
433
+ # If execution gets here, there is probably a bug in the tracer.
434
+ end
435
+ end
378
436
  end
379
437
  end
380
438
 
@@ -393,8 +451,7 @@ module Datadog
393
451
  caller_locations: caller_locations,
394
452
  )
395
453
 
396
- # & is to stop steep complaints, block is always present here.
397
- block&.call(context)
454
+ responder.probe_executed_callback(context)
398
455
  end
399
456
  rescue => exc
400
457
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
@@ -409,9 +466,11 @@ module Datadog
409
466
  # TODO test this path
410
467
  end
411
468
 
412
- # TODO internal check - remove or use a proper exception
469
+ # Internal sanity check - untargeted trace points create a huge
470
+ # performance impact, and we absolutely do not want to set them
471
+ # accidentally.
413
472
  if !iseq && !permit_untargeted_trace_points
414
- raise "Trying to use an untargeted trace point when user did not permit it"
473
+ raise Error::InternalError, "Trying to use an untargeted trace point when user did not permit it"
415
474
  end
416
475
 
417
476
  lock.synchronize do
@@ -424,12 +483,16 @@ module Datadog
424
483
  # actual_path could be nil if we don't use targeted trace points.
425
484
  probe.instrumented_path = actual_path
426
485
 
427
- if iseq
486
+ # TracePoint#enable returns false when it succeeds.
487
+ rv = if iseq
428
488
  tp.enable(target: iseq, target_line: line_no)
429
489
  else
430
490
  tp.enable
431
491
  end
432
- # TracePoint#enable returns false when it succeeds.
492
+
493
+ DI.instrumented_count_inc(:line)
494
+
495
+ rv
433
496
  end
434
497
  true
435
498
  end
@@ -439,15 +502,17 @@ module Datadog
439
502
  if tp = probe.instrumentation_trace_point
440
503
  tp.disable
441
504
  probe.instrumentation_trace_point = nil
505
+
506
+ DI.instrumented_count_dec(:line)
442
507
  end
443
508
  end
444
509
  end
445
510
 
446
- def hook(probe, &block)
511
+ def hook(probe, responder)
447
512
  if probe.method?
448
- hook_method(probe, &block)
513
+ hook_method(probe, responder)
449
514
  elsif probe.line?
450
- hook_line(probe, &block)
515
+ hook_line(probe, responder)
451
516
  else
452
517
  # TODO add test coverage for this path
453
518
  logger.debug { "di: unknown probe type to hook: #{probe}" }
@@ -8,7 +8,7 @@ module Datadog
8
8
  #
9
9
  # @api private
10
10
  class Logger
11
- extend Forwardable # steep:ignore
11
+ extend Forwardable
12
12
 
13
13
  def initialize(settings, target)
14
14
  @settings = settings
@@ -18,7 +18,7 @@ module Datadog
18
18
  attr_reader :settings
19
19
  attr_reader :target
20
20
 
21
- def_delegators :target, :debug # steep:ignore
21
+ def_delegators :target, :debug
22
22
 
23
23
  def trace(&block)
24
24
  if settings.dynamic_instrumentation.internal.trace_logging