datadog 2.30.0 → 2.32.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 (219) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -1
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +17 -7
  4. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +11 -4
  5. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +6 -0
  6. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +18 -0
  7. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +10 -0
  8. data/ext/datadog_profiling_native_extension/extconf.rb +7 -4
  9. data/ext/datadog_profiling_native_extension/http_transport.c +10 -5
  10. data/ext/libdatadog_api/crashtracker.c +5 -8
  11. data/ext/libdatadog_api/datadog_ruby_common.c +18 -0
  12. data/ext/libdatadog_api/datadog_ruby_common.h +10 -0
  13. data/ext/libdatadog_api/di.c +127 -0
  14. data/ext/libdatadog_api/extconf.rb +9 -4
  15. data/ext/libdatadog_api/init.c +5 -2
  16. data/ext/libdatadog_extconf_helpers.rb +46 -1
  17. data/lib/datadog/ai_guard/component.rb +2 -0
  18. data/lib/datadog/ai_guard/configuration.rb +105 -2
  19. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +41 -3
  20. data/lib/datadog/ai_guard/evaluation/content_builder.rb +31 -0
  21. data/lib/datadog/ai_guard/evaluation/content_part.rb +36 -0
  22. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +3 -1
  23. data/lib/datadog/ai_guard/evaluation/request.rb +14 -9
  24. data/lib/datadog/ai_guard/evaluation/result.rb +3 -1
  25. data/lib/datadog/ai_guard/evaluation.rb +37 -7
  26. data/lib/datadog/ai_guard/ext.rb +1 -0
  27. data/lib/datadog/ai_guard.rb +26 -8
  28. data/lib/datadog/appsec/autoload.rb +1 -1
  29. data/lib/datadog/appsec/component.rb +11 -7
  30. data/lib/datadog/appsec/configuration.rb +414 -1
  31. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +2 -1
  32. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +6 -7
  33. data/lib/datadog/appsec/instrumentation/gateway.rb +0 -13
  34. data/lib/datadog/appsec/metrics/telemetry.rb +13 -1
  35. data/lib/datadog/appsec/monitor/gateway/watcher.rb +2 -0
  36. data/lib/datadog/appsec/security_engine/runner.rb +1 -1
  37. data/lib/datadog/appsec/trace_keeper.rb +18 -6
  38. data/lib/datadog/appsec/utils/http/media_type.rb +1 -2
  39. data/lib/datadog/appsec/utils/http/url_encoded.rb +3 -3
  40. data/lib/datadog/appsec.rb +5 -9
  41. data/lib/datadog/core/configuration/base.rb +17 -5
  42. data/lib/datadog/core/configuration/components.rb +22 -9
  43. data/lib/datadog/core/configuration/config_helper.rb +9 -0
  44. data/lib/datadog/core/configuration/option.rb +30 -5
  45. data/lib/datadog/core/configuration/option_definition.rb +38 -12
  46. data/lib/datadog/core/configuration/options.rb +40 -6
  47. data/lib/datadog/core/configuration/settings.rb +18 -0
  48. data/lib/datadog/core/configuration/supported_configurations.rb +3 -0
  49. data/lib/datadog/core/configuration.rb +1 -1
  50. data/lib/datadog/core/contrib/rails/railtie.rb +32 -0
  51. data/lib/datadog/core/contrib/rails/utils.rb +7 -3
  52. data/lib/datadog/core/crashtracking/component.rb +3 -3
  53. data/lib/datadog/core/diagnostics/environment_logger.rb +3 -1
  54. data/lib/datadog/core/environment/container.rb +2 -2
  55. data/lib/datadog/core/environment/ext.rb +1 -0
  56. data/lib/datadog/core/environment/identity.rb +25 -3
  57. data/lib/datadog/core/environment/process.rb +12 -0
  58. data/lib/datadog/core/feature_flags.rb +1 -1
  59. data/lib/datadog/core/metrics/client.rb +5 -5
  60. data/lib/datadog/core/remote/client.rb +1 -1
  61. data/lib/datadog/core/remote/component.rb +38 -21
  62. data/lib/datadog/core/runtime/metrics.rb +1 -1
  63. data/lib/datadog/core/telemetry/component.rb +3 -0
  64. data/lib/datadog/core/telemetry/emitter.rb +1 -1
  65. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +2 -3
  66. data/lib/datadog/core/telemetry/event/app_extended_heartbeat.rb +32 -0
  67. data/lib/datadog/core/telemetry/event/app_started.rb +151 -169
  68. data/lib/datadog/core/telemetry/event.rb +1 -7
  69. data/lib/datadog/core/telemetry/ext.rb +1 -0
  70. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -0
  71. data/lib/datadog/core/telemetry/worker.rb +20 -0
  72. data/lib/datadog/core/transport/http.rb +2 -0
  73. data/lib/datadog/core/utils/only_once.rb +1 -1
  74. data/lib/datadog/core/utils/spawn_monkey_patch.rb +36 -0
  75. data/lib/datadog/core/utils.rb +1 -1
  76. data/lib/datadog/core/workers/async.rb +1 -1
  77. data/lib/datadog/core.rb +1 -2
  78. data/lib/datadog/data_streams/configuration.rb +40 -1
  79. data/lib/datadog/data_streams/pathway_context.rb +1 -1
  80. data/lib/datadog/data_streams/processor.rb +1 -1
  81. data/lib/datadog/data_streams.rb +1 -1
  82. data/lib/datadog/di/base.rb +8 -5
  83. data/lib/datadog/di/boot.rb +2 -4
  84. data/lib/datadog/di/code_tracker.rb +179 -1
  85. data/lib/datadog/di/component.rb +5 -1
  86. data/lib/datadog/di/configuration.rb +235 -2
  87. data/lib/datadog/di/instrumenter.rb +55 -29
  88. data/lib/datadog/di/probe_builder.rb +1 -1
  89. data/lib/datadog/di/probe_file_loader.rb +2 -2
  90. data/lib/datadog/di/probe_manager.rb +6 -6
  91. data/lib/datadog/di/probe_notification_builder.rb +110 -2
  92. data/lib/datadog/di/probe_notifier_worker.rb +2 -2
  93. data/lib/datadog/di/remote.rb +6 -6
  94. data/lib/datadog/di/transport/input.rb +3 -3
  95. data/lib/datadog/di.rb +81 -0
  96. data/lib/datadog/error_tracking/configuration.rb +55 -2
  97. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  98. data/lib/datadog/open_feature/component.rb +18 -1
  99. data/lib/datadog/open_feature/evaluation_engine.rb +2 -2
  100. data/lib/datadog/open_feature/hooks/flag_eval_hook.rb +49 -0
  101. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  102. data/lib/datadog/open_feature/provider.rb +19 -1
  103. data/lib/datadog/open_feature/remote.rb +1 -1
  104. data/lib/datadog/open_feature/transport.rb +1 -1
  105. data/lib/datadog/opentelemetry/configuration/settings.rb +2 -0
  106. data/lib/datadog/opentelemetry/metrics.rb +3 -3
  107. data/lib/datadog/opentelemetry/sdk/configurator.rb +1 -1
  108. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +1 -1
  109. data/lib/datadog/profiling/collectors/code_provenance.rb +36 -11
  110. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +31 -2
  111. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +8 -2
  112. data/lib/datadog/profiling/collectors/info.rb +16 -3
  113. data/lib/datadog/profiling/component.rb +12 -4
  114. data/lib/datadog/profiling/exporter.rb +37 -12
  115. data/lib/datadog/profiling/ext.rb +0 -2
  116. data/lib/datadog/profiling/flush.rb +21 -12
  117. data/lib/datadog/profiling/http_transport.rb +12 -1
  118. data/lib/datadog/profiling/load_native_extension.rb +2 -2
  119. data/lib/datadog/profiling/profiler.rb +13 -5
  120. data/lib/datadog/profiling/scheduler.rb +2 -2
  121. data/lib/datadog/profiling/tasks/exec.rb +8 -3
  122. data/lib/datadog/profiling/tasks/help.rb +1 -0
  123. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  124. data/lib/datadog/profiling.rb +1 -2
  125. data/lib/datadog/single_step_instrument.rb +1 -1
  126. data/lib/datadog/symbol_database/configuration.rb +65 -0
  127. data/lib/datadog/symbol_database/extractor.rb +915 -0
  128. data/lib/datadog/symbol_database/file_hash.rb +46 -0
  129. data/lib/datadog/symbol_database/logger.rb +43 -0
  130. data/lib/datadog/symbol_database/scope.rb +98 -0
  131. data/lib/datadog/symbol_database/service_version.rb +57 -0
  132. data/lib/datadog/symbol_database/symbol.rb +66 -0
  133. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  134. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  135. data/lib/datadog/symbol_database/transport.rb +54 -0
  136. data/lib/datadog/symbol_database/uploader.rb +166 -0
  137. data/lib/datadog/symbol_database.rb +49 -0
  138. data/lib/datadog/tracing/buffer.rb +3 -3
  139. data/lib/datadog/tracing/component.rb +11 -0
  140. data/lib/datadog/tracing/configuration/settings.rb +2 -1
  141. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -3
  142. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +20 -0
  143. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/patcher.rb +3 -1
  144. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  145. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  146. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  147. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  148. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  149. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  150. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  151. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  152. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  153. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +2 -2
  154. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  155. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  156. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  157. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
  158. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  159. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
  160. data/lib/datadog/tracing/contrib/component.rb +1 -1
  161. data/lib/datadog/tracing/contrib/configurable.rb +18 -3
  162. data/lib/datadog/tracing/contrib/configuration/resolver.rb +7 -4
  163. data/lib/datadog/tracing/contrib/dalli/quantize.rb +1 -1
  164. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
  165. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
  166. data/lib/datadog/tracing/contrib/extensions.rb +9 -0
  167. data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
  168. data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -5
  169. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +2 -2
  170. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +2 -2
  171. data/lib/datadog/tracing/contrib/http/instrumentation.rb +3 -3
  172. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -2
  173. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +3 -3
  174. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +2 -2
  175. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +2 -2
  176. data/lib/datadog/tracing/contrib/karafka/patcher.rb +1 -1
  177. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +3 -3
  178. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
  179. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +3 -3
  180. data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
  181. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  182. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  183. data/lib/datadog/tracing/contrib/rails/patcher.rb +0 -1
  184. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  185. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
  186. data/lib/datadog/tracing/contrib/redis/quantize.rb +1 -1
  187. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
  188. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +1 -1
  189. data/lib/datadog/tracing/contrib/status_range_matcher.rb +4 -0
  190. data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
  191. data/lib/datadog/tracing/contrib.rb +8 -0
  192. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -1
  193. data/lib/datadog/tracing/distributed/baggage.rb +59 -5
  194. data/lib/datadog/tracing/distributed/datadog.rb +13 -11
  195. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +1 -1
  196. data/lib/datadog/tracing/distributed/propagation.rb +2 -2
  197. data/lib/datadog/tracing/distributed/trace_context.rb +74 -32
  198. data/lib/datadog/tracing/event.rb +1 -1
  199. data/lib/datadog/tracing/metadata/tagging.rb +2 -2
  200. data/lib/datadog/tracing/pipeline.rb +1 -1
  201. data/lib/datadog/tracing/remote.rb +1 -1
  202. data/lib/datadog/tracing/sampling/ext.rb +2 -0
  203. data/lib/datadog/tracing/sampling/priority_sampler.rb +13 -0
  204. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  205. data/lib/datadog/tracing/sampling/rule_sampler.rb +54 -25
  206. data/lib/datadog/tracing/sampling/span/rule_parser.rb +2 -2
  207. data/lib/datadog/tracing/span_operation.rb +4 -4
  208. data/lib/datadog/tracing/trace_operation.rb +53 -9
  209. data/lib/datadog/tracing/tracer.rb +29 -4
  210. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  211. data/lib/datadog/tracing/transport/trace_formatter.rb +1 -1
  212. data/lib/datadog/tracing/workers.rb +2 -1
  213. data/lib/datadog/version.rb +1 -1
  214. metadata +27 -12
  215. data/lib/datadog/ai_guard/configuration/settings.rb +0 -113
  216. data/lib/datadog/appsec/configuration/settings.rb +0 -423
  217. data/lib/datadog/data_streams/configuration/settings.rb +0 -49
  218. data/lib/datadog/di/configuration/settings.rb +0 -243
  219. data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
