datadog 2.39.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 (296) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +269 -44
  3. data/README.md +3 -0
  4. data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +12 -24
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +256 -193
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -5
  7. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +0 -2
  8. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +19 -20
  9. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.h +5 -1
  10. data/ext/datadog_profiling_native_extension/collectors_stack.c +267 -171
  11. data/ext/datadog_profiling_native_extension/collectors_stack.h +17 -3
  12. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +307 -217
  13. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +10 -5
  14. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +5 -4
  15. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +19 -11
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -10
  17. data/ext/datadog_profiling_native_extension/extconf.rb +67 -99
  18. data/ext/datadog_profiling_native_extension/heap_recorder.c +353 -257
  19. data/ext/datadog_profiling_native_extension/heap_recorder.h +50 -31
  20. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +3 -27
  21. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +853 -496
  22. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +42 -15
  23. data/ext/datadog_profiling_native_extension/profiling.c +30 -23
  24. data/ext/datadog_profiling_native_extension/ruby_helpers.c +4 -80
  25. data/ext/datadog_profiling_native_extension/ruby_helpers.h +4 -7
  26. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +0 -2
  27. data/ext/datadog_profiling_native_extension/stack_recorder.c +116 -89
  28. data/ext/datadog_profiling_native_extension/stack_recorder.h +12 -5
  29. data/ext/datadog_profiling_native_extension/time_helpers.c +2 -4
  30. data/ext/datadog_profiling_native_extension/time_helpers.h +15 -15
  31. data/ext/libdatadog_api/datadog_ruby_common.c +19 -11
  32. data/ext/libdatadog_api/datadog_ruby_common.h +7 -10
  33. data/ext/libdatadog_api/di.c +43 -3
  34. data/ext/libdatadog_api/extconf.rb +6 -1
  35. data/ext/libdatadog_api/feature_flags.c +8 -0
  36. data/ext/libdatadog_api/init.c +9 -1
  37. data/ext/libdatadog_api/otel_thread_context.c +274 -0
  38. data/ext/libdatadog_api/otel_thread_context.h +5 -0
  39. data/ext/libdatadog_api/process_discovery.c +15 -1
  40. data/ext/libdatadog_api/trace_exporter.c +2078 -0
  41. data/ext/libdatadog_api/trace_exporter.h +5 -0
  42. data/ext/libdatadog_extconf_helpers.rb +14 -9
  43. data/lib/datadog/ai_guard/component.rb +17 -8
  44. data/lib/datadog/ai_guard/configuration/ext.rb +2 -0
  45. data/lib/datadog/ai_guard/configuration.rb +13 -0
  46. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +48 -55
  47. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +1 -1
  48. data/lib/datadog/ai_guard/contrib/ruby_llm/message_adapter.rb +76 -0
  49. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +1 -0
  50. data/lib/datadog/ai_guard/contrib/ruby_llm/redaction_change_set.rb +135 -0
  51. data/lib/datadog/ai_guard/evaluation/client.rb +50 -0
  52. data/lib/datadog/ai_guard/evaluation/content_part.rb +12 -0
  53. data/lib/datadog/ai_guard/evaluation/message.rb +25 -7
  54. data/lib/datadog/ai_guard/evaluation/message_builder.rb +31 -0
  55. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +6 -6
  56. data/lib/datadog/ai_guard/evaluation/outcome.rb +24 -0
  57. data/lib/datadog/ai_guard/evaluation/request.rb +8 -64
  58. data/lib/datadog/ai_guard/evaluation/response.rb +33 -0
  59. data/lib/datadog/ai_guard/evaluation/result.rb +11 -18
  60. data/lib/datadog/ai_guard/evaluation/tool_call.rb +15 -2
  61. data/lib/datadog/ai_guard/evaluation.rb +36 -14
  62. data/lib/datadog/ai_guard/ext.rb +4 -1
  63. data/lib/datadog/ai_guard/{api_client.rb → http_client.rb} +7 -5
  64. data/lib/datadog/ai_guard/metrics/telemetry.rb +43 -0
  65. data/lib/datadog/ai_guard/redaction/replacements.rb +97 -0
  66. data/lib/datadog/ai_guard/redaction/result.rb +29 -0
  67. data/lib/datadog/ai_guard/redaction.rb +80 -0
  68. data/lib/datadog/ai_guard.rb +58 -57
  69. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +21 -42
  70. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +1 -1
  71. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +1 -1
  72. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +1 -1
  73. data/lib/datadog/appsec/assets/blocked.html +1 -108
  74. data/lib/datadog/appsec/configuration.rb +11 -2
  75. data/lib/datadog/appsec/context.rb +1 -1
  76. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +1 -1
  77. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +2 -2
  78. data/lib/datadog/appsec/contrib/devise/configuration.rb +1 -1
  79. data/lib/datadog/appsec/contrib/devise/patcher.rb +1 -1
  80. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +2 -2
  81. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +2 -2
  82. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -1
  83. data/lib/datadog/appsec/contrib/rack/ext.rb +2 -2
  84. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +2 -2
  85. data/lib/datadog/appsec/contrib/rack/patcher.rb +1 -1
  86. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +6 -3
  87. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +30 -3
  88. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +8 -7
  89. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +2 -1
  90. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +2 -2
  91. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +15 -3
  92. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +8 -7
  93. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +2 -1
  94. data/lib/datadog/appsec/processor/rule_loader.rb +12 -12
  95. data/lib/datadog/appsec/response.rb +1 -1
  96. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +3 -3
  97. data/lib/datadog/appsec/security_engine/engine.rb +5 -4
  98. data/lib/datadog/core/configuration/components.rb +50 -19
  99. data/lib/datadog/core/configuration/components_state.rb +8 -1
  100. data/lib/datadog/core/configuration/option_definition.rb +1 -1
  101. data/lib/datadog/core/configuration/options.rb +1 -1
  102. data/lib/datadog/core/configuration/settings.rb +38 -43
  103. data/lib/datadog/core/configuration/supported_configurations.rb +18 -2
  104. data/lib/datadog/core/crashtracking/component.rb +5 -1
  105. data/lib/datadog/core/diagnostics/environment_logger.rb +36 -1
  106. data/lib/datadog/core/environment/gc.rb +1 -1
  107. data/lib/datadog/core/environment/vm_cache.rb +1 -1
  108. data/lib/datadog/core/environment/yjit.rb +2 -2
  109. data/lib/datadog/core/metrics/helpers.rb +1 -1
  110. data/lib/datadog/core/metrics/options.rb +2 -2
  111. data/lib/datadog/core/process_discovery.rb +6 -2
  112. data/lib/datadog/core/remote/client/capabilities.rb +32 -21
  113. data/lib/datadog/core/remote/client.rb +45 -4
  114. data/lib/datadog/core/remote/component.rb +30 -7
  115. data/lib/datadog/core/remote/configuration/repository.rb +2 -2
  116. data/lib/datadog/core/remote/dispatcher.rb +47 -6
  117. data/lib/datadog/core/remote/worker.rb +8 -0
  118. data/lib/datadog/core/telemetry/configuration_value.rb +38 -0
  119. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +3 -1
  120. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +1 -1
  121. data/lib/datadog/core/telemetry/event/app_started.rb +5 -24
  122. data/lib/datadog/core/telemetry/event/generate_metrics.rb +1 -1
  123. data/lib/datadog/core/telemetry/event/log.rb +2 -2
  124. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
  125. data/lib/datadog/core/telemetry/metric.rb +2 -2
  126. data/lib/datadog/core/telemetry/request.rb +2 -2
  127. data/lib/datadog/core/transport/http.rb +1 -1
  128. data/lib/datadog/core/transport/transport.rb +1 -1
  129. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +170 -18
  130. data/lib/datadog/core/utils/sequence.rb +3 -6
  131. data/lib/datadog/core/utils/time.rb +16 -54
  132. data/lib/datadog/core/workers/runtime_metrics.rb +1 -1
  133. data/lib/datadog/data_streams/pathway_context.rb +20 -22
  134. data/lib/datadog/data_streams/processor.rb +45 -14
  135. data/lib/datadog/di/boot.rb +1 -0
  136. data/lib/datadog/di/capture_expression_evaluator/result.rb +30 -0
  137. data/lib/datadog/di/capture_expression_evaluator.rb +39 -9
  138. data/lib/datadog/di/component.rb +108 -3
  139. data/lib/datadog/di/configuration.rb +53 -2
  140. data/lib/datadog/di/context.rb +7 -2
  141. data/lib/datadog/di/contrib/active_record.rb +8 -2
  142. data/lib/datadog/di/el/compiler.rb +88 -12
  143. data/lib/datadog/di/el/evaluator.rb +82 -2
  144. data/lib/datadog/di/el/expression.rb +13 -2
  145. data/lib/datadog/di/evaluation_failure.rb +28 -0
  146. data/lib/datadog/di/guardrails_telemetry/reason.rb +87 -0
  147. data/lib/datadog/di/guardrails_telemetry.rb +203 -0
  148. data/lib/datadog/di/instrumenter.rb +106 -15
  149. data/lib/datadog/di/logger.rb +32 -10
  150. data/lib/datadog/di/probe.rb +9 -1
  151. data/lib/datadog/di/probe_builder.rb +7 -6
  152. data/lib/datadog/di/probe_manager.rb +56 -8
  153. data/lib/datadog/di/probe_notification_builder.rb +31 -28
  154. data/lib/datadog/di/probe_notifier_worker.rb +56 -6
  155. data/lib/datadog/di/proc_responder.rb +1 -1
  156. data/lib/datadog/di/remote.rb +49 -5
  157. data/lib/datadog/di/serializer.rb +172 -55
  158. data/lib/datadog/di/snapshot_event.rb +38 -0
  159. data/lib/datadog/di/transport/http.rb +13 -3
  160. data/lib/datadog/di/transport/input.rb +32 -7
  161. data/lib/datadog/error_tracking/component.rb +1 -1
  162. data/lib/datadog/kit/appsec/events/v2.rb +2 -2
  163. data/lib/datadog/open_feature/activation.rb +268 -0
  164. data/lib/datadog/open_feature/agentless/configuration_source.rb +246 -0
  165. data/lib/datadog/open_feature/agentless/parser.rb +43 -0
  166. data/lib/datadog/open_feature/agentless/response.rb +19 -0
  167. data/lib/datadog/open_feature/agentless/transport.rb +103 -0
  168. data/lib/datadog/open_feature/component.rb +85 -13
  169. data/lib/datadog/open_feature/configuration/agentless_endpoint.rb +90 -0
  170. data/lib/datadog/open_feature/configuration/source/resolution.rb +22 -0
  171. data/lib/datadog/open_feature/configuration/source.rb +43 -0
  172. data/lib/datadog/open_feature/configuration.rb +124 -0
  173. data/lib/datadog/open_feature/evaluation_engine.rb +29 -3
  174. data/lib/datadog/open_feature/exposures/batch_builder.rb +1 -1
  175. data/lib/datadog/open_feature/exposures/event.rb +15 -8
  176. data/lib/datadog/open_feature/ext.rb +19 -0
  177. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +236 -80
  178. data/lib/datadog/open_feature/flag_evaluation/writer.rb +179 -68
  179. data/lib/datadog/open_feature/hooks/evaluation_telemetry_opt_out_hook.rb +25 -0
  180. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +24 -21
  181. data/lib/datadog/open_feature/native_evaluator.rb +20 -6
  182. data/lib/datadog/open_feature/noop_evaluator.rb +5 -0
  183. data/lib/datadog/open_feature/provider.rb +315 -10
  184. data/lib/datadog/open_feature/remote.rb +5 -5
  185. data/lib/datadog/open_feature/transport.rb +5 -0
  186. data/lib/datadog/open_feature.rb +31 -1
  187. data/lib/datadog/opentelemetry/sdk/propagator.rb +1 -1
  188. data/lib/datadog/opentelemetry/signal_configuration.rb +13 -9
  189. data/lib/datadog/opentelemetry/trace.rb +3 -0
  190. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +10 -2
  191. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -7
  192. data/lib/datadog/profiling/collectors/info.rb +40 -13
  193. data/lib/datadog/profiling/collectors/thread_context.rb +6 -17
  194. data/lib/datadog/profiling/component.rb +33 -39
  195. data/lib/datadog/profiling/exporter.rb +10 -10
  196. data/lib/datadog/profiling/tasks/exec.rb +1 -1
  197. data/lib/datadog/profiling.rb +4 -0
  198. data/lib/datadog/symbol_database/extractor.rb +89 -107
  199. data/lib/datadog/tracing/component.rb +44 -2
  200. data/lib/datadog/tracing/configuration/dynamic/option.rb +4 -0
  201. data/lib/datadog/tracing/configuration/dynamic.rb +5 -3
  202. data/lib/datadog/tracing/configuration/ext.rb +4 -0
  203. data/lib/datadog/tracing/configuration/settings.rb +36 -0
  204. data/lib/datadog/tracing/context.rb +24 -2
  205. data/lib/datadog/tracing/context_provider.rb +17 -5
  206. data/lib/datadog/tracing/contrib/action_cable/events.rb +1 -1
  207. data/lib/datadog/tracing/contrib/action_mailer/events.rb +1 -1
  208. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +2 -2
  209. data/lib/datadog/tracing/contrib/action_view/events.rb +1 -1
  210. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +1 -1
  211. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +1 -1
  212. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
  213. data/lib/datadog/tracing/contrib/active_record/events.rb +1 -1
  214. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +2 -2
  215. data/lib/datadog/tracing/contrib/aws/ext.rb +1 -1
  216. data/lib/datadog/tracing/contrib/aws/services.rb +1 -1
  217. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +1 -1
  218. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +10 -5
  219. data/lib/datadog/tracing/contrib/excon/middleware.rb +10 -3
  220. data/lib/datadog/tracing/contrib/ext.rb +3 -3
  221. data/lib/datadog/tracing/contrib/faraday/middleware.rb +10 -4
  222. data/lib/datadog/tracing/contrib/grape/endpoint.rb +32 -5
  223. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +1 -1
  224. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +1 -1
  225. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +1 -1
  226. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +2 -1
  227. data/lib/datadog/tracing/contrib/http/instrumentation.rb +16 -1
  228. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +8 -2
  229. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +10 -4
  230. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +4 -0
  231. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +7 -2
  232. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +7 -2
  233. data/lib/datadog/tracing/contrib/kafka/events.rb +1 -1
  234. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +1 -1
  235. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +1 -1
  236. data/lib/datadog/tracing/contrib/opensearch/ext.rb +1 -1
  237. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +1 -1
  238. data/lib/datadog/tracing/contrib/patchable.rb +1 -1
  239. data/lib/datadog/tracing/contrib/patcher.rb +1 -1
  240. data/lib/datadog/tracing/contrib/que/tracer.rb +1 -1
  241. data/lib/datadog/tracing/contrib/racecar/events.rb +1 -1
  242. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +1 -1
  243. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +1 -1
  244. data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
  245. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +1 -1
  246. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +1 -1
  247. data/lib/datadog/tracing/contrib/redis/quantize.rb +36 -2
  248. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +9 -3
  249. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +10 -3
  250. data/lib/datadog/tracing/contrib/sequel/database.rb +1 -5
  251. data/lib/datadog/tracing/contrib/sequel/jdbc_connection_string.rb +140 -0
  252. data/lib/datadog/tracing/contrib/sequel/utils.rb +50 -63
  253. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +1 -1
  254. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +1 -1
  255. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -1
  256. data/lib/datadog/tracing/contrib/utils/quantization/hash_formatter.rb +1 -1
  257. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +108 -3
  258. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +1 -1
  259. data/lib/datadog/tracing/correlation.rb +2 -2
  260. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -2
  261. data/lib/datadog/tracing/distributed/baggage.rb +1 -3
  262. data/lib/datadog/tracing/distributed/datadog.rb +3 -3
  263. data/lib/datadog/tracing/distributed/propagation.rb +3 -0
  264. data/lib/datadog/tracing/distributed/propagation_policy.rb +5 -0
  265. data/lib/datadog/tracing/distributed/trace_context.rb +14 -271
  266. data/lib/datadog/tracing/distributed/trace_state/datadog.rb +233 -0
  267. data/lib/datadog/tracing/distributed/trace_state/ext.rb +44 -0
  268. data/lib/datadog/tracing/distributed/trace_state/open_telemetry.rb +156 -0
  269. data/lib/datadog/tracing/distributed/trace_state.rb +121 -0
  270. data/lib/datadog/tracing/metadata/ext.rb +3 -0
  271. data/lib/datadog/tracing/metadata/tagging.rb +1 -1
  272. data/lib/datadog/tracing/otel_thread_context.rb +97 -0
  273. data/lib/datadog/tracing/remote.rb +236 -37
  274. data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -0
  275. data/lib/datadog/tracing/span.rb +1 -1
  276. data/lib/datadog/tracing/span_operation.rb +1 -1
  277. data/lib/datadog/tracing/sync_writer.rb +8 -3
  278. data/lib/datadog/tracing/trace_digest.rb +22 -4
  279. data/lib/datadog/tracing/trace_operation.rb +22 -10
  280. data/lib/datadog/tracing/tracer.rb +28 -7
  281. data/lib/datadog/tracing/transport/http/telemetry.rb +94 -0
  282. data/lib/datadog/tracing/transport/http/traces.rb +2 -0
  283. data/lib/datadog/tracing/transport/io/traces.rb +1 -1
  284. data/lib/datadog/tracing/transport/native/response.rb +71 -0
  285. data/lib/datadog/tracing/transport/native/telemetry.rb +17 -0
  286. data/lib/datadog/tracing/transport/native.rb +395 -0
  287. data/lib/datadog/tracing/transport/span_events_negotiation.rb +41 -0
  288. data/lib/datadog/tracing/transport/telemetry.rb +62 -0
  289. data/lib/datadog/tracing/transport/trace_formatter.rb +14 -4
  290. data/lib/datadog/tracing/transport/traces.rb +85 -38
  291. data/lib/datadog/tracing/writer.rb +17 -4
  292. data/lib/datadog/version.rb +1 -1
  293. metadata +51 -13
  294. data/lib/datadog/ai_guard/evaluation/content_builder.rb +0 -31
  295. data/lib/datadog/appsec/utils/hash_coercion.rb +0 -23
  296. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -69
