datadog 2.7.1 → 2.9.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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +69 -1
  3. data/ext/datadog_profiling_native_extension/clock_id.h +2 -2
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +64 -54
  5. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
  7. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +16 -16
  8. data/ext/datadog_profiling_native_extension/collectors_stack.c +7 -7
  9. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +259 -132
  10. data/ext/datadog_profiling_native_extension/extconf.rb +0 -8
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +11 -89
  12. data/ext/datadog_profiling_native_extension/heap_recorder.h +1 -1
  13. data/ext/datadog_profiling_native_extension/http_transport.c +4 -4
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +4 -1
  15. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -1
  16. data/ext/datadog_profiling_native_extension/profiling.c +10 -8
  17. data/ext/datadog_profiling_native_extension/ruby_helpers.c +8 -8
  18. data/ext/datadog_profiling_native_extension/stack_recorder.c +54 -88
  19. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -1
  20. data/ext/datadog_profiling_native_extension/time_helpers.h +1 -1
  21. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
  22. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
  23. data/ext/libdatadog_api/crashtracker.c +3 -0
  24. data/ext/libdatadog_extconf_helpers.rb +1 -1
  25. data/lib/datadog/appsec/assets/waf_rules/recommended.json +355 -157
  26. data/lib/datadog/appsec/assets/waf_rules/strict.json +62 -32
  27. data/lib/datadog/appsec/component.rb +1 -8
  28. data/lib/datadog/appsec/context.rb +54 -0
  29. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +73 -0
  30. data/lib/datadog/appsec/contrib/active_record/integration.rb +41 -0
  31. data/lib/datadog/appsec/contrib/active_record/patcher.rb +53 -0
  32. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +6 -6
  33. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +4 -4
  34. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +19 -28
  35. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +5 -5
  36. data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
  37. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +64 -96
  38. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +10 -10
  39. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +5 -5
  40. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +6 -6
  41. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
  42. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -49
  43. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +21 -32
  44. data/lib/datadog/appsec/contrib/rails/patcher.rb +1 -1
  45. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +6 -6
  46. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +41 -63
  47. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +2 -2
  48. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +5 -5
  49. data/lib/datadog/appsec/event.rb +6 -6
  50. data/lib/datadog/appsec/ext.rb +3 -1
  51. data/lib/datadog/appsec/monitor/gateway/watcher.rb +22 -32
  52. data/lib/datadog/appsec/monitor/reactive/set_user.rb +5 -5
  53. data/lib/datadog/appsec/processor/context.rb +2 -2
  54. data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
  55. data/lib/datadog/appsec/remote.rb +1 -3
  56. data/lib/datadog/appsec/response.rb +7 -11
  57. data/lib/datadog/appsec.rb +6 -5
  58. data/lib/datadog/auto_instrument.rb +3 -0
  59. data/lib/datadog/core/configuration/agent_settings_resolver.rb +39 -11
  60. data/lib/datadog/core/configuration/components.rb +20 -2
  61. data/lib/datadog/core/configuration/settings.rb +10 -0
  62. data/lib/datadog/core/configuration.rb +10 -2
  63. data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
  64. data/lib/datadog/core/crashtracking/component.rb +1 -3
  65. data/lib/datadog/core/remote/client/capabilities.rb +6 -0
  66. data/lib/datadog/core/remote/client.rb +65 -59
  67. data/lib/datadog/core/telemetry/component.rb +9 -3
  68. data/lib/datadog/core/telemetry/event.rb +87 -3
  69. data/lib/datadog/core/telemetry/ext.rb +1 -0
  70. data/lib/datadog/core/telemetry/logging.rb +2 -2
  71. data/lib/datadog/core/telemetry/metric.rb +22 -0
  72. data/lib/datadog/core/telemetry/worker.rb +33 -0
  73. data/lib/datadog/di/base.rb +115 -0
  74. data/lib/datadog/di/code_tracker.rb +11 -7
  75. data/lib/datadog/di/component.rb +21 -11
  76. data/lib/datadog/di/configuration/settings.rb +11 -1
  77. data/lib/datadog/di/contrib/active_record.rb +1 -0
  78. data/lib/datadog/di/contrib/railtie.rb +15 -0
  79. data/lib/datadog/di/contrib.rb +26 -0
  80. data/lib/datadog/di/error.rb +5 -0
  81. data/lib/datadog/di/instrumenter.rb +111 -20
  82. data/lib/datadog/di/preload.rb +18 -0
  83. data/lib/datadog/di/probe.rb +11 -1
  84. data/lib/datadog/di/probe_builder.rb +1 -0
  85. data/lib/datadog/di/probe_manager.rb +8 -5
  86. data/lib/datadog/di/probe_notification_builder.rb +27 -7
  87. data/lib/datadog/di/probe_notifier_worker.rb +5 -6
  88. data/lib/datadog/di/remote.rb +124 -0
  89. data/lib/datadog/di/serializer.rb +14 -7
  90. data/lib/datadog/di/transport.rb +3 -5
  91. data/lib/datadog/di/utils.rb +7 -0
  92. data/lib/datadog/di.rb +23 -62
  93. data/lib/datadog/kit/appsec/events.rb +3 -3
  94. data/lib/datadog/kit/identity.rb +4 -4
  95. data/lib/datadog/profiling/component.rb +59 -69
  96. data/lib/datadog/profiling/http_transport.rb +1 -26
  97. data/lib/datadog/tracing/configuration/settings.rb +4 -8
  98. data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
  99. data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
  100. data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
  101. data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
  102. data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
  103. data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -2
  104. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +3 -1
  105. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
  106. data/lib/datadog/tracing/contrib/active_support/cache/redis.rb +16 -4
  107. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
  108. data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
  109. data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
  110. data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
  111. data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
  112. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +4 -0
  113. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +6 -1
  114. data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
  115. data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
  116. data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
  117. data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
  118. data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
  119. data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
  120. data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
  121. data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
  122. data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
  123. data/lib/datadog/tracing/span.rb +12 -4
  124. data/lib/datadog/tracing/span_event.rb +123 -3
  125. data/lib/datadog/tracing/span_operation.rb +6 -0
  126. data/lib/datadog/tracing/transport/serializable_trace.rb +24 -6
  127. data/lib/datadog/version.rb +2 -2
  128. data/lib/datadog.rb +3 -0
  129. metadata +30 -17
  130. data/lib/datadog/appsec/processor/actions.rb +0 -49
  131. data/lib/datadog/appsec/reactive/operation.rb +0 -68
  132. data/lib/datadog/appsec/scope.rb +0 -58
  133. data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
@@ -32,7 +32,7 @@ module Datadog
32
32
  install_pending_method_probes(tp.self)
33
33
  rescue => exc
34
34
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
35
- logger.warn("Unhandled exception in definition trace point: #{exc.class}: #{exc}")
35
+ logger.debug { "di: unhandled exception in definition trace point: #{exc.class}: #{exc}" }
36
36
  telemetry&.report(exc, description: "Unhandled exception in definition trace point")
37
37
  # TODO test this path
38
38
  end
@@ -120,7 +120,7 @@ module Datadog
120
120
  # In "propagate all exceptions" mode we will try to instrument again.
121
121
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
122
122
 
123
- logger.warn("Error processing probe configuration: #{exc.class}: #{exc}")
123
+ logger.debug { "di: error processing probe configuration: #{exc.class}: #{exc}" }
124
124
  telemetry&.report(exc, description: "Error processing probe configuration")
125
125
  # TODO report probe as failed to agent since we won't attempt to
126
126
  # install it again.
@@ -160,8 +160,8 @@ module Datadog
160
160
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
161
161
  # Silence all exceptions?
162
162
  # TODO should we propagate here and rescue upstream?
163
- logger.warn("Error removing probe #{probe.id}: #{exc.class}: #{exc}")
164
- telemetry&.report(exc, description: "Error removing probe #{probe.id}")
163
+ logger.debug { "di: error removing probe #{probe.id}: #{exc.class}: #{exc}" }
164
+ telemetry&.report(exc, description: "Error removing probe")
165
165
  end
166
166
  end
167
167
  end
@@ -190,7 +190,7 @@ module Datadog
190
190
  rescue => exc
191
191
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
192
192
 
193
- logger.warn("Error installing probe after class is defined: #{exc.class}: #{exc}")
193
+ logger.debug { "di: error installing probe after class is defined: #{exc.class}: #{exc}" }
194
194
  telemetry&.report(exc, description: "Error installing probe after class is defined")
195
195
  end
196
196
  end
@@ -206,6 +206,9 @@ module Datadog
206
206
  # point, which is invoked for each required or loaded file
207
207
  # (and also for eval'd code, but those invocations are filtered out).
208
208
  def install_pending_line_probes(path)
209
+ if path.nil?
210
+ raise ArgumentError, "path must not be nil"
211
+ end
209
212
  @lock.synchronize do
210
213
  @pending_probes.values.each do |probe|
211
214
  if probe.line?
@@ -65,14 +65,18 @@ module Datadog
65
65
  arguments: if serialized_entry_args
66
66
  serialized_entry_args
67
67
  else
68
- (args || kwargs) && serializer.serialize_args(args, kwargs)
68
+ (args || kwargs) && serializer.serialize_args(args, kwargs,
69
+ depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
70
+ attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
69
71
  end,
70
72
  throwable: nil,
71
73
  # standard:enable all
72
74
  },
73
75
  return: {
74
76
  arguments: {
75
- "@return": serializer.serialize_value(rv),
77
+ "@return": serializer.serialize_value(rv,
78
+ depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
79
+ attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count),
76
80
  },
77
81
  throwable: nil,
78
82
  },
@@ -80,7 +84,9 @@ module Datadog
80
84
  elsif probe.line?
81
85
  {
82
86
  lines: snapshot && {
83
- probe.line_no => {locals: serializer.serialize_vars(snapshot)},
87
+ probe.line_no => {locals: serializer.serialize_vars(snapshot,
88
+ depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
89
+ attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count,)},
84
90
  },
85
91
  }
86
92
  end
@@ -121,7 +127,7 @@ module Datadog
121
127
  },
122
128
  # In python tracer duration is under debugger.snapshot,
123
129
  # but UI appears to expect it here at top level.
124
- duration: duration ? (duration * 10**9).to_i : nil,
130
+ duration: duration ? (duration * 10**9).to_i : 0,
125
131
  host: nil,
126
132
  logger: {
127
133
  name: probe.file,
@@ -135,15 +141,17 @@ module Datadog
135
141
  version: 2,
136
142
  },
137
143
  # TODO add tests that the trace/span id is correctly propagated
138
- "dd.trace_id": Datadog::Tracing.active_trace&.id,
139
- "dd.span_id": Datadog::Tracing.active_span&.id,
144
+ "dd.trace_id": active_trace&.id&.to_s,
145
+ "dd.span_id": active_span&.id&.to_s,
140
146
  ddsource: 'dd_debugger',
141
147
  message: probe.template && evaluate_template(probe.template,
142
- duration: duration ? duration * 1000 : nil),
148
+ duration: duration ? duration * 1000 : 0),
143
149
  timestamp: timestamp,
144
150
  }
145
151
  end
146
152
 
153
+ private
154
+
147
155
  def build_status(probe, message:, status:)
148
156
  {
149
157
  service: settings.service,
@@ -194,6 +202,18 @@ module Datadog
194
202
  map[name] = value
195
203
  end
196
204
  end
205
+
206
+ def active_trace
207
+ if defined?(Datadog::Tracing)
208
+ Datadog::Tracing.active_trace
209
+ end
210
+ end
211
+
212
+ def active_span
213
+ if defined?(Datadog::Tracing)
214
+ Datadog::Tracing.active_span
215
+ end
216
+ end
197
217
  end
198
218
  end
199
219
  end
@@ -77,7 +77,7 @@ module Datadog
77
77
  rescue => exc
78
78
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
79
79
 
80
- logger.warn("Error in probe notifier worker: #{exc.class}: #{exc} (at #{exc.backtrace.first})")
80
+ logger.debug { "di: error in probe notifier worker: #{exc.class}: #{exc} (at #{exc.backtrace.first})" }
81
81
  telemetry&.report(exc, description: "Error in probe notifier worker")
82
82
  end
83
83
  @lock.synchronize do
@@ -183,9 +183,8 @@ module Datadog
183
183
  define_method("add_#{event_type}") do |event|
184
184
  @lock.synchronize do
185
185
  queue = send("#{event_type}_queue")
186
- # TODO determine a suitable limit via testing/benchmarking
187
- if queue.length > 100
188
- logger.warn("#{self.class.name}: dropping #{event_type} because queue is full")
186
+ if queue.length > settings.dynamic_instrumentation.internal.snapshot_queue_capacity
187
+ logger.debug { "di: #{self.class.name}: dropping #{event_type} because queue is full" }
189
188
  else
190
189
  queue << event
191
190
  end
@@ -242,7 +241,7 @@ module Datadog
242
241
  end
243
242
  rescue => exc
244
243
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
245
- logger.warn("failed to send #{event_name}: #{exc.class}: #{exc} (at #{exc.backtrace.first})")
244
+ logger.debug { "di: failed to send #{event_name}: #{exc.class}: #{exc} (at #{exc.backtrace.first})" }
246
245
  # Should we report this error to telemetry? Most likely failure
247
246
  # to send is due to a network issue, and trying to send a
248
247
  # telemetry message would also fail.
@@ -253,7 +252,7 @@ module Datadog
253
252
  # Normally the queue should only be consumed in this method,
254
253
  # however if anyone consumes it elsewhere we don't want to block
255
254
  # while consuming it here. Rescue ThreadError and return.
256
- logger.warn("Unexpected #{event_name} queue underflow - consumed elsewhere?")
255
+ logger.debug { "di: unexpected #{event_name} queue underflow - consumed elsewhere?" }
257
256
  telemetry&.report(exc, description: "Unexpected #{event_name} queue underflow")
258
257
  ensure
259
258
  @lock.synchronize do
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ # Provides an interface expected by the core Remote subsystem to
6
+ # receive DI-specific remote configuration.
7
+ #
8
+ # In order to apply (i.e., act on) the configuration, we need the
9
+ # state stored under DI Component. Thus, this module forwards actual
10
+ # configuration application to the ProbeManager associated with the
11
+ # global DI Component.
12
+ #
13
+ # @api private
14
+ module Remote
15
+ class ReadError < StandardError; end
16
+
17
+ class << self
18
+ PRODUCT = 'LIVE_DEBUGGING'
19
+
20
+ def products
21
+ [PRODUCT]
22
+ end
23
+
24
+ def capabilities
25
+ []
26
+ end
27
+
28
+ def receivers(telemetry)
29
+ receiver do |repository, _changes|
30
+ # DEV: Filter our by product. Given it will be very common
31
+ # DEV: we can filter this out before we receive the data in this method.
32
+ # DEV: Apply this refactor to AppSec as well if implemented.
33
+
34
+ component = DI.component
35
+ # We should always have a non-nil DI component here, because we
36
+ # only add DI product to remote config request if DI is enabled.
37
+ # Ideally, we should be injected with the DI component here
38
+ # rather than having to retrieve it from global state.
39
+ # If the component is nil for some reason, we also don't have a
40
+ # logger instance to report the issue.
41
+ if component
42
+
43
+ probe_manager = component.probe_manager
44
+
45
+ current_probe_ids = {}
46
+ repository.contents.each do |content|
47
+ case content.path.product
48
+ when PRODUCT
49
+ begin
50
+ probe_spec = parse_content(content)
51
+ probe = ProbeBuilder.build_from_remote_config(probe_spec)
52
+ payload = component.probe_notification_builder.build_received(probe)
53
+ component.probe_notifier_worker.add_status(payload)
54
+ component.logger.debug { "di: received probe from RC: #{probe.type} #{probe.location}" }
55
+
56
+ begin
57
+ # TODO test exception capture
58
+ probe_manager.add_probe(probe)
59
+ content.applied
60
+ rescue => exc
61
+ raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
62
+
63
+ component.logger.debug { "di: unhandled exception adding probe in DI remote receiver: #{exc.class}: #{exc}" }
64
+ component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
65
+
66
+ # If a probe fails to install, we will mark the content
67
+ # as errored. On subsequent remote configuration application
68
+ # attemps, probe manager will raise the "previously errored"
69
+ # exception and we'll rescue it here, again marking the
70
+ # content as errored but with a somewhat different exception
71
+ # message.
72
+ # TODO stack trace must be redacted or not sent at all
73
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}: #{Array(exc.backtrace).join("\n")}")
74
+ end
75
+
76
+ # Important: even if processing fails for this probe config,
77
+ # we need to note it as being current so that we do not
78
+ # try to remove instrumentation that is still supposed to be
79
+ # active.
80
+ current_probe_ids[probe_spec.fetch('id')] = true
81
+ rescue => exc
82
+ raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
83
+
84
+ component.logger.debug { "di: unhandled exception handling probe in DI remote receiver: #{exc.class}: #{exc}" }
85
+ component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
86
+
87
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}: #{Array(exc.backtrace).join("\n")}")
88
+ end
89
+ end
90
+ end
91
+
92
+ begin
93
+ # TODO test exception capture
94
+ probe_manager.remove_other_probes(current_probe_ids.keys)
95
+ rescue => exc
96
+ raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
97
+
98
+ component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
99
+ component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ def receiver(products = [PRODUCT], &block)
106
+ matcher = Core::Remote::Dispatcher::Matcher::Product.new(products)
107
+ [Core::Remote::Dispatcher::Receiver.new(matcher, &block)]
108
+ end
109
+
110
+ private
111
+
112
+ def parse_content(content)
113
+ data = content.data.read
114
+
115
+ content.data.rewind
116
+
117
+ raise ReadError, 'EOF reached' if data.nil?
118
+
119
+ JSON.parse(data)
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -82,7 +82,9 @@ module Datadog
82
82
  # between positional and keyword arguments. We convert positional
83
83
  # arguments to keyword arguments ("arg1", "arg2", ...) and ensure
84
84
  # the positional arguments are listed first.
85
- def serialize_args(args, kwargs)
85
+ def serialize_args(args, kwargs,
86
+ depth: settings.dynamic_instrumentation.max_capture_depth,
87
+ attribute_count: settings.dynamic_instrumentation.max_capture_attribute_count)
86
88
  counter = 0
87
89
  combined = args.each_with_object({}) do |value, c|
88
90
  counter += 1
@@ -90,16 +92,18 @@ module Datadog
90
92
  # kwargs when they are merged below.
91
93
  c[:"arg#{counter}"] = value
92
94
  end.update(kwargs)
93
- serialize_vars(combined)
95
+ serialize_vars(combined, depth: depth, attribute_count: attribute_count)
94
96
  end
95
97
 
96
98
  # Serializes variables captured by a line probe.
97
99
  #
98
100
  # These are normally local variables that exist on a particular line
99
101
  # of executed code.
100
- def serialize_vars(vars)
102
+ def serialize_vars(vars,
103
+ depth: settings.dynamic_instrumentation.max_capture_depth,
104
+ attribute_count: settings.dynamic_instrumentation.max_capture_attribute_count)
101
105
  vars.each_with_object({}) do |(k, v), agg|
102
- agg[k] = serialize_value(v, name: k)
106
+ agg[k] = serialize_value(v, name: k, depth: depth, attribute_count: attribute_count)
103
107
  end
104
108
  end
105
109
 
@@ -115,7 +119,11 @@ module Datadog
115
119
  # (integers, strings, arrays, hashes).
116
120
  #
117
121
  # Respects string length, collection size and traversal depth limits.
118
- def serialize_value(value, name: nil, depth: settings.dynamic_instrumentation.max_capture_depth, type: nil)
122
+ def serialize_value(value, name: nil,
123
+ depth: settings.dynamic_instrumentation.max_capture_depth,
124
+ attribute_count: nil,
125
+ type: nil)
126
+ attribute_count ||= settings.dynamic_instrumentation.max_capture_attribute_count
119
127
  cls = type || value.class
120
128
  begin
121
129
  if redactor.redact_type?(value)
@@ -203,7 +211,6 @@ module Datadog
203
211
  serialized.update(notCapturedReason: "depth")
204
212
  else
205
213
  fields = {}
206
- max = settings.dynamic_instrumentation.max_capture_attribute_count
207
214
  cur = 0
208
215
 
209
216
  # MRI and JRuby 9.4.5+ preserve instance variable definition
@@ -229,7 +236,7 @@ module Datadog
229
236
  ivars = value.instance_variables
230
237
 
231
238
  ivars.each do |ivar|
232
- if cur >= max
239
+ if cur >= attribute_count
233
240
  serialized.update(notCapturedReason: "fieldCount", fields: fields)
234
241
  break
235
242
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ostruct'
4
3
  require_relative 'error'
4
+ require_relative '../core/transport/http/adapters/net'
5
5
 
6
6
  module Datadog
7
7
  module DI
@@ -45,7 +45,7 @@ module Datadog
45
45
 
46
46
  def send_input(payload)
47
47
  send_request('Probe snapshot submission',
48
- path: INPUT_PATH, body: payload.to_s,
48
+ path: INPUT_PATH, body: payload.to_json,
49
49
  headers: {'content-type' => 'application/json'},)
50
50
  end
51
51
 
@@ -60,9 +60,7 @@ module Datadog
60
60
  attr_reader :client
61
61
 
62
62
  def send_request(desc, **options)
63
- # steep:ignore:start
64
- env = OpenStruct.new(**options)
65
- # steep:ignore:end
63
+ env = Core::Transport::HTTP::Env.new(nil, options)
66
64
  response = client.post(env)
67
65
  unless response.ok?
68
66
  raise Error::AgentCommunicationError, "#{desc} failed: #{response.code}: #{response.payload}"
@@ -12,6 +12,13 @@ module Datadog
12
12
  # If suffix is not an absolute path, the path matches if its suffix is
13
13
  # the provided suffix, at a path component boundary.
14
14
  module_function def path_matches_suffix?(path, suffix)
15
+ if path.nil?
16
+ raise ArgumentError, "nil path passed"
17
+ end
18
+ if suffix.nil?
19
+ raise ArgumentError, "nil suffix passed"
20
+ end
21
+
15
22
  if suffix.start_with?('/')
16
23
  path == suffix
17
24
  else
data/lib/datadog/di.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'di/base'
3
4
  require_relative 'di/error'
4
5
  require_relative 'di/code_tracker'
5
6
  require_relative 'di/component'
@@ -12,22 +13,11 @@ require_relative 'di/probe_manager'
12
13
  require_relative 'di/probe_notification_builder'
13
14
  require_relative 'di/probe_notifier_worker'
14
15
  require_relative 'di/redactor'
16
+ require_relative 'di/remote'
15
17
  require_relative 'di/serializer'
16
18
  require_relative 'di/transport'
17
19
  require_relative 'di/utils'
18
20
 
19
- if defined?(ActiveRecord::Base)
20
- # The third-party library integrations need to be loaded after the
21
- # third-party libraries are loaded. Tracing and appsec use Railtie
22
- # to delay integrations until all of the application's dependencies
23
- # are loaded, when running under Rails. We should do the same here in
24
- # principle, however DI currently only has an ActiveRecord integration
25
- # and AR should be loaded before any application code is loaded, being
26
- # part of Rails, therefore for now we should be OK to just require the
27
- # AR integration from here.
28
- require_relative 'di/contrib/active_record'
29
- end
30
-
31
21
  module Datadog
32
22
  # Namespace for Datadog dynamic instrumentation.
33
23
  #
@@ -43,63 +33,34 @@ module Datadog
43
33
  Extensions.activate!
44
34
 
45
35
  class << self
46
- attr_reader :code_tracker
47
36
 
48
- # Activates code tracking. Normally this method should be called
49
- # when the application starts. If instrumenting third-party code,
50
- # code tracking needs to be enabled before the third-party libraries
51
- # are loaded. If you definitely will not be instrumenting
52
- # third-party libraries, activating tracking after third-party libraries
53
- # have been loaded may improve lookup performance.
37
+ # This method is called from DI Remote handler to issue DI operations
38
+ # to the probe manager (add or remove probes).
54
39
  #
55
- # TODO test that activating tracker multiple times preserves
56
- # existing mappings in the registry
57
- def activate_tracking!
58
- (@code_tracker ||= CodeTracker.new).start
59
- end
60
-
61
- # Deactivates code tracking. In normal usage of DI this method should
62
- # never be called, however it is used by DI's test suite to reset
63
- # state for individual tests.
40
+ # When DI Remote is executing, Datadog.components should be initialized
41
+ # and we should be able to reference it to get to the DI component.
64
42
  #
65
- # Note that deactivating tracking clears out the registry, losing
66
- # the ability to look up files that have been loaded into the process
67
- # already.
68
- def deactivate_tracking!
69
- code_tracker&.stop
70
- end
71
-
72
- # Returns whether code tracking is available.
73
- # This method should be used instead of querying #code_tracker
74
- # because the latter one may be nil.
75
- def code_tracking_active?
76
- code_tracker&.active? || false
77
- end
78
-
43
+ # Given that we need the current_component anyway for code tracker,
44
+ # perhaps we should delete the +component+ method and just use
45
+ # +current_component+ in all cases.
79
46
  def component
80
- # TODO uncomment when remote is merged
81
- #Datadog.send(:components).dynamic_instrumentation
47
+ Datadog.send(:components).dynamic_instrumentation
82
48
  end
83
49
  end
84
50
  end
85
51
  end
86
52
 
87
- =begin not yet enabled
88
- # :script_compiled trace point was added in Ruby 2.6.
89
- if RUBY_VERSION >= '2.6'
90
- begin
91
- # Activate code tracking by default because line trace points will not work
92
- # without it.
93
- Datadog::DI.activate_tracking!
94
- rescue => exc
95
- if defined?(Datadog.logger)
96
- Datadog.logger.warn("Failed to activate code tracking for DI: #{exc.class}: #{exc}")
97
- else
98
- # We do not have Datadog logger potentially because DI code tracker is
99
- # being loaded early in application boot process and the rest of datadog
100
- # wasn't loaded yet. Output to standard error.
101
- warn("Failed to activate code tracking for DI: #{exc.class}: #{exc}")
102
- end
103
- end
53
+ if %w(1 true).include?(ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore
54
+ # For initial release of Dynamic Instrumentation, activate code tracking
55
+ # only if DI is explicitly requested in the environment.
56
+ # Code tracking is required for line probes to work; see the comments
57
+ # above for the implementation of the method.
58
+ #
59
+ # If DI is enabled programmatically, the application can (and must,
60
+ # for line probes to work) activate tracking in an initializer.
61
+ Datadog::DI.activate_tracking
104
62
  end
105
- =end
63
+
64
+ require_relative 'di/contrib'
65
+
66
+ Datadog::DI::Contrib.load_now_or_later
@@ -136,9 +136,9 @@ module Datadog
136
136
  private
137
137
 
138
138
  def set_trace_and_span_context(method, trace = nil, span = nil)
139
- if (appsec_scope = Datadog::AppSec.active_scope)
140
- trace = appsec_scope.trace
141
- span = appsec_scope.service_entry_span
139
+ if (appsec_context = Datadog::AppSec.active_context)
140
+ trace = appsec_context.trace
141
+ span = appsec_context.span
142
142
  end
143
143
 
144
144
  trace ||= Datadog::Tracing.active_trace
@@ -66,7 +66,7 @@ module Datadog
66
66
  active_span.set_tag("usr.#{k}", v) unless v.nil?
67
67
  end
68
68
 
69
- if Datadog::AppSec.active_scope
69
+ if Datadog::AppSec.active_context
70
70
  user = ::Datadog::AppSec::Instrumentation::Gateway::User.new(id)
71
71
  ::Datadog::AppSec::Instrumentation.gateway.push('identity.set_user', user)
72
72
  end
@@ -78,9 +78,9 @@ module Datadog
78
78
  private
79
79
 
80
80
  def set_trace_and_span_context(method, trace = nil, span = nil)
81
- if (appsec_scope = Datadog::AppSec.active_scope)
82
- trace = appsec_scope.trace
83
- span = appsec_scope.service_entry_span
81
+ if (appsec_context = Datadog::AppSec.active_context)
82
+ trace = appsec_context.trace
83
+ span = appsec_context.span
84
84
  end
85
85
 
86
86
  trace ||= Datadog::Tracing.active_trace