datadog 2.7.1 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
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