@@ -13,7 +13,7 @@ module Datadog
13
13
  ALL = [
14
14
  Events::Consume,
15
15
  Events::Batch,
16
- Events::Message
16
+ Events::Message,
17
17
  ].freeze
18
18
 
19
19
  module_function
@@ -15,7 +15,7 @@ module Datadog
15
15
  response: %w[
16
16
  Content-Type
17
17
  X-Request-ID
18
- ]
18
+ ],
19
19
  }.freeze
20
20
 
21
21
  option :enabled do |o|
@@ -10,7 +10,7 @@ module Datadog
10
10
  module HeaderTagging
11
11
  DATADOG_REQUEST_ATTRIBUTION_HEADERS = [
12
12
  "x-datadog-endpoint-scan",
13
- "x-datadog-security-test"
13
+ "x-datadog-security-test",
14
14
  ].freeze
15
15
 
16
16
  def self.tag_request_headers(span, env, configuration)
@@ -70,7 +70,7 @@ module Datadog
70
70
  module Patcher
71
71
  PATCHERS = [
72
72
  MiddlewarePatcher,
73
- MiddlewareNamePatcher
73
+ MiddlewareNamePatcher,
74
74
  ].freeze
75
75
 
76
76
  module_function
@@ -27,7 +27,7 @@ module Datadog
27
27
  options = {
28
28
  service: configuration[:web_service_name],
29
29
  start_time: start_time,
30
- type: Tracing::Metadata::Ext::HTTP::TYPE_PROXY
30
+ type: Tracing::Metadata::Ext::HTTP::TYPE_PROXY,
31
31
  }
