datadog 2.37.0 → 2.38.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -1
  3. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +23 -15
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +19 -24
  5. data/ext/datadog_profiling_native_extension/collectors_stack.h +0 -1
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +155 -141
  7. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +1 -0
  8. data/ext/datadog_profiling_native_extension/extconf.rb +0 -3
  9. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +5 -0
  10. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +2 -0
  11. data/ext/datadog_profiling_native_extension/time_helpers.h +12 -7
  12. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +5 -0
  13. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +5 -0
  14. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
  15. data/lib/datadog/core/configuration/settings.rb +4 -4
  16. data/lib/datadog/core/configuration/supported_configurations.rb +1 -0
  17. data/lib/datadog/core/knuth_sampler.rb +3 -1
  18. data/lib/datadog/core/metrics/client.rb +1 -1
  19. data/lib/datadog/core/transport/http/builder.rb +1 -1
  20. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +1 -1
  21. data/lib/datadog/core/utils/time.rb +5 -1
  22. data/lib/datadog/di/boot.rb +3 -0
  23. data/lib/datadog/di/capture_expression.rb +21 -0
  24. data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
  25. data/lib/datadog/di/capture_limits.rb +41 -0
  26. data/lib/datadog/di/component.rb +2 -2
  27. data/lib/datadog/di/configuration.rb +6 -0
  28. data/lib/datadog/di/context.rb +9 -3
  29. data/lib/datadog/di/el/compiler.rb +1 -1
  30. data/lib/datadog/di/instrumenter.rb +34 -3
  31. data/lib/datadog/di/probe.rb +47 -2
  32. data/lib/datadog/di/probe_builder.rb +83 -1
  33. data/lib/datadog/di/probe_notification_builder.rb +48 -7
  34. data/lib/datadog/di/remote.rb +1 -1
  35. data/lib/datadog/di/serializer.rb +22 -10
  36. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +5 -0
  37. data/lib/datadog/profiling/collectors/thread_context.rb +9 -1
  38. data/lib/datadog/profiling/component.rb +17 -0
  39. data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
  40. data/lib/datadog/tracing/contrib/active_job/patcher.rb +12 -0
  41. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +2 -3
  42. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +12 -12
  43. data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
  44. data/lib/datadog/tracing/contrib/sequel/utils.rb +2 -2
  45. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +1 -1
  46. data/lib/datadog/tracing/contrib/sinatra/framework.rb +1 -1
  47. data/lib/datadog/tracing/transport/trace_formatter.rb +1 -1
  48. data/lib/datadog/version.rb +1 -1
  49. metadata +9 -5
@@ -131,9 +131,12 @@ module Datadog
131
131
  # in upstream code.
132
132
  def serialize_args(args, kwargs, target_self,
133
133
  depth: settings.dynamic_instrumentation.max_capture_depth,
134
- attribute_count: settings.dynamic_instrumentation.max_capture_attribute_count)
134
+ attribute_count: settings.dynamic_instrumentation.max_capture_attribute_count,
135
+ length: nil,
136
+ collection_size: nil)
135
137
  combined = combine_args(args, kwargs, target_self)
136
- serialize_vars(combined, depth: depth, attribute_count: attribute_count)
138
+ serialize_vars(combined, depth: depth, attribute_count: attribute_count,
139
+ length: length, collection_size: collection_size)
137
140
  end
138
141
 
139
142
  # Serializes variables captured by a line probe.
@@ -142,9 +145,12 @@ module Datadog
142
145
  # of executed code.
143
146
  def serialize_vars(vars,
144
147
  depth: settings.dynamic_instrumentation.max_capture_depth,
145
- attribute_count: settings.dynamic_instrumentation.max_capture_attribute_count)
148
+ attribute_count: settings.dynamic_instrumentation.max_capture_attribute_count,
149
+ length: nil,
150
+ collection_size: nil)
146
151
  vars.each_with_object({}) do |(k, v), agg|
147
- agg[k] = serialize_value(v, name: k, depth: depth, attribute_count: attribute_count)
152
+ agg[k] = serialize_value(v, name: k, depth: depth, attribute_count: attribute_count,
153
+ length: length, collection_size: collection_size)
148
154
  end
149
155
  end
150
156
 
@@ -163,6 +169,8 @@ module Datadog
163
169
  def serialize_value(value, name: nil,
164
170
  depth: settings.dynamic_instrumentation.max_capture_depth,
165
171
  attribute_count: nil,
172
+ length: nil,
173
+ collection_size: nil,
166
174
  type: nil)
167
175
  attribute_count ||= settings.dynamic_instrumentation.max_capture_attribute_count
