datadog 2.45.0 → 2.46.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -2
  3. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +4 -0
  4. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +2 -0
  5. data/ext/datadog_profiling_native_extension/profiling.c +6 -0
  6. data/ext/libdatadog_api/di.c +30 -5
  7. data/ext/libdatadog_api/extconf.rb +2 -0
  8. data/ext/libdatadog_api/otel_thread_context.c +20 -14
  9. data/ext/libdatadog_api/process_discovery.c +15 -1
  10. data/ext/libdatadog_api/trace_exporter.c +105 -25
  11. data/ext/libdatadog_extconf_helpers.rb +1 -1
  12. data/lib/datadog/core/configuration/components.rb +29 -8
  13. data/lib/datadog/core/configuration/supported_configurations.rb +5 -2
  14. data/lib/datadog/core/process_discovery.rb +5 -1
  15. data/lib/datadog/core/remote/client/capabilities.rb +34 -29
  16. data/lib/datadog/core/remote/component.rb +28 -12
  17. data/lib/datadog/core/remote/dispatcher.rb +13 -3
  18. data/lib/datadog/di/boot.rb +1 -0
  19. data/lib/datadog/di/capture_expression_evaluator/result.rb +30 -0
  20. data/lib/datadog/di/capture_expression_evaluator.rb +39 -9
  21. data/lib/datadog/di/component.rb +38 -3
  22. data/lib/datadog/di/configuration.rb +53 -2
  23. data/lib/datadog/di/context.rb +7 -2
  24. data/lib/datadog/di/contrib/active_record.rb +8 -2
  25. data/lib/datadog/di/evaluation_failure.rb +28 -0
  26. data/lib/datadog/di/guardrails_telemetry/reason.rb +87 -0
  27. data/lib/datadog/di/guardrails_telemetry.rb +203 -0
  28. data/lib/datadog/di/instrumenter.rb +65 -15
  29. data/lib/datadog/di/logger.rb +30 -8
  30. data/lib/datadog/di/probe_manager.rb +48 -5
  31. data/lib/datadog/di/probe_notification_builder.rb +28 -26
  32. data/lib/datadog/di/probe_notifier_worker.rb +56 -6
  33. data/lib/datadog/di/serializer.rb +130 -54
  34. data/lib/datadog/di/snapshot_event.rb +38 -0
  35. data/lib/datadog/di/transport/http.rb +13 -3
  36. data/lib/datadog/di/transport/input.rb +32 -7
  37. data/lib/datadog/open_feature/activation.rb +268 -0
  38. data/lib/datadog/open_feature/component.rb +31 -15
  39. data/lib/datadog/open_feature/provider.rb +282 -5
  40. data/lib/datadog/open_feature.rb +31 -1
  41. data/lib/datadog/tracing/component.rb +14 -0
  42. data/lib/datadog/tracing/configuration/settings.rb +3 -1
  43. data/lib/datadog/tracing/otel_thread_context.rb +3 -5
  44. data/lib/datadog/tracing/transport/http/telemetry.rb +94 -0
  45. data/lib/datadog/tracing/transport/http/traces.rb +2 -0
  46. data/lib/datadog/tracing/transport/native/telemetry.rb +17 -0
  47. data/lib/datadog/tracing/transport/native.rb +16 -1
  48. data/lib/datadog/tracing/transport/telemetry.rb +62 -0
  49. data/lib/datadog/tracing/transport/traces.rb +82 -15
  50. data/lib/datadog/version.rb +1 -1
  51. metadata +16 -7
@@ -2,8 +2,8 @@
2
2
 
3
3
  # rubocop:disable Lint/AssignmentInCondition
4
4
 
5
+ require_relative "evaluation_failure"
5
6
  require_relative "fatal_exceptions"
6
- require_relative "capture_expression_evaluator"
7
7
 
8
8
  module Datadog
9
9
  module DI
@@ -11,14 +11,18 @@ module Datadog
11
11
  #
12
12
  # @api private
13
13
  class ProbeNotificationBuilder
14
- def initialize(settings, serializer, logger, telemetry: nil)
14
+ # Initializes the builder.
15
+ #
16
+ # @param settings [Datadog::Core::Configuration::Settings] tracer settings
17
+ # @param serializer [Serializer] serializer for captured values
18
+ # @param logger [DI::Logger] logger for builder diagnostics
19
+ # @param capture_expression_evaluator [CaptureExpressionEvaluator] evaluator for the probe's capture expressions
20
+ # @return [void]
21
+ def initialize(settings, serializer, logger, capture_expression_evaluator:)
15
22
  @settings = settings