32
32
 
33
33
  request_span = Tracing.trace(Ext::SPAN_HTTP_PROXY_REQUEST, **options)
@@ -34,7 +34,7 @@ module Datadog
34
34
  host: hash[:host],
35
35
  port: hash[:port],
36
36
  db: hash[:db],
37
- scheme: hash[:scheme]
37
+ scheme: hash[:scheme],
38
38
  }
39
39
  end
40
40
 
@@ -15,6 +15,9 @@ module Datadog
15
15
 
16
16
  AUTH_COMMANDS = %w[AUTH auth].freeze
17
17
 
18
+ CONNECTION_SETUP_COMMANDS = Set.new(%w[HELLO]).freeze
19
+ CLIENT_CONNECTION_SETUP_SUBCOMMANDS = Set.new(%w[SETINFO SETNAME]).freeze
20
+
18
21
  MULTI_VERB_COMMANDS = Set.new(
19
22
  %w[
20
23
  ACL
@@ -40,7 +43,7 @@ module Datadog
40
43
 
41
44
  def format_command_args(command_args)
42
45
  command_args = resolve_command_args(command_args)
43
- return "AUTH ?" if auth_command?(command_args)
46
+ return "AUTH ?" if auth_command?(command_args) || hello_auth_command?(command_args)
44
47
 
45
48
  verb, *args = command_args.map { |x| format_arg(x) }
46
49
  Core::Utils.truncate("#{verb.upcase} #{args.join(" ")}", CMD_MAX_LEN, TOO_LONG_MARK)
@@ -51,6 +54,8 @@ module Datadog
51
54
 
52
55
  return get_verb(command_args.first) if command_args.first.is_a?(Array)
53
56
 
57
+ return "AUTH" if hello_auth_command?(command_args)
58
+
54
59
  verb = command_args.first.to_s.upcase
55
60
  return verb unless MULTI_VERB_COMMANDS.include?(verb) && command_args[1]
56
61
 
@@ -64,6 +69,35 @@ module Datadog
64
69
  AUTH_COMMANDS.include?(verb)
65
70
  end
66
71
 
72
+ # Identifies a RESP3 `HELLO ... AUTH user pass` handshake command, which embeds
73
+ # credentials the same way a standalone `AUTH` command does and must be redacted
74
+ # (and kept visible as its own span) rather than treated as a silent connection
75
+ # bootstrap command.
76
+ def hello_auth_command?(command_args)
77
+ return false unless command_args.is_a?(Array) && !command_args.empty?
78
+
79
+ # `redis-client` always emits this as `["HELLO", "3", "AUTH", username, password]` — check
80
+ # the fixed "AUTH" keyword position only, never the username/password values themselves,
81
+ # since those are untrusted, possibly-binary bulk strings that `#upcase` can raise on.
82
+ return false if command_args.length < 3
83
+ command_args[0].to_s.upcase == "HELLO" && command_args[2].to_s.upcase == "AUTH"
84
+ end
85
+
86
+ # Identifies protocol-level connection bootstrap commands (`HELLO`, `CLIENT SETINFO`,
87
+ # `CLIENT SETNAME`) that `redis-client` sends as part of its per-connection handshake,
88
+ # so they can be excluded from pipeline resource naming. A `HELLO` carrying `AUTH`
89
+ # credentials is excluded from this so authentication remains visible as its own span.
90
+ def connection_setup_command?(command_args)
91
+ command_args = resolve_command_args(command_args)
92
+ return false unless command_args.is_a?(Array) && !command_args.empty?
93
+ return false if hello_auth_command?(command_args)
94
+
95
+ verb = command_args.first.to_s.upcase
96
+ return true if CONNECTION_SETUP_COMMANDS.include?(verb)
97
+
98
+ verb == "CLIENT" && CLIENT_CONNECTION_SETUP_SUBCOMMANDS.include?(command_args[1].to_s.upcase)
99
+ end
100
+
67
101
  # Unwraps command array when Redis is called with the following syntax:
68
102
  # redis.call([:cmd, 'arg1', ...])
69
103
  def resolve_command_args(command_args)
@@ -72,7 +106,7 @@ module Datadog
72
106
  command_args
73
107
  end
74
108
 
75
- private_class_method :auth_command?, :resolve_command_args
109
+ private_class_method :auth_command?, :hello_auth_command?, :resolve_command_args
76
110
  end
77
111
  end
78
112
  end
@@ -37,12 +37,18 @@ module Datadog
37
37
  end
38
38
 
39
39
  def call_pipelined(client, commands, service_name, command_args)
40
+ # `redis-client` sends its per-connection handshake (`HELLO`, `CLIENT SETINFO`/`SETNAME`)
41
+ # as a pipeline through this same code path. Excluding those from resource naming keeps
42
+ # the resource focused on application-issued commands (e.g. a lone `SELECT` prelude).
43
+ traced_commands = commands.reject { |c| Contrib::Redis::Quantize.connection_setup_command?(c) }
44
+ return yield if traced_commands.empty? && !commands.empty?
45
+
40
46
  Tracing.trace(Redis::Ext::SPAN_COMMAND, type: Redis::Ext::TYPE, service: service_name) do |span|
41
47
  span.set_tag(Tracing::Metadata::Ext::TAG_SVC_SRC, Redis::Ext::TAG_COMPONENT)
42
- raw_command = get_pipeline_commands(commands, true)
43
- span.resource = command_args ? raw_command : get_pipeline_commands(commands, false)
48
+ raw_command = get_pipeline_commands(traced_commands, true)
49
+ span.resource = command_args ? raw_command : get_pipeline_commands(traced_commands, false)
44
50
 
45
- span.set_metric Contrib::Redis::Ext::METRIC_PIPELINE_LEN, commands.length
51
+ span.set_metric Contrib::Redis::Ext::METRIC_PIPELINE_LEN, traced_commands.length
46
52
 
47
53
  Contrib::Redis::Tags.set_common_tags(client, span, raw_command)
48
54
 
@@ -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(
@@ -46,11 +46,7 @@ module Datadog
46
46
  end
47
47
 
48
48
  def parse_opts(sql, opts)
49
- db_opts = if ::Sequel::VERSION < "3.41.0" && self.class.to_s !~ /Dataset$/
50
- @opts
51
- elsif instance_variable_defined?(:@pool) && @pool
52
- @pool.db.opts
53
- end
49
+ db_opts = @pool.db.opts if instance_variable_defined?(:@pool) && @pool
54
50
  sql = sql.is_a?(::Sequel::SQL::Expression) ? literal(sql) : sql.to_s
55
51
 
56
52
  Utils.parse_opts(sql, opts, db_opts)
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Contrib
6
+ module Sequel
7
+ # Extracts connection metadata from JDBC connection strings.
8
+ module JDBCConnectionString
9
+ MAX_BYTES = 8_192
10
+
11
+ # Matches JDBC connection strings whose subname uses `[transport:]//host...`.
12
+ CONNECTION_STRING_PATTERN =
13
+ %r{\Ajdbc:[a-z][a-z0-9+.-]*:(?:[a-z][a-z0-9+.-]*:)?//(?<subname>.+)\z}im
14
+
15
+ # Extracts the host and port from a string.
16
+ # The host can be a multi-host value, a bracketed IPv6 host, or an ordinary host.
17
+ HOST_AND_PORT_PATTERN =
18
+ /\A(?:\[(?<ipv6_host>[^\[\]]+)\]|(?<host>[^,]+(?:,[^,]+)+|[^:]+))(?::(?<port>\d+))?\z/
19
+
20
+ DATABASE_PROPERTY_PATTERN =
21
+ /(?:\A|[&;])(?:databaseName|database)=(?<value>[^&;]+)/i
22
+ LIBRARIES_PROPERTY_PATTERN =
23
+ /(?:\A|[&;])libraries=,*(?<value>[^,&;]+)/i
24
+ DATABASE_PATH_DELIMITER_PATTERN = %r{[:@\[\]?&=#]}
25
+
26
+ private_constant :MAX_BYTES, :CONNECTION_STRING_PATTERN,
27
+ :DATABASE_PROPERTY_PATTERN, :LIBRARIES_PROPERTY_PATTERN,
28
+ :DATABASE_PATH_DELIMITER_PATTERN, :HOST_AND_PORT_PATTERN
29
+
30
+ class << self
31
+ # The returned Hash guarantees the existence of all keys,
32
+ # but Hash values can be `nil` when not parseable from the connection string.
33
+ #
34
+ # @param connection_string [String, nil] the JDBC connection string to parse
35
+ # @return [Hash{Symbol => String, nil}]
36
+ # - `:host` — host value when the subname uses authority syntax: `//host[:port]`
37
+ # - `:port` — port when the subname uses authority syntax: `//host[:port]`
38
+ # - `:database` — best-effort database name
39
+ def parse(connection_string)
40
+ # @type var result: metadata
41
+ result = {host: nil, port: nil, database: nil}
42
+ return result unless connection_string.is_a?(String) && connection_string.valid_encoding?
43
+
44
+ if connection_string.bytesize > MAX_BYTES
45
+ # Strip userinfo before truncation. Otherwise, an `@` beyond the limit could be
46
+ # discarded and leave a credential prefix looking like a valid authority/host.
47
+ authority_marker = connection_string.index("//")
48
+ if authority_marker
49
+ authority_start = authority_marker + 2
50
+ authority_end = [
51
+ connection_string.index("/", authority_start),
52
+ connection_string.index(";", authority_start),
53
+ connection_string.index("?", authority_start),
54
+ ].compact.min || connection_string.length
55
+ userinfo_end = connection_string.rindex("@", authority_end - 1)
56
+
57
+ if userinfo_end && userinfo_end >= authority_start
58
+ connection_string =
59
+ # Steep: https://github.com/soutaro/steep/issues/1219
60
+ connection_string[0...authority_start] + # steep:ignore NoMethod
61
+ connection_string[(userinfo_end + 1)..-1].to_s
62
+ end
63
+ end
64
+
65
+ # Keep one extra byte, then let `chop` safely remove multi-byte unicode characters.
66
+ if connection_string.bytesize > MAX_BYTES
67
+ connection_string = connection_string.byteslice(0, MAX_BYTES + 1).chop
68
+ end
69
+ end
70
+
71
+ match = CONNECTION_STRING_PATTERN.match(connection_string)
72
+ return result unless match
73
+
74
+ # We start with: `host[:port][/database][;properties][?query]`.
75
+ subname = match[:subname]
76
+
77
+ # Extract `query` from the end, leaving `host[:port][/database][;properties]`.
78
+ subname, query_separator, query = subname.partition("?")
79
+ query = nil if query_separator.empty?
80
+
81
+ # Extract `properties` next, leaving `host[:port][/database]`.
82
+ subname, properties_separator, properties = subname.partition(";")
83
+ properties = nil if properties_separator.empty?
84
+
85
+ # Separate the authority (`host[:port]`) from the optional database path.
86
+ authority, path = subname.split("/", 2)
87
+ return result if authority.nil? || authority.empty?
88
+
89
+ host, port = host_and_port_from_authority(authority)
90
+ return result unless host
91
+
92
+ database = database_from_path(path) ||
93
+ database_from_properties(properties) || database_from_properties(query)
94
+
95
+ {host: host, port: port, database: database}
96
+ rescue Encoding::CompatibilityError, ArgumentError
97
+ result
98
+ end
99
+
100
+ private
101
+
102
+ def database_from_path(path)
103
+ return if path.nil? || path.empty?
104
+
105
+ database = path.split(DATABASE_PATH_DELIMITER_PATTERN, 2).first
106
+ return if database.nil? || database.empty?
107
+
108
+ database
109
+ end
110
+
111
+ def host_and_port_from_authority(authority)
112
+ # Discard optional `userinfo@`, retaining only `host[:port]`.
113
+ authority = authority.rpartition("@").last
114
+ return if authority.empty?
115
+
116
+ match = HOST_AND_PORT_PATTERN.match(authority)
117
+ return unless match
118
+
119
+ # Only one of `ipv6_host` or `host` will be populated.
120
+ host = match[:ipv6_host] || match[:host]
121
+ port = match[:port]
122
+
123
+ [host, port]
124
+ end
125
+
126
+ def database_from_properties(properties)
127
+ return unless properties
128
+
129
+ database = DATABASE_PROPERTY_PATTERN.match(properties)
130
+ return database[:value] if database
131
+
132
+ libraries = LIBRARIES_PROPERTY_PATTERN.match(properties)
133
+ libraries && libraries[:value]
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "uri"
4
-
5
3
  require_relative "../../metadata/ext"
6
4
  require_relative "../utils/database"
7
5
  require_relative "ext"
6
+ require_relative "jdbc_connection_string"
8
7
  require_relative "../ext"
9
8
  require_relative "../span_attribute_schema"
10
9
 
@@ -14,11 +13,6 @@ module Datadog
14
13
  module Sequel
15
14
  # General purpose functions for Sequel
16
15
  module Utils
17
- JDBC_URI_PATTERN = %r{\Ajdbc:(?<vendor>[a-z][a-z0-9+.-]*):(?<location>//[^\r\n]*)\z}i
18
- DATABASE_PROPERTY_PATTERN =
19
- /(?:\A|[&;])(?<key>databaseName|database|libraries)=(?<value>[^&;]+)/i
20
- private_constant :JDBC_URI_PATTERN, :DATABASE_PROPERTY_PATTERN
21
-
22
16
  class << self
23
17
  # Ruby database connector library
24
18
  #
@@ -42,34 +36,6 @@ module Datadog
42
36
  Contrib::Utils::Database.normalize_vendor(database.database_type.to_s)
43
37
  end
44
38
 
45
- # Parses URI-style JDBC connection strings, extracting host, port, and
46
- # (best-effort) database name. Unsupported or ambiguous forms return empty
47
- # metadata rather than potentially incorrect tags.
48
- def parse_jdbc_uri(uri)
49
- result = {host: nil, port: nil, database: nil}
50
- return result unless uri.is_a?(String) && uri.valid_encoding?
51
-
52
- match = JDBC_URI_PATTERN.match(uri)
53
- return result unless match
54
-
55
- vendor = match[:vendor].downcase
56
- location, properties = match[:location].split(";", 2)
57
-
58
- # Several JDBC vendors append properties with semicolons, outside the URI
59
- # grammar. Parse the URI-compatible location separately from those properties.
60
- parsed = URI.parse("#{vendor}:#{location}")
61
-
62
- host = parsed.hostname
63
- port = parsed.port
64
-
65
- database = database_from_path(parsed.path) ||
66
- database_from_properties(properties) || database_from_properties(parsed.query)
67
-
68
- {host: host, port: port&.to_s, database: database}
69
- rescue URI::InvalidURIError, Encoding::CompatibilityError, ArgumentError
70
- result
71
- end
72
-
73
39
  def parse_opts(sql, opts, db_opts, dataset = nil)
74
40
  # Prepared statements don't provide their sql query in the +sql+ parameter.
75
41
  if !sql.is_a?(String) && dataset&.respond_to?(:prepared_sql) &&
@@ -84,7 +50,7 @@ module Datadog
84
50
  query: sql,
85
51
  prepared_name: prepared_name,
86
52
  database: db_opts[:database],
87
- host: db_opts[:host]
53
+ host: db_opts[:host],
88
54
  }
89
55
  end
90
56
 
@@ -121,25 +87,38 @@ module Datadog
121
87
  Contrib::Analytics.set_sample_rate(span, analytics_sample_rate) if analytics_enabled?
122
88
  end
123
89
 
124
- # Resolves the connection host/port/database for a Sequel::Database. When the
125
- # connection string is a JDBC URL (Sequel's JDBC adapter, used on JRuby), the
126
- # host/port/database are parsed from it regardless of whether opts[:host] is set.
90
+ # Resolves the connection host/port/database for a Sequel::Database. For Sequel's
91
+ # JDBC adapter (used on JRuby), metadata is parsed from the JDBC connection string
92
+ # regardless of whether opts[:host] is set.
127
93
  def connection_metadata(db)
128
94
  opts = db.opts || {}
129
95
  host = opts[:host]
130
96
  port = opts[:port]
131
97
  database = opts[:database]
132
98
 
133
- # A JDBC URL (in :uri, :url, or :database) can carry credentials, so always parse
134
- # it and emit only the parsed database name -- never the raw connection string.
135
- conn = opts[:uri] || opts[:url] || opts[:database]
136
- is_jdbc = conn.is_a?(String) && conn.byteslice(0, 5)&.casecmp("jdbc:") == 0
99
+ # A JDBC connection string (in :uri, :url, or :database) can carry credentials, so
100
+ # always emit only parsed metadata -- never the raw connection string.
101
+ connection_string = opts[:uri] || opts[:url] || opts[:database]
102
+ is_jdbc = connection_string.is_a?(String) &&
103
+ connection_string.byteslice(0, 5)&.casecmp("jdbc:") == 0
137
104
  if is_jdbc
138
- parsed = parse_jdbc_uri(conn)
105
+ parsed = JDBCConnectionString.parse(connection_string)
106
+
107
+ # JNDI/DataSource-managed connections keep only a lookup name in opts (e.g.
108
+ # "jdbc:jndi:..."), so nothing can be parsed from it. Recover the endpoint from the
109
+ # live connection's JDBC metadata, the same way Sequel resolves JNDI. This stays a
110
+ # fallback rather than the primary source: it requires a connection checkout and
111
+ # some drivers report no connection string, whereas the opts value is free and
112
+ # already present for direct connections (and non-JDBC adapters have no such
113
+ # metadata at all).
114
+ if parsed[:host].nil? && parsed[:port].nil? && parsed[:database].nil?
115
+ fallback_metadata = jdbc_metadata_from_connection(db)
116
+ parsed = fallback_metadata if fallback_metadata
117
+ end
139
118
 
140
- # Sequel's JDBC adapter connects with the URL and ignores separate
119
+ # Sequel's JDBC adapter connects with the connection string and ignores separate
141
120
  # :host/:port options, unlike native adapters where those options take precedence.
142
- if !parsed[:host].nil? || !parsed[:port].nil? || !parsed[:database].nil?
121
+ if parsed[:host] || parsed[:port] || parsed[:database]
143
122
  host = parsed[:host]
144
123
  port = parsed[:port]
145
124
  end
@@ -151,24 +130,32 @@ module Datadog
151
130
 
152
131
  private
153
132
 
154
- def database_from_path(path)
155
- return unless path&.start_with?("/")
156
-
157
- database = path[1..-1]
158
- return if database.empty? || database.include?("/")
159
-
160
- database
161
- end
162
-
163
- def database_from_properties(properties)
164
- return unless properties
165
-
166
- match = DATABASE_PROPERTY_PATTERN.match(properties)
167
- return unless match
133
+ # Resolves host/port/database from the live connection's JDBC metadata
134
+ # (java.sql.DatabaseMetaData#getURL), for JNDI/DataSource connections whose opts hold
135
+ # only a lookup name. Returns the parsed metadata, or nil when it can't be resolved.
136
+ #
137
+ # Only *parsed, credential-free* metadata is memoized -- the raw connection string is
138
+ # used transiently and never stored or logged. A completed lookup that yields no usable
139
+ # connection string is a permanent property of the connection, so it is cached to avoid
140
+ # re-checking out a connection on every query. A raised error is treated as transient
141
+ # (pool checkout timeout, dropped connection, ...) and left uncached, so a later query
142
+ # can retry once connectivity recovers.
143
+ def jdbc_metadata_from_connection(db)
144
+ return db.instance_variable_get(:@datadog_jdbc_metadata) if db.instance_variable_defined?(:@datadog_jdbc_metadata)
145
+
146
+ connection_string =
147
+ begin
148
+ db.synchronize do |conn|
149
+ conn.get_meta_data.get_url if conn.respond_to?(:get_meta_data)
150
+ end
151
+ rescue => e
152
+ Datadog.logger.debug { "Sequel: unable to resolve JDBC connection metadata (#{e.class})" }
153
+ return nil
154
+ end
168
155
 
169
- database = match[:value]
170
- database = database.split(",", 2).first if match[:key].casecmp("libraries").zero?
171
- database
156
+ metadata = connection_string && JDBCConnectionString.parse(connection_string)
157
+ db.instance_variable_set(:@datadog_jdbc_metadata, metadata)
158
+ metadata
172
159
  end
173
160
 
174
161
  def datadog_configuration
@@ -35,7 +35,7 @@ module Datadog
35
35
  Tracing::Distributed::TraceContext.new(fetcher: Tracing::Distributed::Fetcher),
36
36
  Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_BAGGAGE =>
37
37
  Tracing::Distributed::Baggage.new(fetcher: Tracing::Distributed::Fetcher),
38
- Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_NONE => Tracing::Distributed::None.new
38
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_NONE => Tracing::Distributed::None.new,
39
39
  },
40
40
  propagation_style_inject: propagation_style_inject,
41
41
  propagation_style_extract: propagation_style_extract,
@@ -12,7 +12,7 @@ module Datadog
12
12
  # @public_api
13
13
  class Settings < Contrib::Configuration::Settings
14
14
  DEFAULT_HEADERS = {
15
- response: %w[Content-Type X-Request-ID]
15
+ response: %w[Content-Type X-Request-ID],
16
16
  }.freeze
17
17
 
18
18
  option :enabled do |o|
@@ -18,7 +18,7 @@ module Datadog
18
18
  trace_options = {
19
19
  service: configuration[:service_name],
20
20
  type: Tracing::Metadata::Ext::AppTypes::TYPE_WORKER,
21
- on_error: configuration[:on_error]
21
+ on_error: configuration[:on_error],
22
22
  }
23
23
 
24
24
  Tracing.trace(Ext::SPAN_JOB, **trace_options) do |request_span|
@@ -15,7 +15,7 @@ module Datadog
15
15
  DEFAULT_OPTIONS = { # steep:ignore IncompatibleAssignment
16
16
  exclude: EXCLUDE_KEYS,
17
17
  show: SHOW_KEYS,
18
- placeholder: PLACEHOLDER
18
+ placeholder: PLACEHOLDER,
19
19
  }.freeze
20
20
 
21
21
  module_function