168
176
  cls = type || value.class
@@ -235,7 +243,8 @@ module Datadog
235
243
  #
236
244
  # Truncate binary data BEFORE escaping to avoid cutting mid-escape-sequence.
237
245
  # For regular strings, the limit is applied to string length in characters.
238
- max = settings.dynamic_instrumentation.max_capture_string_length
246
+ length ||= settings.dynamic_instrumentation.max_capture_string_length
247
+ max = length
239
248
 
240
249
  if value.encoding == Encoding::BINARY || !value.valid_encoding?
241
250
  # Truncate binary data BEFORE escaping to avoid cutting mid-escape-sequence
@@ -263,7 +272,8 @@ module Datadog
263
272
  if depth <= 0
264
273
  serialized.update(notCapturedReason: "depth")
265
274
  else
266
- max = settings.dynamic_instrumentation.max_capture_collection_size
275
+ collection_size ||= settings.dynamic_instrumentation.max_capture_collection_size
276
+ max = collection_size
267
277
  if max != 0 && value.length > max
268
278
  serialized.update(notCapturedReason: "collectionSize", size: value.length)
269
279
  # same steep failure with array slices.
@@ -271,7 +281,7 @@ module Datadog
271
281
  value = value[0...max] || []
272
282
  end
273
283
  entries = value.map do |elt|
274
- serialize_value(elt, depth: depth - 1)
284
+ serialize_value(elt, depth: depth - 1, length: length, collection_size: collection_size, attribute_count: attribute_count)
275
285
  end
276
286
  serialized.update(elements: entries)
277
287
  end
@@ -279,7 +289,8 @@ module Datadog
279
289
  if depth <= 0
280
290
  serialized.update(notCapturedReason: "depth")
281
291
  else
282
- max = settings.dynamic_instrumentation.max_capture_collection_size
292
+ collection_size ||= settings.dynamic_instrumentation.max_capture_collection_size
293
+ max = collection_size
283
294
  cur = 0
284
295
  entries = []
285
296
  value.each do |k, v|
@@ -288,7 +299,8 @@ module Datadog
288
299
  break
289
300
  end
290
301
  cur += 1
291
- entries << [serialize_value(k, depth: depth - 1), serialize_value(v, name: k, depth: depth - 1)]
302
+ entries << [serialize_value(k, depth: depth - 1, length: length, collection_size: collection_size, attribute_count: attribute_count),
303
+ serialize_value(v, name: k, depth: depth - 1, length: length, collection_size: collection_size, attribute_count: attribute_count)]
292
304
  end
293
305
  serialized.update(entries: entries)
294
306
  end
@@ -327,7 +339,7 @@ module Datadog
327
339
  break
328
340
  end
329
341
  cur += 1
330
- fields[ivar] = serialize_value(value.instance_variable_get(ivar), name: ivar, depth: depth - 1)
342
+ fields[ivar] = serialize_value(value.instance_variable_get(ivar), name: ivar, depth: depth - 1, length: length, collection_size: collection_size, attribute_count: attribute_count)
331
343
  end
332
344
  serialized.update(fields: fields)
333
345
  end
@@ -170,11 +170,16 @@ module Datadog
170
170
 
171
171
  @wait_until_running_condition.wait(@wait_until_running_mutex, timeout_seconds)
172
172
 
173
+ # Not using `_native_is_running?(self) || raise(...)` here: `_native_is_running?` is declared
174
+ # to return `bool`, so steep infers the `||` expression as `bool` too and rejects it against
175
+ # this method's declared `true` return type. The explicit `if`/`else` type-checks correctly.
176
+ # rubocop:disable Style/RedundantCondition
173
177
  if self.class._native_is_running?(self)
174
178
  true
175
179
  else
176
180
  raise "Timeout waiting for #{self.class.name} to start (waited for #{timeout_seconds} seconds)"
177
181
  end
182
+ # rubocop:enable Style/RedundantCondition
178
183
  end
179
184
  end
180
185
 
@@ -45,9 +45,10 @@ module Datadog
45
45
  waiting_for_gvl_threshold_ns: 10_000_000,
46
46
  otel_context_enabled: false,
47
47
  native_filenames_enabled: true,
48
+ trigger_global_reset: true,
48
49
  **options
49
50
  )