16
23
  @serializer = serializer
17
24
  @logger = logger
18
- @telemetry = telemetry
19
- @capture_expression_evaluator = CaptureExpressionEvaluator.new(
20
- settings: settings, serializer: serializer, logger: logger, telemetry: telemetry,
21
- )
25
+ @capture_expression_evaluator = capture_expression_evaluator
22
26
  end
23
27
 
24
28
  attr_reader :settings
@@ -27,8 +31,6 @@ module Datadog
27
31
 
28
32
  attr_reader :logger
29
33
 
30
- attr_reader :telemetry
31
-
32
34
  attr_reader :capture_expression_evaluator
33
35
 
34
36
  def build_received(probe)
@@ -53,7 +55,7 @@ module Datadog
53
55
  build_status(probe,
54
56
  message: "Instrumentation for probe #{probe.id} failed: #{exc}",
55
57
  status: "ERROR",
56
- exception: exc)
58
+ exception: exc,)
57
59
  end
58
60
 
59
61
  def build_disabled(probe, duration)
@@ -83,10 +85,11 @@ module Datadog
83
85
  capture_expression_evaluation_errors = []
84
86
  captures = if probe.capture_snapshot?
85
87
  snapshot_limits = probe.snapshot_serializer_limits(settings)
88
+ serialization_deadline = serializer.serialization_deadline
86
89
  if probe.method?
87
90
  return_arguments = {
88
- "@return": serializer.serialize_value(context.return_value, **snapshot_limits),
89
- self: serializer.serialize_value(context.target_self, **snapshot_limits),
91
+ "@return": serializer.serialize_value(context.return_value, deadline: serialization_deadline, **snapshot_limits),
92
+ self: serializer.serialize_value(context.target_self, deadline: serialization_deadline, **snapshot_limits),
90
93
  }
91
94
  {
92
95
  entry: {
@@ -99,10 +102,10 @@ module Datadog
99
102
  }
100
103
  elsif probe.line?
101
104
  {
102
- lines: (locals = context.serialized_locals) && {
105
+ lines: (locals = context.serialized_locals(serialization_deadline: serialization_deadline)) && {
103
106
  probe.line_no => {
104
107
  locals: locals,
105
- arguments: {self: serializer.serialize_value(context.target_self, **snapshot_limits)},
108
+ arguments: {self: serializer.serialize_value(context.target_self, deadline: serialization_deadline, **snapshot_limits)},
106
109
  },
107
110
  },
108
111
  }
@@ -116,8 +119,9 @@ module Datadog
116
119
  entry: {captureExpressions: captured_block},
117
120
  }
118
121
  else
119
- captured_block, capture_expression_evaluation_errors =
120
- capture_expression_evaluator.evaluate(probe, context)
122
+ capture_evaluation_result = capture_expression_evaluator.evaluate(probe, context)
123
+ captured_block = capture_evaluation_result.serialized_values
124
+ capture_expression_evaluation_errors = capture_evaluation_result.errors
121
125
  {
122
126
  return: {
123
127
  captureExpressions: captured_block,
@@ -126,8 +130,9 @@ module Datadog
126
130
  }
127
131
  end
128
132
  elsif probe.line?
129
- captured_block, capture_expression_evaluation_errors =
130
- capture_expression_evaluator.evaluate(probe, context)
133
+ capture_evaluation_result = capture_expression_evaluator.evaluate(probe, context)
134
+ captured_block = capture_evaluation_result.serialized_values
135
+ capture_expression_evaluation_errors = capture_evaluation_result.errors
131
136
  {
132
137
  lines: {
133
138
  probe.line_no => {captureExpressions: captured_block},
@@ -144,14 +149,11 @@ module Datadog
144
149
  evaluation_errors.concat(capture_expression_evaluation_errors)
145
150
  build_snapshot_base(context,
146
151
  evaluation_errors: evaluation_errors, message: message,
147
- captures: captures)
152
+ captures: captures,)
148
153
  end
149
154
 
150
155
  def build_condition_evaluation_failed(context, expression, exception)
151
- error = {
152
- message: "#{exception.class}: #{exception}",
153
- expr: expression.dsl_expr,
154
- }
156
+ error = EvaluationFailure.new(expr: expression.dsl_expr, message: "#{exception.class}: #{exception}")
155
157
  build_snapshot_base(context, evaluation_errors: [error])
156
158
  end
157
159
 
@@ -350,7 +352,7 @@ module Datadog
350
352
  snapshot: {
351
353
  id: SecureRandom.uuid,
352
354
  timestamp: timestamp,
353
- evaluationErrors: evaluation_errors,
355
+ evaluationErrors: evaluation_errors.map { |failure| {expr: failure.expr, message: failure.message} },
354
356
  probe: {
355
357
  id: probe.id,
356
358
  version: 0,
@@ -412,10 +414,10 @@ module Datadog
412
414
  end
413
415
  rescue Exception => exc # standard:disable Lint/RescueException
414
416
  Datadog::DI.reraise_if_fatal(exc)
415
- evaluation_errors << {
416
- message: "#{exc.class}: #{exc.message}",
417
+ evaluation_errors << EvaluationFailure.new(
417
418
  expr: segment.dsl_expr, # steep:ignore NoMethod
418
- }
419
+ message: "#{exc.class}: #{exc.message}",
420
+ )
419
421
  "[evaluation error]"
420
422
  end.join
421
423
  [message, evaluation_errors]
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative "../core/semaphore"
4
4
  require_relative "fatal_exceptions"
5
+ require_relative "guardrails_telemetry"
6
+ require_relative "snapshot_event"
5
7
 
6
8
  module Datadog
7
9
  module DI
@@ -24,14 +26,23 @@ module Datadog
24
26
  #
25
27
  # @api private
26
28
  class ProbeNotifierWorker
29
+ # Initializes the background worker.
30
+ #
31
+ # @param settings [Datadog::Core::Configuration::Settings] tracer settings
32
+ # @param logger [DI::Logger] logger for worker diagnostics
33
+ # @param agent_settings [Datadog::Core::Configuration::AgentSettings] agent connection settings for the transports
27
34
  # @param probe_repository [ProbeRepository] Repository for looking up probes.
28
35
  # Used for handling serialization errors (disabling affected probes).
29
36
  # @param probe_notification_builder [ProbeNotificationBuilder] Builder for
30
37
  # creating status notifications. Used for reporting ERROR status.
38
+ # @param telemetry [Datadog::Core::Telemetry::Component, nil] component worker errors are reported through
39
+ # @param guardrails_telemetry [GuardrailsTelemetry] emitter for the canonical
40
+ # guardrails drop metric at the queue-full admission drop
31
41
  def initialize(settings, logger, agent_settings:,
32
- probe_repository:, probe_notification_builder:, telemetry: nil)
42
+ probe_repository:, probe_notification_builder:, guardrails_telemetry:, telemetry: nil)
33
43
  @settings = settings
34
44
  @telemetry = telemetry
45
+ @guardrails_telemetry = guardrails_telemetry
35
46
  @status_queue = []
36
47
  @snapshot_queue = []
37
48
  @agent_settings = agent_settings
@@ -42,6 +53,8 @@ module Datadog
42
53
  @sleep_remaining = nil
43
54
  @wake_scheduled = false
44
55
  @thread = nil
56
+ @status_transport = nil
57
+ @snapshot_transport = nil
45
58
  @pid = nil
46
59
  @flush = 0
47
60
  @probe_repository = probe_repository
@@ -51,6 +64,9 @@ module Datadog
51
64
  attr_reader :settings
52
65
  attr_reader :logger
53
66
  attr_reader :telemetry
67
+ # The guardrails metric emitter.
68
+ # @return [GuardrailsTelemetry]
69
+ attr_reader :guardrails_telemetry
54
70
  attr_reader :agent_settings
55
71
  attr_reader :probe_repository
56
72
  attr_reader :probe_notification_builder
@@ -188,6 +204,9 @@ module Datadog
188
204
 
189
205
  attr_reader :last_sent
190
206
 
207
+ # Builds and memoizes the diagnostics transport for status events.
208
+ #
209
+ # @return [DI::Transport::Diagnostics::Transport]
191
210
  def status_transport
192
211
  @status_transport ||= DI::Transport::HTTP.diagnostics(agent_settings: agent_settings, logger: logger)
193
212
  end
@@ -196,8 +215,12 @@ module Datadog
196
215
  status_transport.send_diagnostics(batch)
197
216
  end
198
217
 
218
+ # Builds and memoizes the snapshot input transport.
219
+ #
220
+ # @return [DI::Transport::Input::Transport]
199
221
  def snapshot_transport
200
- @snapshot_transport ||= DI::Transport::HTTP.input(agent_settings: agent_settings, logger: logger, telemetry: telemetry)
222
+ @snapshot_transport ||= DI::Transport::HTTP.input(agent_settings: agent_settings, logger: logger,
223
+ telemetry: telemetry, guardrails_telemetry: guardrails_telemetry,)
201
224
  end
202
225
 
203
226
  # Sends a batch of snapshot payloads to the agent.
@@ -206,7 +229,7 @@ module Datadog
206
229
  # serialization failures via callback. This allows healthy probes
207
230
  # to continue working even when one probe produces un-serializable data.
208
231
  #
209
- # @param batch [Array<Hash>] Array of snapshot payload hashes
232
+ # @param batch [Array<SnapshotEvent>] the snapshot events queued for sending
210
233
  def do_send_snapshot(batch)
211
234
  snapshot_transport.send_input(batch, tags, on_serialization_error: method(:handle_serialization_error))
212
235
  end
@@ -259,6 +282,12 @@ module Datadog
259
282
  # Signals the background thread to wake up (and do the sending)
260
283
  # if it has been more than 1 second since the last send of the same
261
284
  # event type.
285
+ #
286
+ # @param event [Hash] the status or snapshot payload to enqueue
287
+ # @param probe [Probe, nil] the probe the payload belongs to, for status log
288
+ # attribution, the queue-full drop event type, and the queued
289
+ # snapshot event's guardrails event type
290
+ # @return [void]
262
291
  define_method("add_#{event_type}") do |event, probe: nil|
263
292
  # Enqueueing reads DI configuration and writes debug/trace logs, both
264
293
  # of which can call stdlib methods (e.g. Set#include? via the
@@ -275,12 +304,28 @@ module Datadog
275
304
  begin
276
305
  @lock.synchronize do
277
306
  queue = send("#{event_type}_queue")
307
+ guardrails_event_type = if event_type == :snapshot
308
+ probe ? GuardrailsTelemetry.probe_event_type_tag(probe) : GuardrailsTelemetry::EVENT_TYPE_SNAPSHOT
309
+ end
278
310
  if queue.length > settings.dynamic_instrumentation.internal.snapshot_queue_capacity
279
311
  if event_type == :status && probe
280
312
  status = event.dig(:debugger, :diagnostics, :status)
281
- logger.debug { "di: dropping status for #{probe.type} probe at #{probe.location} (#{probe.id}): #{status} because queue is full" }
313
+ logger.debug do
314
+ "di: dropping status for #{probe.type} probe at #{probe.location}" \
315
+ " (#{probe.id}): #{status} because queue is full" \
316
+ " (#{GuardrailsTelemetry::Reason::QUEUE_FULL})"
317
+ end
282
318
  else
283
- logger.debug { "di: #{self.class.name}: dropping #{event_type} event because queue is full" }
319
+ logger.debug do
320
+ "di: #{self.class.name}: dropping #{event_type} event because queue is full" \
321
+ " (#{GuardrailsTelemetry::Reason::QUEUE_FULL})"
322
+ end
323
+ end
324
+ if event_type == :snapshot
325
+ guardrails_telemetry.dropped(
326
+ reason: GuardrailsTelemetry::Reason::QUEUE_FULL,
327
+ event_type: guardrails_event_type,
328
+ )
284
329
  end
285
330
  else
286
331
  if event_type == :status && probe
@@ -289,7 +334,11 @@ module Datadog
289
334
  else
290
335
  logger.trace { "di: #{self.class.name}: queueing #{event_type} event" }
291
336
  end
292
- queue << event
337
+ queue << if event_type == :snapshot
338
+ SnapshotEvent.new(payload: event, event_type: guardrails_event_type)
339
+ else
340
+ event
341
+ end
293
342
  end
294
343
  end
295
344
 
@@ -307,6 +356,7 @@ module Datadog
307
356
  # Worker could be not running if the process forked - check and
308
357
  # start it again in this case.
309
358
  start
359
+ nil
310
360
  ensure
311
361
  Datadog::DI.leave_probe unless was_in_probe
312
362
  end