@@ -73,7 +73,7 @@ module Datadog
73
73
  # set the resource with the quantized query
74
74
  span.resource = serialized_query
75
75
  rescue => e
76
- Datadog.logger.debug("error when handling MongoDB 'started' event: #{e}")
76
+ Datadog.logger.debug("error when handling MongoDB 'started' event: #{e.class}: #{e.message}")
77
77
  end
78
78
  # rubocop:enable Metrics/AbcSize
79
79
 
@@ -85,7 +85,7 @@ module Datadog
85
85
  # the framework itself, so we set only the error and the message
86
86
  span.set_error(event)
87
87
  rescue => e
88
- Datadog.logger.debug("error when handling MongoDB 'failed' event: #{e}")
88
+ Datadog.logger.debug("error when handling MongoDB 'failed' event: #{e.class}: #{e.message}")
89
89
  ensure
90
90
  # whatever happens, the Span must be removed from the local storage and
91
91
  # it must be finished to prevent any leak
@@ -101,7 +101,7 @@ module Datadog
101
101
  rows = event.reply.fetch('n', nil)
102
102
  span.set_tag(Ext::TAG_ROWS, rows) unless rows.nil?
103
103
  rescue => e
104
- Datadog.logger.debug("error when handling MongoDB 'succeeded' event: #{e}")
104
+ Datadog.logger.debug("error when handling MongoDB 'succeeded' event: #{e.class}: #{e.message}")
105
105
  ensure
106
106
  # whatever happens, the Span must be removed from the local storage and
107
107
  # it must be finished to prevent any leak
@@ -84,7 +84,7 @@ module Datadog
84
84
  span.resource = "#{method} #{quantized_url}"
85
85
  Contrib::SpanAttributeSchema.set_peer_service!(span, Ext::PEER_SERVICE_SOURCES)
86
86
  rescue => e
87
- Datadog.logger.error(e.message)
87
+ Datadog.logger.error("#{e.class}: #{e.message}")
88
88
  Datadog::Core::Telemetry::Logger.report(e)
89
89
  # TODO: Refactor the code to streamline the execution without ensure
90
90
  ensure
@@ -28,7 +28,7 @@ module Datadog
28
28
  span.type = Tracing::Metadata::Ext::SQL::TYPE
29
29
  span.set_tag(Ext::TAG_QUERY_ASYNC, false)
30
30
  rescue => e
31
- Datadog.logger.debug("error preparing span for presto: #{e}")
31
+ Datadog.logger.debug("error preparing span for presto: #{e.class}: #{e.message}")
32
32
  end
33
33
 
34
34
  super(query)
@@ -46,7 +46,7 @@ module Datadog
46
46
  span.type = Tracing::Metadata::Ext::SQL::TYPE
47
47
  span.set_tag(Ext::TAG_QUERY_ASYNC, !blk.nil?)
48
48
  rescue => e
49
- Datadog.logger.debug("error preparing span for presto: #{e}")
49
+ Datadog.logger.debug("error preparing span for presto: #{e.class}: #{e.message}")
50
50
  end
51
51
 
52
52
  super(query, &blk)
@@ -65,7 +65,7 @@ module Datadog
65
65
  # ^ not an SQL type span, since there's no SQL query
66
66
  span.set_tag(Ext::TAG_QUERY_ID, query_id)
67
67
  rescue => e
68
- Datadog.logger.debug("error preparing span for presto: #{e}")
68
+ Datadog.logger.debug("error preparing span for presto: #{e.class}: #{e.message}")
69
69
  end
70
70
 
71
71
  super(query_id)
@@ -41,7 +41,7 @@ module Datadog
41
41
  # context of middleware patching outside a Rails server process (eg. a
42
42
  # process that doesn't serve HTTP requests but has Rails environment
43
43
  # loaded such as a Resque master process)
44
- Datadog.logger.debug("Error patching middleware stack: #{e}")
44
+ Datadog.logger.debug("Error patching middleware stack: #{e.class}: #{e.message}")
45
45
  end
46
46
 
47
47
  def retain_middleware_name(middleware)
@@ -39,7 +39,7 @@ module Datadog
39
39
  rescue => e
40
40
  # in case of an Exception we don't create a
41
41
  # `request.queuing` span
42
- Datadog.logger.debug("[rack] unable to parse request queue headers: #{e}")
42
+ Datadog.logger.debug("[rack] unable to parse request queue headers: #{e.class}: #{e.message}")
43
43
  nil
44
44
  end
45
45
  end
@@ -18,7 +18,7 @@ module Datadog
18
18
  app_config.log_tags << proc { Tracing.log_correlation if Datadog.configuration.tracing.log_injection }
19
19
  rescue => e
20
20
  Datadog.logger.warn(
21
- "Unable to add Datadog Trace context to ActiveSupport::TaggedLogging: #{e.class.name} #{e.message}"
21
+ "Unable to add Datadog Trace context to ActiveSupport::TaggedLogging: #{e.class}: #{e.message}"
22
22
  )
23
23
  false
24
24
  end
@@ -6,7 +6,6 @@ require_relative 'framework'
6
6
  require_relative 'log_injection'
7
7
  require_relative 'middlewares'
8
8
  require_relative 'runner'
9
- require_relative '../../../core/contrib/rails/utils'
10
9
  require_relative '../semantic_logger/patcher'
11
10
 
12
11
  module Datadog
@@ -86,7 +86,7 @@ module Datadog
86
86
  # Reads one more byte than the limit to allow us to check if the source exceeds the limit.
87
87
  source = File.read(file, MAX_TAG_VALUE_SIZE + 1)
88
88
  rescue => e
89
- Datadog.logger.debug("Failed to read file '#{file}' for Rails runner: #{e.message}")
89
+ Datadog.logger.debug { "Failed to read file '#{file}' for Rails runner: #{e.class}: #{e.message}" }
90
90
  end
91
91
 
92
92
  Tracing.trace(
@@ -66,7 +66,7 @@ module Datadog
66
66
  span.set_tag(Ext::TAG_TASK_ARG_NAMES, arg_names)
67
67
  span.set_tag(Ext::TAG_INVOKE_ARGS, quantize_args(args)) unless args.nil?
68
68
  rescue => e
69
- Datadog.logger.debug("Error while tracing Rake invoke: #{e.class.name} #{e.message}")
69
+ Datadog.logger.debug { "Error while tracing Rake invoke: #{e.class}: #{e.message}" }
70
70
  end
71
71
 
72
72
  def annotate_execute!(span, args)
@@ -75,7 +75,7 @@ module Datadog
75
75
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_EXECUTE)
76
76
  span.set_tag(Ext::TAG_EXECUTE_ARGS, quantize_args(args.to_hash)) unless args.nil?
77
77
  rescue => e
78
- Datadog.logger.debug("Error while tracing Rake execute: #{e.class.name} #{e.message}")
78
+ Datadog.logger.debug { "Error while tracing Rake execute: #{e.class}: #{e.message}" }
79
79
  end
80
80
 
81
81
  def quantize_args(args)
@@ -34,7 +34,7 @@ module Datadog
34
34
  str = Core::Utils.utf8_encode(arg, binary: true, placeholder: PLACEHOLDER)
35
35
  Core::Utils.truncate(str, VALUE_MAX_LEN, TOO_LONG_MARK)
36
36
  rescue => e
37
- Datadog.logger.debug("non formattable Redis arg #{str}: #{e}")
37
+ Datadog.logger.debug("non formattable Redis arg #{str}: #{e.class}: #{e.message}")
38
38
  PLACEHOLDER
39
39
  end
40
40
 
@@ -47,7 +47,7 @@ module Datadog
47
47
 
48
48
  Contrib::SpanAttributeSchema.set_peer_service!(span, Ext::PEER_SERVICE_SOURCES)
49
49
  rescue => e
50
- Datadog.logger.error(e.message)
50
+ Datadog.logger.error("#{e.class}: #{e.message}")
51
51
  Datadog::Core::Telemetry::Logger.report(e)
52
52
  end
53
53
 
@@ -25,7 +25,7 @@ module Datadog
25
25
  job['class'].to_s
26
26
  end
27
27
  rescue => e
28
- Datadog.logger.debug { "Error retrieving Sidekiq job class name (jid:#{job["jid"]}): #{e}" }
28
+ Datadog.logger.debug { "Error retrieving Sidekiq job class name (jid:#{job["jid"]}): #{e.class}: #{e.message}" }
29
29
 
30
30
  job['class'].to_s
31
31
  end
@@ -27,6 +27,10 @@ module Datadog
27
27
  end
28
28
  end
29
29
  end
30
+
31
+ def to_s
32
+ @ranges.map { |range| range.is_a?(Range) ? "#{range.begin}-#{range.end}" : range.to_s }.join(',')
33
+ end
30
34
  end
31
35
  end
32
36
  end
@@ -55,7 +55,7 @@ module Datadog
55
55
  span.set_tag(Ext::TAG_REQUEST_PATH, event.path)
56
56
  span.set_tag(Ext::TAG_REQUEST_NUM_RETRIES, event.num_retries.to_s)
57
57
  rescue => e
58
- Datadog.logger.debug(e.message)
58
+ Datadog.logger.debug { "#{e.class}: #{e.message}" }
59
59
  end
60
60
 
61
61
  def configuration
@@ -81,3 +81,11 @@ require_relative 'contrib/sneakers/integration'
81
81
  require_relative 'contrib/stripe/integration'
82
82
  require_relative 'contrib/sucker_punch/integration'
83
83
  require_relative 'contrib/trilogy/integration'
84
+
85
+ # This list is used to determine if an integration is a built-in integration,
86
+ # and prevent sending telemetry for custom integrations.
87
+ # To do this, we get the list of registered integrations at this point and freeze it.
88
+ # Later, when custom integrations are registered, they will not be included in this list.
89
+ # .uniq to handle aliases, as we can register the same integration multiple times with different names.
90
+ Datadog::Tracing::Contrib::BUILT_IN_INTEGRATIONS =
91
+ Datadog::Tracing::Contrib::REGISTRY.map { |entry| entry.klass }.uniq.freeze
@@ -24,7 +24,9 @@ module Datadog
24
24
  end
25
25
  end
26
26
  rescue => e
27
- logger.warn("Failed to collect tracing environment information: #{e} Location: #{Array(e.backtrace).first}")
27
+ logger.warn(
28
+ "Failed to collect tracing environment information: #{e.class}: #{e.message} Location: #{Array(e.backtrace).first}"
29
+ )
28
30
  end
29
31
  end
30
32
 
@@ -77,7 +77,7 @@ module Datadog
77
77
  # Record telemetry for successful injection
78
78
  record_telemetry_metric('context_header_style.injected', 1, {'header_style' => 'baggage'})
79
79
  rescue => e
80
- ::Datadog.logger.warn("Failed to encode and inject baggage header: #{e.class}: #{e}")
80
+ ::Datadog.logger.warn("Failed to encode and inject baggage header: #{e.class}: #{e.message}")
81
81
  end
82
82
  end
83
83
 
@@ -133,12 +133,56 @@ module Datadog
133
133
  # - Keys and values are URL-encoded
134
134
  # - Returns an empty hash if the baggage header is malformed
135
135
  #
136
+ # For entries with duplicate keys, only last one is preserved:
137
+ # https://www.w3.org/TR/2024/CR-baggage-20240530/#mutating-baggage
138
+ #
136
139
  # @param baggage_header [String] The W3C Baggage header string to parse
137
140
  # @return [Hash<String, String>] A hash of decoded baggage items
138
141
  def parse_baggage_header(baggage_header)
142
+ # Reject inputs that would cause String operations below to raise.
143
+ # `valid_encoding?` is true for ASCII-8BIT regardless of byte values, so
144
+ # the typical Rack path is unaffected; only UTF-8-tagged-but-actually-invalid
145
+ # input (e.g. headers re-encoded upstream) is short-circuited here.
146
+ unless baggage_header.valid_encoding?
147
+ record_telemetry_metric('context_header_style.malformed', 1, {'header_style' => 'baggage'})
148
+ return {}
149
+ end
150
+
151
+ if baggage_header.bytesize > DD_TRACE_BAGGAGE_MAX_BYTES
152
+ record_telemetry_metric('context_header.truncated', 1, {'header_style' => 'baggage', 'truncation_reason' => 'baggage_byte_count_exceeded'})
153
+
154
+ # We MUST NOT propagate partial entries, but we SHOULD try
155
+ # to parse as much of the baggage as possible:
156
+ # https://www.w3.org/TR/2024/CR-baggage-20240530/#limits
157
+
158
+ # We parse 1 byte over the limit to detect if the last entry
159
+ # is a partial entry (toss) or ends exactly at the limit (keep).
160
+ remove_last_entry = baggage_header.byteslice(DD_TRACE_BAGGAGE_MAX_BYTES, 1) != ','
161
+
162
+ # To ensure we don't have a trailing partial UTF-8 codepoint, we keep one extra byte
163
+ # and safely remove it with `#chop`.
164
+ # `#chops` walks back the string until it finds a valid character boundary and deletes
165
+ # from there.
166
+ baggage_header = baggage_header.byteslice(0, DD_TRACE_BAGGAGE_MAX_BYTES + 1) #: String
167
+ baggage_header = baggage_header.chop
168
+ end
169
+
139
170
  baggage = {}
140
- baggages = baggage_header.split(',')
141
- baggages.each do |key_value|
171
+ # DEV: To avoid unnecessary eager string allocation, replace with
172
+ # DEV: `split(',') { |s| ... }` when Ruby 2.5 is no longer supported.
173
+ baggages = baggage_header.split(',', DD_TRACE_BAGGAGE_MAX_ITEMS + 1) # Stop splitting if we've reached max size
174
+ baggages.each_with_index do |key_value, index|
175
+ if index >= DD_TRACE_BAGGAGE_MAX_ITEMS
176
+ record_telemetry_metric('context_header.truncated', 1, {'header_style' => 'baggage', 'truncation_reason' => 'baggage_item_count_exceeded'})
177
+ break
178
+ end
179
+
180
+ # On truncation, remove incomplete trailing partial entry
181
+ next if remove_last_entry && index == baggages.size - 1
182
+
183
+ # Empty items are skipped
184
+ next if key_value.strip.empty?
185
+
142
186
  key, value = key_value.split('=', 2)
143
187
  # If baggage is malformed, return an empty hash
144
188
  if key.nil? || value.nil?
@@ -147,8 +191,17 @@ module Datadog
147
191
  return {}
148
192
  end
149
193
 
150
- key = URI.decode_www_form_component(key.strip)
151
- value = URI.decode_www_form_component(value.strip)
194
+ begin
195
+ key = URI.decode_www_form_component(key.strip)
196
+ value = URI.decode_www_form_component(value.strip)
197
+ rescue ArgumentError
198
+ # `URI.decode_www_form_component` raises on malformed percent encoding
199
+ # (e.g. `%XX`, lone `%`). Treat as malformed rather than letting it
200
+ # propagate to `Propagation#extract`'s caller.
201
+ record_telemetry_metric('context_header_style.malformed', 1, {'header_style' => 'baggage'})
202
+ return {}
203
+ end
204
+
152
205
  if key.empty? || value.empty?
153
206
  # Record telemetry for malformed header
154
207
  record_telemetry_metric('context_header_style.malformed', 1, {'header_style' => 'baggage'})
@@ -157,6 +210,7 @@ module Datadog
157
210
 
158
211
  baggage[key] = value
159
212
  end
213
+
160
214
  baggage
161
215
  end
162
216
 
@@ -131,17 +131,15 @@ module Datadog
131
131
 
132
132
  encoded_tags = DatadogTagsCodec.encode(tags)
133
133
 
134
- return set_tags_propagation_error(reason: 'inject_max_size') if tags_too_large?(
135
- encoded_tags.size,
136
- scenario: 'inject'
137
- )
134
+ return set_tags_propagation_error(reason: 'inject_max_size') if tags_too_large?(encoded_tags, scenario: 'inject')
138
135
 
139
136
  data[@tags_key] = encoded_tags
140
137
  rescue => e
141
138
  set_tags_propagation_error(reason: 'encoding_error')
142
139
  ::Datadog.logger.warn(
143
- "Failed to inject x-datadog-tags: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
140
+ "Failed to inject x-datadog-tags: #{e.class}: #{e.message} at #{Array(e.backtrace).first}"
144
141
  )
142
+ nil
145
143
  end
146
144
 
147
145
  # Import `x-datadog-tags` tags as trace distributed tags.
@@ -155,7 +153,7 @@ module Datadog
155
153
 
156
154
  return if !tags || tags.empty?
157
155
  return set_tags_propagation_error(reason: 'disabled') if tags_disabled?
158
- return set_tags_propagation_error(reason: 'extract_max_size') if tags_too_large?(tags.size, scenario: 'extract')
156
+ return set_tags_propagation_error(reason: 'extract_max_size') if tags_too_large?(tags, scenario: 'extract')
159
157
 
160
158
  tags_hash = DatadogTagsCodec.decode(tags)
161
159
  # Only extract keys with the expected Datadog prefix
@@ -166,8 +164,9 @@ module Datadog
166
164
  rescue => e
167
165
  set_tags_propagation_error(reason: 'decoding_error')
168
166
  ::Datadog.logger.warn(
169
- "Failed to extract x-datadog-tags: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
167
+ "Failed to extract x-datadog-tags: #{e.class}: #{e.message} at #{Array(e.backtrace).first}"
170
168
  )
169
+ nil
171
170
  end
172
171
 
173
172
  def set_tags_propagation_error(reason:)
@@ -180,12 +179,15 @@ module Datadog
180
179
  ::Datadog.configuration.tracing.x_datadog_tags_max_length <= 0
181
180
  end
182
181
 
183
- def tags_too_large?(size, scenario:)
184
- return false if size <= ::Datadog.configuration.tracing.x_datadog_tags_max_length
182
+ def tags_too_large?(tags, scenario:)
183
+ size = tags.bytesize
184
+ max_length = ::Datadog.configuration.tracing.x_datadog_tags_max_length
185
+
186
+ return false if size <= max_length
185
187
 
186
188
  ::Datadog.logger.warn(
187
- "Failed to #{scenario} x-datadog-tags: tags are too large for configured limit (size:#{size} >= " \
188
- "limit:#{::Datadog.configuration.tracing.x_datadog_tags_max_length}). This limit can be configured " \
189
+ "Failed to #{scenario} x-datadog-tags: tags are too large for configured limit (bytesize:#{size} >= " \
190
+ "limit:#{max_length}). This limit can be configured " \
189
191
  'through the DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH environment variable.'
190
192
  )
191
193
 
@@ -41,7 +41,7 @@ module Datadog
41
41
  "#{key}=#{value.strip}"
42
42
  end.join(',')
43
43
  rescue => e
44
- raise EncodingError, "Error encoding tags `#{tags}`: `#{e}`"
44
+ raise EncodingError, "Error encoding tags `#{tags}`: `#{e.class}: #{e.message}`"
45
45
  end
46
46
 
47
47
  # Deserializes a `x-datadog-tags`-formatted String into a {Hash<String,String>}.
@@ -76,7 +76,7 @@ module Datadog
76
76
  rescue => e
77
77
  result = nil
78
78
  ::Datadog.logger.error(
79
- "Error injecting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
79
+ "Error injecting distributed trace data. Cause: #{e.class}: #{e.message} Location: #{Array(e.backtrace).first}"
80
80
  )
81
81
  ::Datadog::Core::Telemetry::Logger.report(
82
82
  e,
@@ -139,7 +139,7 @@ module Datadog
139
139
  rescue => e
140
140
  # TODO: Not to report Telemetry logs for now
141
141
  ::Datadog.logger.error(
142
- "Error extracting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
142
+ "Error extracting distributed trace data. Cause: #{e.class}: #{e.message} Location: #{Array(e.backtrace).first}"
143
143
  )
144
144
  end
145
145
  # Handle baggage after all other styles if present
@@ -149,9 +149,9 @@ module Datadog
149
149
  # @see https://www.w3.org/TR/trace-context/#tracestate-header
150
150
  def build_tracestate(digest)
151
151
  tracestate = +'dd='
152
- tracestate << last_dd_parent_id(digest)
153
- tracestate << "s:#{digest.trace_sampling_priority};" if digest.trace_sampling_priority
154
- tracestate << "o:#{serialize_origin(digest.trace_origin)};" if digest.trace_origin
152
+ append_dd(tracestate, last_dd_parent_id(digest))
153
+ append_dd(tracestate, "s:#{digest.trace_sampling_priority};") if digest.trace_sampling_priority
154
+ append_dd(tracestate, "o:#{serialize_origin(digest.trace_origin)};") if digest.trace_origin
155
155
 
156
156
  # Replacing this by safe navigation seems to have a different behaviour on Rubies <= 3.0.
157
157
  # It cause a LocalJumpError in the CI.
@@ -159,45 +159,50 @@ module Datadog
159
159
  digest.trace_distributed_tags.each do |name, value|
160
160
  tag = "t.#{serialize_tag_key(name)}:#{serialize_tag_value(value)};"
161
161
 
162
- # If tracestate size limit is exceed, drop the remaining data.
163
- # String#bytesize is used because only ASCII characters are allowed.
164
- #
165
- # We add 1 to the limit because of the trailing comma, which will be removed before returning.
166
- break if tracestate.bytesize + tag.bytesize > (TRACESTATE_VALUE_SIZE_LIMIT + 1)
167
-
168
- tracestate << tag
162
+ # If tracestate size limit is exceeded, drop the remaining data.
163
+ break unless append_dd(tracestate, tag)
169
164
  end
170
165
  end
171
166
 
172
- tracestate << digest.trace_state_unknown_fields if digest.trace_state_unknown_fields
167
+ append_dd(tracestate, digest.trace_state_unknown_fields) if digest.trace_state_unknown_fields
168
+ vendors = split_tracestate(digest.trace_state)
173
169
 
174
170
  # Is there any Datadog-specific information to propagate.
175
171
  # Check for > 3 size because the empty prefix `dd=` has 3 characters.
176
- if tracestate.size > 3
172
+ if tracestate.bytesize > 3
177
173
  # Propagate upstream tracestate with `dd=...` appended to the list
178
174
  tracestate.chop! # Removes trailing `;` from Datadog trace state string.
179
175
 
180
- if digest.trace_state
181
- trace_state = digest.trace_state.strip
182
-
176
+ if vendors && !vendors.empty?
183
177
  # Delete existing `dd=` tracestate fields, if present.
184
- vendors = split_tracestate(trace_state)
185
178
  vendors.reject! { |v| v.start_with?('dd=') }
186
- end
187
179
 
188
- if vendors && !vendors.empty?
189
180
  # Ensure the list has at most 31 elements, as we need to prepend Datadog's
190
181
  # entry and the limit is 32 elements total.
191
- vendors = vendors[0..30]
192
- "#{tracestate},#{vendors.join(",")}"
193
- else
194
- tracestate.to_s
182
+ vendors.first(TRACESTATE_MAX_LIST_MEMBERS - 1).each do |vendor|
183
+ break if tracestate.bytesize + vendor.bytesize + 1 > TRACESTATE_MAX_SIZE_LIMIT
184
+
185
+ tracestate << ',' << vendor
186
+ end
195
187
  end
196
- else
197
- digest.trace_state # Propagate upstream tracestate with no Datadog changes
188
+
189
+ tracestate.to_s
190
+ elsif vendors && !vendors.empty?
191
+ vendors.join(',')
198
192
  end
199
193
  end
200
194
 
195
+ # Appends a Datadog tracestate field when it fits.
196
+ def append_dd(tracestate, field)
197
+ return true if field.empty?
198
+
199
+ # We add 1 to the limit because of the trailing semicolon, which will be removed before returning.
200
+ return false if tracestate.bytesize + field.bytesize > (TRACESTATE_VALUE_SIZE_LIMIT + 1)
201
+
202
+ tracestate << field
203
+ true
204
+ end
205
+
201
206
  def last_dd_parent_id(digest)
202
207
  if !digest.span_remote
203
208
  span_id = digest.span_id || 0 # Fall back to zero (invalid) if not present
@@ -281,19 +286,21 @@ module Datadog
281
286
 
282
287
  def parse_traceparent_string(traceparent)
283
288
  return unless traceparent
289
+ return if traceparent.bytesize > TRACEPARENT_MAX_SIZE_LIMIT
284
290
 
285
- version, trace_id, parent_id, trace_flags, extra = traceparent.strip.split('-')
291
+ traceparent = traceparent.strip
286
292
 
287
- return if version.size != 2 || version[0] < '0' || version[0] > 'f' || version[1] < '0' || version[1] > 'f'
293
+ version, trace_id, parent_id, trace_flags, extra = traceparent.split('-', 5)
294
+
295
+ return unless version && trace_id && parent_id && trace_flags
296
+ return if version.size != 2 || trace_id.size != 32 || parent_id.size != 16 || trace_flags.size != 2
297
+ return if version[0] < '0' || version[0] > 'f' || version[1] < '0' || version[1] > 'f'
288
298
 
289
299
  return if version == INVALID_VERSION
290
300
 
291
301
  # Extra fields are not allowed in version 00, but we have to be lenient for future versions.
292
302
  return if version == SPEC_VERSION && extra
293
303
 
294
- # Invalid field sizes
295
- return if trace_id.size != 32 || parent_id.size != 16 || trace_flags.size != 2
296
-
297
304
  [Integer(trace_id, 16), Integer(parent_id, 16), Integer(trace_flags, 16)]
298
305
  rescue ArgumentError # Conversion to integer failed
299
306
  nil
@@ -306,9 +313,10 @@ module Datadog
306
313
  # @return [Array<String,Integer,String,String,Hash>] returns 4 values:
307
314
  # tracestate, sampling_priority, ts_parent_id, origin, tags.
308
315
  def extract_tracestate(tracestate)
309
- return unless tracestate
310
-
311
316
  vendors = split_tracestate(tracestate)
317
+ return unless vendors && !vendors.empty?
318
+
319
+ tracestate = vendors.join(',')
312
320
 
313
321
  # Find Datadog's `dd=` tracestate field.
314
322
  idx = vendors.index { |v| v.start_with?('dd=') }
@@ -396,10 +404,44 @@ module Datadog
396
404
  end
397
405
  end
398
406
 
407
+ # We MUST NOT propagate partial members, but we SHOULD try
408
+ # to parse as much of the tracestate as possible.
399
409
  def split_tracestate(tracestate)
400
- tracestate.split(/[ \t]*+,[ \t]*+/)[0..31]
410
+ return unless tracestate
411
+
412
+ remove_last_member = false
413
+ if tracestate.bytesize > TRACESTATE_MAX_SIZE_LIMIT
414
+ # We parse 1 byte over the limit to detect if the last member
415
+ # is a partial member (toss) or ends exactly at the limit (keep).
416
+ remove_last_member = tracestate.byteslice(TRACESTATE_MAX_SIZE_LIMIT, 1) != ','
417
+
418
+ # To ensure we don't have a trailing partial UTF-8 codepoint, we keep one extra byte
419
+ # and safely remove it with `#chop`.
420
+ # `#chop` walks back the string until it finds a valid character boundary and deletes
421
+ # from there.
422
+ tracestate = tracestate.byteslice(0, TRACESTATE_MAX_SIZE_LIMIT + 1) #: String
423
+ tracestate = tracestate.chop
424
+ end
425
+
426
+ vendors = tracestate.split(',', TRACESTATE_MAX_LIST_MEMBERS + 1)
427
+ if vendors.length > TRACESTATE_MAX_LIST_MEMBERS || remove_last_member
428
+ vendors.pop
429
+ end
430
+
431
+ vendors.each(&:strip!)
432
+ vendors.pop while vendors.last == ''
433
+ vendors
401
434
  end
402
435
 
436
+ TRACEPARENT_MAX_SIZE_LIMIT = 512
437
+ private_constant :TRACEPARENT_MAX_SIZE_LIMIT
438
+
439
+ TRACESTATE_MAX_SIZE_LIMIT = 512
440
+ private_constant :TRACESTATE_MAX_SIZE_LIMIT
441
+
442
+ TRACESTATE_MAX_LIST_MEMBERS = 32
443
+ private_constant :TRACESTATE_MAX_LIST_MEMBERS
444
+
403
445
  # Version 0xFF is invalid as per spec
404
446
  # @see https://www.w3.org/TR/trace-context/#version
405
447
  INVALID_VERSION = 'ff'
@@ -62,7 +62,7 @@ module Datadog
62
62
  block.call(*args)
63
63
  rescue => e
64
64
  Datadog.logger.debug do
65
- "Error while handling '#{name}' event with '#{block}': #{e.class.name} #{e.message} " \
65
+ "Error while handling '#{name}' event with '#{block}': #{e.class}: #{e.message} " \
66
66
  "at #{Array(e.backtrace).first}"
67
67
  end
68
68
  end
@@ -52,7 +52,7 @@ module Datadog
52
52
  meta[Core::Utils.utf8_encode(key)] = Core::Utils.utf8_encode(value)
53
53
  end
54
54
  rescue => e
55
- Datadog.logger.debug("Unable to set the tag #{key}, ignoring it. Caused by: #{e}")
55
+ Datadog.logger.debug("Unable to set the tag #{key}, ignoring it. Caused by: #{e.class}: #{e.message}")
56
56
  end
57
57
 
58
58
  # Sets tags from given hash, for each key in hash it sets the tag with that key
@@ -102,7 +102,7 @@ module Datadog
102
102
  # Encode strings in UTF-8 to facilitate downstream serialization
103
103
  metrics[Core::Utils.utf8_encode(key)] = value
104
104
  rescue => e
105
- Datadog.logger.debug("Unable to set the metric #{key}, ignoring it. Caused by: #{e}")
105
+ Datadog.logger.debug("Unable to set the metric #{key}, ignoring it. Caused by: #{e.class}: #{e.message}")
106
106
  end
107
107
 
108
108
  # This method removes a metric for the given key. It acts like {#clear_tag}.
@@ -51,7 +51,7 @@ module Datadog
51
51
  end
52
52
  rescue => e
53
53
  Datadog.logger.debug(
54
- "trace dropped entirely due to `Pipeline.before_flush` error: #{e}"
54
+ "trace dropped entirely due to `Pipeline.before_flush` error: #{e.class}: #{e.message}"
55
55
  )
56
56
 
57
57
  nil
@@ -43,7 +43,7 @@ module Datadog
43
43
 
44
44
  Datadog.send(:components).telemetry.client_configuration_change!(env_vars)
45
45
  rescue => e
46
- content.errored("#{e.class.name} #{e.message}: #{Array(e.backtrace).join("\n")}")
46
+ content.errored("#{e.class}: #{e.message}: #{Array(e.backtrace).join("\n")}")
47
47
  end
48
48
 
49
49
  def receivers(_telemetry)
@@ -47,6 +47,8 @@ module Datadog
47
47
  MANUAL = '-4'
48
48
  # Formerly AppSec.
49
49
  ASM = '-5'
50
+ # AI Guard.
51
+ AI_GUARD = '-13'
50
52
  # Dynamically configured rule, explicitly created by the user.
51
53
  REMOTE_USER_RULE = '-11'
52
54
  # Dynamically configured rule, automatically generated by Datadog.
@@ -77,6 +77,19 @@ module Datadog
77
77
  end
78
78
  end
79
79
 
80
+ def reconsider_sample_resource!(trace)
81
+ return unless @priority_sampler.respond_to?(:reconsider_sample_resource!)
82
+
83
+ preserving_sampling(trace) do
84
+ @priority_sampler.reconsider_sample_resource!(trace)
85
+ end
86
+ end
87
+
88
+ def resource_sampling?
89
+ @priority_sampler.respond_to?(:resource_sampling?) &&
90
+ @priority_sampler.resource_sampling?
91
+ end
92
+
80
93
  # (see Datadog::Tracing::Sampling::RateByServiceSampler#update)
81
94
  def update(rate_by_service, decision: nil)
82
95
  @priority_sampler.update(rate_by_service, decision: decision)