50
- new(
51
+ collector = new(
51
52
  recorder: recorder,
52
53
  max_frames: max_frames,
53
54
  tracer: tracer,
@@ -57,6 +58,13 @@ module Datadog
57
58
  native_filenames_enabled: native_filenames_enabled,
58
59
  **options,
59
60
  )
61
+
62
+ # By default, mirror what the CpuAndWallTimeWorker does when profiling starts: reset the global per-thread
63
+ # context state, which (among other things) resizes the per-thread sampling buffers to this collector's
64
+ # max_frames. Tests that need to control this explicitly can pass `trigger_global_reset: false`.
65
+ Testing._native_global_reset_per_thread_context(collector) if trigger_global_reset
66
+
67
+ collector
60
68
  end
61
69
 
62
70
  def inspect
@@ -226,6 +226,15 @@ module Datadog
226
226
  return false
227
227
  end
228
228
 
229
+ # Heap profiling relies on `ObjectSpace._id2ref`, which was removed on Ruby 4.1
230
+ # (https://bugs.ruby-lang.org/issues/22135).
231
+ if RubyVersion.is?(">= 4.1")
232
+ logger.warn(
233
+ "Heap profiling is currently incompatible with Ruby 4.1+ and has been disabled."
234
+ )
235
+ return false
236
+ end
237
+
229
238
  unless allocation_profiling_enabled
230
239
  logger.warn(
231
240
  "Heap profiling was requested but allocation profiling is not enabled. " \
@@ -244,6 +253,14 @@ module Datadog
244
253
 
245
254
  return false unless heap_profiling_enabled && heap_size_profiling_enabled
246
255
 
256
+ if RubyVersion.is?(">= 4")
257
+ logger.info(
258
+ "Heap live size profiling is currently incompatible with Ruby 4 and has been disabled. " \
259
+ "Heap live objects is not affected and remains enabled."
260
+ )
261
+ return false
262
+ end
263
+
247
264
  true
248
265
  end
249
266
 
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../data_streams'
4
+ require_relative 'ext'
5
+
6
+ module Datadog
7
+ module Tracing
8
+ module Contrib
9
+ module ActiveJob
10
+ # Propagates Data Streams Monitoring pathway context through the serialized
11
+ # job payload, allowing produce (enqueue) and consume (perform) checkpoints
12
+ # to be connected across the process boundary.
13
+ #
14
+ # The DSM calls are rescued in isolation so a checkpoint failure can never
15
+ # break a job, while a genuine error raised by the underlying serialization
16
+ # still propagates.
17
+ #
18
+ # Only prepended on ActiveJob 5.0+, where `serialize`/`deserialize` are instance
19
+ # methods the framework routes through. ActiveJob 4.2 deserializes via a class
20
+ # method, so the pathway cannot be propagated there.
21
+ module DataStreams
22
+ def serialize
23
+ job_data = super
24
+ return job_data unless Datadog::DataStreams.enabled?
25
+
26
+ begin
27
+ Datadog::DataStreams.set_produce_checkpoint(
28
+ type: Ext::TAG_COMPONENT,
29
+ destination: queue_name,
30
+ auto_instrumentation: true
31
+ ) do |key, value|
32
+ job_data[key] = value
33
+ end
34
+ rescue => e
35
+ Datadog.logger.debug { "Error setting DSM produce checkpoint: #{e.class}: #{e.message}" }
36
+ end
37
+
38
+ job_data
39
+ end
40
+
41
+ def deserialize(job_data)
42
+ super
43
+
44
+ return unless Datadog::DataStreams.enabled?
45
+
46
+ begin
47
+ Datadog::DataStreams.set_consume_checkpoint(
48
+ type: Ext::TAG_COMPONENT,
49
+ source: queue_name,
50
+ auto_instrumentation: true
51
+ ) do |key|
52
+ job_data[key]
53
+ end
54
+ rescue => e
55
+ Datadog.logger.debug { "Error setting DSM consume checkpoint: #{e.class}: #{e.message}" }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../patcher'
4
+ require_relative 'data_streams'
4
5
  require_relative 'ext'
5
6
  require_relative 'events'
6
7
  require_relative 'log_injection'
@@ -22,6 +23,7 @@ module Datadog
22
23
  def patch
23
24
  Events.subscribe!
24
25
  inject_log_correlation
26
+ inject_data_streams
25
27
  end
26
28
 
27
29
  def inject_log_correlation
@@ -33,6 +35,16 @@ module Datadog
33
35
  end
34
36
  end
35
37
  end
38
+
39
+ def inject_data_streams
40
+ # ActiveJob 4.2 deserializes jobs through a class method rather than the
41
+ # instance method, so the DSM pathway can only be propagated on 5.0+.
42
+ return if ::ActiveJob.gem_version < Gem::Version.new('5.0.0')
43
+
44
+ ::ActiveSupport.on_load(:active_job) do
45
+ prepend DataStreams
46
+ end
47
+ end
36
48
  end
37
49
  end
38
50
  end
@@ -18,10 +18,9 @@ module Datadog
18
18
 
19
19
  def supported?
20
20
  Gem.loaded_specs['active_model_serializers'] \
21
- && (
22
- Gem.loaded_specs['active_model_serializers'].version >= Gem::Version.new('0.9') \
21
+ &&
22
+ Gem.loaded_specs['active_model_serializers'].version >= Gem::Version.new('0.9') \
23
23
  && Gem.loaded_specs['active_model_serializers'].version < Gem::Version.new('0.10')
24
- )
25
24
  end
26
25
 
27
26
  def event_name
@@ -28,19 +28,19 @@ module Datadog
28
28
  end
29
29
  end
30
30
 
31
- def exec_params(sql, params, *args, &block)
31
+ def exec_params(sql, *args, &block)
32
32
  return super unless enabled?
33
33
 
34
34
  trace(Ext::SPAN_EXEC_PARAMS, sql: sql, block: block) do |sql_statement, wrapped_block|
35
- super(sql_statement, params, *args, &wrapped_block)
35
+ super(sql_statement, *args, &wrapped_block)
36
36
  end
37
37
  end
38
38
 
39
- def exec_prepared(statement_name, params, *args, &block)
39
+ def exec_prepared(statement_name, *args, &block)
40
40
  return super unless enabled?
41
41
 
42
42
  trace(Ext::SPAN_EXEC_PREPARED, statement_name: statement_name, block: block) do |_, wrapped_block|
43
- super(statement_name, params, *args, &wrapped_block)
43
+ super(statement_name, *args, &wrapped_block)
44
44
  end
45
45
  end
46
46
 
@@ -54,20 +54,20 @@ module Datadog
54
54
  end
55
55
 
56
56
  # async_exec_params is an alias to exec_params
57
- def async_exec_params(sql, params, *args, &block)
57
+ def async_exec_params(sql, *args, &block)
58
58
  return super unless enabled?
59
59
 
60
60
  trace(Ext::SPAN_ASYNC_EXEC_PARAMS, sql: sql, block: block) do |sql_statement, wrapped_block|
61
- super(sql_statement, params, *args, &wrapped_block)
61
+ super(sql_statement, *args, &wrapped_block)
62
62
  end
63
63
  end
64
64
 
65
65
  # async_exec_prepared is an alias to exec_prepared
66
- def async_exec_prepared(statement_name, params, *args, &block)
66
+ def async_exec_prepared(statement_name, *args, &block)
67
67
  return super unless enabled?
68
68
 
69
69
  trace(Ext::SPAN_ASYNC_EXEC_PREPARED, statement_name: statement_name, block: block) do |_, wrapped_block|
70
- super(statement_name, params, *args, &wrapped_block)
70
+ super(statement_name, *args, &wrapped_block)
71
71
  end
72
72
  end
73
73
 
@@ -79,19 +79,19 @@ module Datadog
79
79
  end
80
80
  end
81
81
 
82
- def sync_exec_params(sql, params, *args, &block)
82
+ def sync_exec_params(sql, *args, &block)
83
83
  return super unless enabled?
84
84
 
85
85
  trace(Ext::SPAN_SYNC_EXEC_PARAMS, sql: sql, block: block) do |sql_statement, wrapped_block|
86
- super(sql_statement, params, *args, &wrapped_block)
86
+ super(sql_statement, *args, &wrapped_block)
87
87
  end
88
88
  end
89
89
 
90
- def sync_exec_prepared(statement_name, params, *args, &block)
90
+ def sync_exec_prepared(statement_name, *args, &block)
91
91
  return super unless enabled?
92
92
 
93
93
  trace(Ext::SPAN_SYNC_EXEC_PREPARED, statement_name: statement_name, block: block) do |_, wrapped_block|
94
- super(statement_name, params, *args, &wrapped_block)
94
+ super(statement_name, *args, &wrapped_block)
95
95
  end
96
96
  end
97
97
 
@@ -56,7 +56,7 @@ module Datadog
56
56
  end
57
57
  end
58
58
 
59
- following = (middleware.instance_variable_get('@app') if middleware.instance_variable_defined?('@app'))
59
+ following = (middleware.instance_variable_get(:@app) if middleware.instance_variable_defined?(:@app))
60
60
 
61
61
  retain_middleware_name(following)
62
62
  end
@@ -34,8 +34,8 @@ module Datadog
34
34
 
35
35
  def parse_opts(sql, opts, db_opts, dataset = nil)
36
36
  # Prepared statements don't provide their sql query in the +sql+ parameter.
37
- if !sql.is_a?(String) && (dataset&.respond_to?(:prepared_sql) &&
38
- (resolved_sql = dataset.prepared_sql))
37
+ if !sql.is_a?(String) && dataset&.respond_to?(:prepared_sql) &&
38
+ (resolved_sql = dataset.prepared_sql)
39
39
  # The dataset contains the resolved SQL query and prepared statement name.
40
40
  prepared_name = dataset.prepared_statement_name
41
41
  sql = resolved_sql
@@ -69,7 +69,7 @@ module Datadog
69
69
 
70
70
  args = job['args']
71
71
  if args && !args.empty?
72
- span.set_tag(Ext::TAG_JOB_ARGS, Contrib::Utils::Quantization::HashFormatter.format(args, (@quantize[:args] || {})))
72
+ span.set_tag(Ext::TAG_JOB_ARGS, Contrib::Utils::Quantization::HashFormatter.format(args, @quantize[:args] || {}))
73
73
  end
74
74
 
75
75
  yield
@@ -81,7 +81,7 @@ module Datadog
81
81
  # The block gets passed prepared arguments for the caller to decide
82
82
  # how to insert.
83
83
  def self.insert_middleware(builder, middleware, args, block)
84
- use = builder.instance_variable_get('@use')
84
+ use = builder.instance_variable_get(:@use)
85
85
  wrapped = wrap_middleware(middleware, *args, &block)
86
86
 
87
87
  # Makes the insert idempotent
@@ -117,7 +117,7 @@ module Datadog
117
117
 
118
118
  root_span.set_tag(
119
119
  Tracing::Metadata::Ext::Distributed::TAG_KNUTH_SAMPLING_RATE,
120
- format('%.6f', (rate * 1e6).round / 1e6.to_f).sub(/\.?0+\z/, '')
120
+ format('%.6f', (rate * 1e6).round / 1e6).sub(/\.?0+\z/, '')
121
121
  )
122
122
  end
123
123
 
@@ -3,7 +3,7 @@
3
3
  module Datadog
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 37
6
+ MINOR = 38
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
  BUILD = nil
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.37.0
4
+ version: 2.38.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '3.5'
33
33
  - - ">="
34
34
  - !ruby/object:Gem::Version
35
- version: 3.5.2
35
+ version: 3.5.3
36
36
  type: :runtime
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  version: '3.5'
43
43
  - - ">="
44
44
  - !ruby/object:Gem::Version
45
- version: 3.5.2
45
+ version: 3.5.3
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: libddwaf
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -520,6 +520,9 @@ files:
520
520
  - lib/datadog/di.rb
521
521
  - lib/datadog/di/base.rb
522
522
  - lib/datadog/di/boot.rb
523
+ - lib/datadog/di/capture_expression.rb
524
+ - lib/datadog/di/capture_expression_evaluator.rb
525
+ - lib/datadog/di/capture_limits.rb
523
526
  - lib/datadog/di/code_tracker.rb
524
527
  - lib/datadog/di/component.rb
525
528
  - lib/datadog/di/configuration.rb
@@ -703,6 +706,7 @@ files:
703
706
  - lib/datadog/tracing/contrib/action_view/patcher.rb
704
707
  - lib/datadog/tracing/contrib/action_view/utils.rb
705
708
  - lib/datadog/tracing/contrib/active_job/configuration/settings.rb
709
+ - lib/datadog/tracing/contrib/active_job/data_streams.rb
706
710
  - lib/datadog/tracing/contrib/active_job/event.rb
707
711
  - lib/datadog/tracing/contrib/active_job/events.rb
708
712
  - lib/datadog/tracing/contrib/active_job/events/discard.rb
@@ -1153,8 +1157,8 @@ licenses:
1153
1157
  - Apache-2.0
1154
1158
  metadata:
1155
1159
  allowed_push_host: https://rubygems.org
1156
- changelog_uri: https://github.com/DataDog/dd-trace-rb/blob/v2.37.0/CHANGELOG.md
1157
- source_code_uri: https://github.com/DataDog/dd-trace-rb/tree/v2.37.0
1160
+ changelog_uri: https://github.com/DataDog/dd-trace-rb/blob/v2.38.0/CHANGELOG.md
1161
+ source_code_uri: https://github.com/DataDog/dd-trace-rb/tree/v2.38.0
1158
1162
  post_install_message: 'JRuby support in the datadog gem is deprecated. Details: https://dtdg.co/jruby-deprecation'
1159
1163
  rdoc_options: []
1160
1164
  require_paths: