datadog 2.10.0 → 2.12.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 (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -1
  3. data/ext/datadog_profiling_native_extension/collectors_stack.c +3 -3
  4. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +44 -1
  5. data/ext/datadog_profiling_native_extension/extconf.rb +4 -0
  6. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +2 -0
  7. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +0 -8
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  9. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +56 -0
  10. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +7 -0
  11. data/ext/datadog_profiling_native_extension/profiling.c +7 -0
  12. data/ext/libdatadog_api/crashtracker.c +4 -4
  13. data/ext/libdatadog_extconf_helpers.rb +1 -1
  14. data/lib/datadog/appsec/configuration/settings.rb +64 -11
  15. data/lib/datadog/appsec/contrib/active_record/patcher.rb +0 -3
  16. data/lib/datadog/appsec/contrib/devise/configuration.rb +76 -0
  17. data/lib/datadog/appsec/contrib/devise/event.rb +4 -7
  18. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +16 -21
  19. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +8 -15
  20. data/lib/datadog/appsec/contrib/devise/patcher/rememberable_patch.rb +1 -1
  21. data/lib/datadog/appsec/contrib/devise/patcher.rb +0 -3
  22. data/lib/datadog/appsec/contrib/devise/tracking.rb +1 -1
  23. data/lib/datadog/appsec/contrib/excon/integration.rb +41 -0
  24. data/lib/datadog/appsec/contrib/excon/patcher.rb +28 -0
  25. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +43 -0
  26. data/lib/datadog/appsec/contrib/faraday/connection_patch.rb +22 -0
  27. data/lib/datadog/appsec/contrib/faraday/integration.rb +42 -0
  28. data/lib/datadog/appsec/contrib/faraday/patcher.rb +53 -0
  29. data/lib/datadog/appsec/contrib/faraday/rack_builder_patch.rb +22 -0
  30. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +42 -0
  31. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +10 -12
  32. data/lib/datadog/appsec/contrib/graphql/patcher.rb +0 -3
  33. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +65 -73
  34. data/lib/datadog/appsec/contrib/rack/patcher.rb +0 -3
  35. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +20 -25
  36. data/lib/datadog/appsec/contrib/rails/patcher.rb +0 -3
  37. data/lib/datadog/appsec/contrib/rest_client/integration.rb +45 -0
  38. data/lib/datadog/appsec/contrib/rest_client/patcher.rb +28 -0
  39. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +39 -0
  40. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +38 -49
  41. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +0 -3
  42. data/lib/datadog/appsec/monitor/gateway/watcher.rb +19 -25
  43. data/lib/datadog/appsec/remote.rb +4 -0
  44. data/lib/datadog/appsec.rb +3 -0
  45. data/lib/datadog/core/configuration/components.rb +8 -2
  46. data/lib/datadog/core/configuration/ext.rb +1 -1
  47. data/lib/datadog/core/configuration/option_definition.rb +2 -0
  48. data/lib/datadog/core/configuration/settings.rb +22 -6
  49. data/lib/datadog/core/encoding.rb +16 -0
  50. data/lib/datadog/core/environment/agent_info.rb +77 -0
  51. data/lib/datadog/core/remote/component.rb +11 -9
  52. data/lib/datadog/core/remote/transport/http/api.rb +13 -18
  53. data/lib/datadog/core/remote/transport/http/config.rb +0 -18
  54. data/lib/datadog/core/remote/transport/http/negotiation.rb +1 -18
  55. data/lib/datadog/core/remote/transport/http.rb +7 -12
  56. data/lib/datadog/core/remote/transport/negotiation.rb +13 -1
  57. data/lib/datadog/core/remote/worker.rb +10 -7
  58. data/lib/datadog/core/telemetry/component.rb +5 -1
  59. data/lib/datadog/core/telemetry/event.rb +5 -0
  60. data/lib/datadog/core/telemetry/worker.rb +9 -5
  61. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +1 -1
  62. data/lib/datadog/{tracing → core}/transport/http/api/instance.rb +1 -1
  63. data/lib/datadog/{tracing → core}/transport/http/api/spec.rb +1 -1
  64. data/lib/datadog/{tracing → core}/transport/http/builder.rb +37 -17
  65. data/lib/datadog/core/transport/response.rb +4 -0
  66. data/lib/datadog/di/code_tracker.rb +15 -8
  67. data/lib/datadog/di/component.rb +2 -3
  68. data/lib/datadog/di/configuration/settings.rb +14 -0
  69. data/lib/datadog/di/contrib.rb +2 -0
  70. data/lib/datadog/di/logger.rb +30 -0
  71. data/lib/datadog/di/probe.rb +3 -6
  72. data/lib/datadog/di/probe_manager.rb +5 -2
  73. data/lib/datadog/di/probe_notifier_worker.rb +35 -8
  74. data/lib/datadog/di/remote.rb +3 -3
  75. data/lib/datadog/di/transport/diagnostics.rb +61 -0
  76. data/lib/datadog/di/transport/http/api.rb +52 -0
  77. data/lib/datadog/di/transport/http/client.rb +46 -0
  78. data/lib/datadog/di/transport/http/diagnostics.rb +92 -0
  79. data/lib/datadog/di/transport/http/input.rb +94 -0
  80. data/lib/datadog/di/transport/http.rb +119 -0
  81. data/lib/datadog/di/transport/input.rb +61 -0
  82. data/lib/datadog/di/utils.rb +91 -0
  83. data/lib/datadog/di.rb +5 -1
  84. data/lib/datadog/profiling/component.rb +2 -8
  85. data/lib/datadog/profiling/load_native_extension.rb +1 -33
  86. data/lib/datadog/tracing/component.rb +1 -0
  87. data/lib/datadog/tracing/configuration/ext.rb +1 -0
  88. data/lib/datadog/tracing/contrib/extensions.rb +14 -0
  89. data/lib/datadog/tracing/contrib/graphql/configuration/error_extension_env_parser.rb +21 -0
  90. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +11 -0
  91. data/lib/datadog/tracing/contrib/graphql/ext.rb +5 -0
  92. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +102 -11
  93. data/lib/datadog/tracing/contrib/rack/header_collection.rb +11 -1
  94. data/lib/datadog/tracing/contrib/rack/middlewares.rb +1 -1
  95. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +6 -1
  96. data/lib/datadog/tracing/sync_writer.rb +5 -2
  97. data/lib/datadog/tracing/tracer.rb +10 -7
  98. data/lib/datadog/tracing/transport/http/api.rb +11 -2
  99. data/lib/datadog/tracing/transport/http/traces.rb +0 -3
  100. data/lib/datadog/tracing/transport/http.rb +12 -7
  101. data/lib/datadog/tracing/transport/serializable_trace.rb +8 -4
  102. data/lib/datadog/tracing/transport/traces.rb +25 -8
  103. data/lib/datadog/tracing/workers/trace_writer.rb +4 -1
  104. data/lib/datadog/tracing/workers.rb +5 -4
  105. data/lib/datadog/tracing/writer.rb +6 -2
  106. data/lib/datadog/version.rb +1 -1
  107. metadata +33 -29
  108. data/ext/datadog_profiling_loader/datadog_profiling_loader.c +0 -142
  109. data/ext/datadog_profiling_loader/extconf.rb +0 -60
  110. data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +0 -46
  111. data/lib/datadog/appsec/contrib/patcher.rb +0 -12
  112. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +0 -69
  113. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +0 -47
  114. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +0 -53
  115. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +0 -53
  116. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +0 -48
  117. data/lib/datadog/appsec/monitor/reactive/set_user.rb +0 -45
  118. data/lib/datadog/appsec/reactive/address_hash.rb +0 -22
  119. data/lib/datadog/appsec/reactive/engine.rb +0 -47
  120. data/lib/datadog/appsec/reactive/subscriber.rb +0 -19
  121. data/lib/datadog/core/remote/transport/http/api/instance.rb +0 -39
  122. data/lib/datadog/core/remote/transport/http/api/spec.rb +0 -21
  123. data/lib/datadog/core/remote/transport/http/builder.rb +0 -219
  124. data/lib/datadog/di/transport.rb +0 -79
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../../core/configuration/agent_settings_resolver'
4
- require_relative '../../../core/transport/http/adapters/registry'
5
- require_relative '../../../core/transport/http/api/map'
6
- require_relative 'api/instance'
7
- require_relative 'client'
3
+ require_relative '../../configuration/agent_settings_resolver'
4
+ require_relative 'adapters/registry'
5
+ require_relative 'api/map'
8
6
 
9
7
  module Datadog
10
- module Tracing
8
+ module Core
11
9
  module Transport
12
10
  module HTTP
13
11
  # Builds new instances of Transport::HTTP::Client
@@ -15,13 +13,14 @@ module Datadog
15
13
  REGISTRY = Datadog::Core::Transport::HTTP::Adapters::Registry.new
16
14
 
17
15
  attr_reader \
16
+ :api_instance_class,
18
17
  :apis,
19
18
  :api_options,
20
19
  :default_adapter,
21
20
  :default_api,
22
21
  :default_headers
23
22
 
24
- def initialize
23
+ def initialize(api_instance_class:)
25
24
  # Global settings
26
25
  @default_adapter = nil
27
26
  @default_headers = {}
@@ -33,6 +32,8 @@ module Datadog
33
32
  # API settings
34
33
  @api_options = {}
35
34
 
35
+ @api_instance_class = api_instance_class
36
+
36
37
  yield(self) if block_given?
37
38
  end
38
39
 
@@ -82,11 +83,10 @@ module Datadog
82
83
  @default_api = key
83
84
  end
84
85
 
85
- def to_transport
86
+ def to_transport(klass)
86
87
  raise NoDefaultApiError if @default_api.nil?
87
88
 
88
- # DEV: Should not be specific to traces
89
- Transport::Traces::Transport.new(to_api_instances, @default_api)
89
+ klass.new(to_api_instances, @default_api)
90
90
  end
91
91
 
92
92
  def to_api_instances
@@ -117,28 +117,48 @@ module Datadog
117
117
  end
118
118
  end
119
119
 
120
- def api_instance_class
121
- API::Instance
122
- end
123
-
124
120
  # Raised when the API key does not match known APIs.
125
121
  class UnknownApiError < StandardError
122
+ attr_reader :key
123
+
126
124
  def initialize(key)
127
- super("Unknown transport API '#{key}'!")
125
+ super()
126
+
127
+ @key = key
128
+ end
129
+
130
+ def message
131
+ "Unknown transport API '#{key}'!"
128
132
  end
129
133
  end
130
134
 
131
135
  # Raised when the identifier cannot be matched to an adapter.
132
136
  class UnknownAdapterError < StandardError
137
+ attr_reader :type
138
+
133
139
  def initialize(type)
134
- super("Unknown transport adapter '#{type}'!")
140
+ super()
141
+
142
+ @type = type
143
+ end
144
+
145
+ def message
146
+ "Unknown transport adapter '#{type}'!"
135
147
  end
136
148
  end
137
149
 
138
150
  # Raised when an adapter cannot be resolved for an API instance.
139
151
  class NoAdapterForApiError < StandardError
152
+ attr_reader :key
153
+
140
154
  def initialize(key)
141
- super("No adapter resolved for transport API '#{key}'!")
155
+ super()
156
+
157
+ @key = key
158
+ end
159
+
160
+ def message
161
+ "No adapter resolved for transport API '#{key}'!"
142
162
  end
143
163
  end
144
164
 
@@ -55,6 +55,10 @@ module Datadog
55
55
  true
56
56
  end
57
57
 
58
+ def to_s
59
+ "#{super}, error_type:#{error.class} error:#{error}"
60
+ end
61
+
58
62
  def inspect
59
63
  "#{super}, error_type:#{error.class} error:#{error}"
60
64
  end
@@ -140,16 +140,23 @@ module Datadog
140
140
  exact = registry[suffix]
141
141
  return [suffix, exact] if exact
142
142
 
143
- inexact = []
144
- registry.each do |path, iseq|
145
- if Utils.path_matches_suffix?(path, suffix)
146
- inexact << [path, iseq]
143
+ suffix = suffix.dup
144
+ loop do
145
+ inexact = []
146
+ registry.each do |path, iseq|
147
+ if Utils.path_matches_suffix?(path, suffix)
148
+ inexact << [path, iseq]
149
+ end
147
150
  end
151
+ if inexact.length > 1
152
+ raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix"
153
+ end
154
+ if inexact.any?
155
+ return inexact.first
156
+ end
157
+ return nil unless suffix.include?('/')
158
+ suffix.sub!(%r{.*/+}, '')
148
159
  end
149
- if inexact.length > 1
150
- raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix"
151
- end
152
- inexact.first
153
160
  end
154
161
  end
155
162
 
@@ -74,14 +74,14 @@ module Datadog
74
74
  def initialize(settings, agent_settings, logger, code_tracker: nil, telemetry: nil)
75
75
  @settings = settings
76
76
  @agent_settings = agent_settings
77
+ logger = DI::Logger.new(settings, logger)
77
78
  @logger = logger
78
79
  @telemetry = telemetry
79
80
  @code_tracker = code_tracker
80
81
  @redactor = Redactor.new(settings)
81
82
  @serializer = Serializer.new(settings, redactor, telemetry: telemetry)
82
83
  @instrumenter = Instrumenter.new(settings, serializer, logger, code_tracker: code_tracker, telemetry: telemetry)
83
- @transport = Transport.new(agent_settings)
84
- @probe_notifier_worker = ProbeNotifierWorker.new(settings, transport, logger, telemetry: telemetry)
84
+ @probe_notifier_worker = ProbeNotifierWorker.new(settings, logger, agent_settings: agent_settings, telemetry: telemetry)
85
85
  @probe_notification_builder = ProbeNotificationBuilder.new(settings, serializer)
86
86
  @probe_manager = ProbeManager.new(settings, instrumenter, probe_notification_builder, probe_notifier_worker, logger, telemetry: telemetry)
87
87
  probe_notifier_worker.start
@@ -93,7 +93,6 @@ module Datadog
93
93
  attr_reader :telemetry
94
94
  attr_reader :code_tracker
95
95
  attr_reader :instrumenter
96
- attr_reader :transport
97
96
  attr_reader :probe_notifier_worker
98
97
  attr_reader :probe_notification_builder
99
98
  attr_reader :probe_manager
@@ -188,6 +188,20 @@ module Datadog
188
188
  o.type :bool
189
189
  o.default false
190
190
  end
191
+
192
+ # Enable logging of dynamic instrumentation activity.
193
+ # This is quite verbose.
194
+ option :trace_logging do |o|
195
+ o.type :bool
196
+ o.default false
197
+
198
+ # Use the same environment variable as the rest of
199
+ # dd-trace-rb logging for now. Could change to a
200
+ # dedicated environment variable in the future but
201
+ # will likely need a way to turn on remote config
202
+ # debugging (since DI uses RC for configuration).
203
+ o.env 'DD_TRACE_DEBUG'
204
+ end
191
205
  end
192
206
  end
193
207
  end
@@ -7,6 +7,7 @@ module Datadog
7
7
  module Contrib
8
8
  module_function def load_now_or_later
9
9
  if Datadog::Core::Contrib::Rails::Utils.railtie_supported?
10
+ Datadog.logger.debug('di: loading contrib/railtie')
10
11
  require_relative 'contrib/railtie'
11
12
  else
12
13
  load_now
@@ -18,6 +19,7 @@ module Datadog
18
19
  # dependencies are loaded (or potentially loaded).
19
20
  module_function def load_now
20
21
  if defined?(ActiveRecord::Base)
22
+ Datadog.logger.debug('di: loading contrib/active_record')
21
23
  require_relative 'contrib/active_record'
22
24
  end
23
25
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Datadog
6
+ module DI
7
+ # Logger facade to add the +trace+ method.
8
+ #
9
+ # @api private
10
+ class Logger
11
+ extend Forwardable # steep:ignore
12
+
13
+ def initialize(settings, target)
14
+ @settings = settings
15
+ @target = target
16
+ end
17
+
18
+ attr_reader :settings
19
+ attr_reader :target
20
+
21
+ def_delegators :target, :debug # steep:ignore
22
+
23
+ def trace(&block)
24
+ if settings.dynamic_instrumentation.internal.trace_logging
25
+ debug(&block)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -155,11 +155,8 @@ module Datadog
155
155
  # Returns whether the provided +path+ matches the user-designated
156
156
  # file (of a line probe).
157
157
  #
158
- # If file is an absolute path (i.e., it starts with a slash), the file
159
- # must be identical to path to match.
160
- #
161
- # If file is not an absolute path, the path matches if the file is its suffix,
162
- # at a path component boundary.
158
+ # Delegates to Utils.path_can_match_spec? which performs fuzzy
159
+ # matching. See the comments in utils.rb for details.
163
160
  def file_matches?(path)
164
161
  if path.nil?
165
162
  raise ArgumentError, "Cannot match against a nil path"
@@ -167,7 +164,7 @@ module Datadog
167
164
  unless file
168
165
  raise ArgumentError, "Probe does not have a file to match against"
169
166
  end
170
- Utils.path_matches_suffix?(path, file)
167
+ Utils.path_can_match_spec?(path, file)
171
168
  end
172
169
 
173
170
  # Instrumentation module for method probes.
@@ -111,9 +111,11 @@ module Datadog
111
111
  # Always remove from pending list here because it makes the
112
112
  # API smaller and shouldn't cause any actual problems.
113
113
  @pending_probes.delete(probe.id)
114
+ logger.trace { "di: installed #{probe.type} probe at #{probe.location} (#{probe.id})" }
114
115
  true
115
116
  rescue Error::DITargetNotDefined
116
117
  @pending_probes[probe.id] = probe
118
+ logger.trace { "di: could not install #{probe.type} probe at #{probe.location} (#{probe.id}) because its target is not defined, adding it to pending list" }
117
119
  false
118
120
  end
119
121
  rescue => exc
@@ -160,7 +162,7 @@ module Datadog
160
162
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
161
163
  # Silence all exceptions?
162
164
  # TODO should we propagate here and rescue upstream?
163
- logger.debug { "di: error removing probe #{probe.id}: #{exc.class}: #{exc}" }
165
+ logger.debug { "di: error removing #{probe.type} probe at #{probe.location} (#{probe.id}): #{exc.class}: #{exc}" }
164
166
  telemetry&.report(exc, description: "Error removing probe")
165
167
  end
166
168
  end
@@ -190,7 +192,7 @@ module Datadog
190
192
  rescue => exc
191
193
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
192
194
 
193
- logger.debug { "di: error installing probe after class is defined: #{exc.class}: #{exc}" }
195
+ logger.debug { "di: error installing #{probe.type} probe at #{probe.location} (#{probe.id}) after class is defined: #{exc.class}: #{exc}" }
194
196
  telemetry&.report(exc, description: "Error installing probe after class is defined")
195
197
  end
196
198
  end
@@ -228,6 +230,7 @@ module Datadog
228
230
  # backend (once per the probe's lifetime) and a snapshot corresponding
229
231
  # to the current invocation.
230
232
  def probe_executed_callback(probe:, **opts)
233
+ logger.trace { "di: executed #{probe.type} probe at #{probe.location} (#{probe.id})" }
231
234
  unless probe.emitting_notified?
232
235
  payload = probe_notification_builder.build_emitting(probe)
233
236
  probe_notifier_worker.add_status(payload)
@@ -10,7 +10,7 @@ module Datadog
10
10
  # The loop inside the worker rescues all exceptions to prevent termination
11
11
  # due to unhandled exceptions raised by any downstream code.
12
12
  # This includes communication and protocol errors when sending the
13
- # payloads to the agent.
13
+ # events to the agent.
14
14
  #
15
15
  # The worker groups the data to send into batches. The goal is to perform
16
16
  # no more than one network operation per event type per second.
@@ -23,12 +23,12 @@ module Datadog
23
23
  #
24
24
  # @api private
25
25
  class ProbeNotifierWorker
26
- def initialize(settings, transport, logger, telemetry: nil)
26
+ def initialize(settings, logger, agent_settings:, telemetry: nil)
27
27
  @settings = settings
28
28
  @telemetry = telemetry
29
29
  @status_queue = []
30
30
  @snapshot_queue = []
31
- @transport = transport
31
+ @agent_settings = agent_settings
32
32
  @logger = logger
33
33
  @lock = Mutex.new
34
34
  @wake = Core::Semaphore.new
@@ -36,15 +36,18 @@ module Datadog
36
36
  @sleep_remaining = nil
37
37
  @wake_scheduled = false
38
38
  @thread = nil
39
+ @pid = nil
39
40
  @flush = 0
40
41
  end
41
42
 
42
43
  attr_reader :settings
43
44
  attr_reader :logger
44
45
  attr_reader :telemetry
46
+ attr_reader :agent_settings
45
47
 
46
48
  def start
47
- return if @thread
49
+ return if @thread && @pid == Process.pid
50
+ logger.trace { "di: starting probe notifier: pid #{$$}" }
48
51
  @thread = Thread.new do
49
52
  loop do
50
53
  # TODO If stop is requested, we stop immediately without
@@ -86,6 +89,7 @@ module Datadog
86
89
  wake.wait(more ? min_send_interval : nil)
87
90
  end
88
91
  end
92
+ @pid = Process.pid
89
93
  end
90
94
 
91
95
  # Stops the background thread.
@@ -94,6 +98,7 @@ module Datadog
94
98
  # to killing the thread using Thread#kill.
95
99
  def stop(timeout = 1)
96
100
  @stop_requested = true
101
+ logger.trace { "di: stopping probe notifier: pid #{$$}" }
97
102
  wake.signal
98
103
  if thread
99
104
  unless thread.join(timeout)
@@ -150,7 +155,6 @@ module Datadog
150
155
 
151
156
  private
152
157
 
153
- attr_reader :transport
154
158
  attr_reader :wake
155
159
  attr_reader :thread
156
160
 
@@ -166,6 +170,22 @@ module Datadog
166
170
 
167
171
  attr_reader :last_sent
168
172
 
173
+ def status_transport
174
+ @status_transport ||= DI::Transport::HTTP.diagnostics(agent_settings: agent_settings)
175
+ end
176
+
177
+ def do_send_status(batch)
178
+ status_transport.send_diagnostics(batch)
179
+ end
180
+
181
+ def snapshot_transport
182
+ @snapshot_transport ||= DI::Transport::HTTP.input(agent_settings: agent_settings)
183
+ end
184
+
185
+ def do_send_snapshot(batch)
186
+ snapshot_transport.send_input(batch)
187
+ end
188
+
169
189
  [
170
190
  [:status, 'probe status'],
171
191
  [:snapshot, 'snapshot'],
@@ -184,8 +204,9 @@ module Datadog
184
204
  @lock.synchronize do
185
205
  queue = send("#{event_type}_queue")
186
206
  if queue.length > settings.dynamic_instrumentation.internal.snapshot_queue_capacity
187
- logger.debug { "di: #{self.class.name}: dropping #{event_type} because queue is full" }
207
+ logger.debug { "di: #{self.class.name}: dropping #{event_type} event because queue is full" }
188
208
  else
209
+ logger.trace { "di: #{self.class.name}: queueing #{event_type} event" }
189
210
  queue << event
190
211
  end
191
212
  end
@@ -200,6 +221,10 @@ module Datadog
200
221
  wake.signal
201
222
  end
202
223
  end
224
+
225
+ # Worker could be not running if the process forked - check and
226
+ # start it again in this case.
227
+ start
203
228
  end
204
229
 
205
230
  # Determine how much longer the worker thread should sleep
@@ -232,9 +257,11 @@ module Datadog
232
257
  instance_variable_set("@#{event_type}_queue", [])
233
258
  @io_in_progress = batch.any? # steep:ignore
234
259
  end
260
+ logger.trace { "di: #{self.class.name}: checking #{event_type} queue - #{batch.length} entries" } # steep:ignore
235
261
  if batch.any? # steep:ignore
236
262
  begin
237
- transport.public_send("send_#{event_type}", batch)
263
+ logger.trace { "di: sending #{batch.length} #{event_type} event(s) to agent" } # steep:ignore
264
+ send("do_send_#{event_type}", batch)
238
265
  time = Core::Utils::Time.get_time
239
266
  @lock.synchronize do
240
267
  @last_sent = time
@@ -263,7 +290,7 @@ module Datadog
263
290
 
264
291
  def maybe_send
265
292
  rv = maybe_send_status
266
- rv || maybe_send_snapshot
293
+ maybe_send_snapshot || rv
267
294
  end
268
295
  end
269
296
  end
@@ -53,7 +53,7 @@ module Datadog
53
53
  payload = probe_notification_builder.build_received(probe)
54
54
  probe_notifier_worker = component.probe_notifier_worker
55
55
  probe_notifier_worker.add_status(payload)
56
- component.logger.debug { "di: received probe from RC: #{probe.type} #{probe.location}" }
56
+ component.logger.debug { "di: received #{probe.type} probe at #{probe.location} (#{probe.id}) via RC" }
57
57
 
58
58
  begin
59
59
  # TODO test exception capture
@@ -76,7 +76,7 @@ module Datadog
76
76
  rescue => exc
77
77
  raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
78
78
 
79
- component.logger.debug { "di: unhandled exception adding probe in DI remote receiver: #{exc.class}: #{exc}" }
79
+ component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc}" }
80
80
  component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
81
81
 
82
82
  # TODO test this path
@@ -101,7 +101,7 @@ module Datadog
101
101
  rescue => exc
102
102
  raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
103
103
 
104
- component.logger.debug { "di: unhandled exception handling probe in DI remote receiver: #{exc.class}: #{exc}" }
104
+ component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc}" }
105
105
  component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
106
106
 
107
107
  # TODO assert content state (errored for this example)
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../core/transport/parcel'
4
+ require_relative 'http/client'
5
+
6
+ module Datadog
7
+ module DI
8
+ module Transport
9
+ module Diagnostics
10
+ class EncodedParcel
11
+ include Datadog::Core::Transport::Parcel
12
+ end
13
+
14
+ class Request < Datadog::Core::Transport::Request
15
+ end
16
+
17
+ class Transport
18
+ attr_reader :client, :apis, :default_api, :current_api_id
19
+
20
+ def initialize(apis, default_api)
21
+ @apis = apis
22
+
23
+ @client = HTTP::Client.new(current_api)
24
+ end
25
+
26
+ def current_api
27
+ @apis[HTTP::API::DIAGNOSTICS]
28
+ end
29
+
30
+ def send_diagnostics(payload)
31
+ json = JSON.dump(payload)
32
+ parcel = EncodedParcel.new(json)
33
+ request = Request.new(parcel)
34
+
35
+ response = @client.send_diagnostics_payload(request)
36
+ unless response.ok?
37
+ # TODO Datadog::Core::Transport::InternalErrorResponse
38
+ # does not have +code+ method, what is the actual API of
39
+ # these response objects?
40
+ raise Error::AgentCommunicationError, "send_diagnostics failed: #{begin
41
+ response.code
42
+ rescue
43
+ "???"
44
+ end}: #{response.payload}"
45
+ end
46
+ rescue Error::AgentCommunicationError
47
+ raise
48
+ # Datadog::Core::Transport does not perform any exception mapping,
49
+ # therefore we could have any exception here from failure to parse
50
+ # agent URI for example.
51
+ # If we ever implement retries for network errors, we should distinguish
52
+ # actual network errors from non-network errors that are raised by
53
+ # transport code.
54
+ rescue => exc
55
+ raise Error::AgentCommunicationError, "send_diagnostics failed: #{exc.class}: #{exc}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../core/encoding'
4
+ require_relative '../../../core/transport/http/api/map'
5
+ require_relative '../../../core/transport/http/api/instance'
6
+ require_relative '../../../core/transport/http/api/spec'
7
+ require_relative 'diagnostics'
8
+ require_relative 'input'
9
+
10
+ module Datadog
11
+ module DI
12
+ module Transport
13
+ module HTTP
14
+ # Namespace for API components
15
+ module API
16
+ # Default API versions
17
+ DIAGNOSTICS = 'diagnostics'
18
+ INPUT = 'input'
19
+
20
+ module_function
21
+
22
+ def defaults
23
+ Datadog::Core::Transport::HTTP::API::Map[
24
+ DIAGNOSTICS => Spec.new do |s|
25
+ s.diagnostics = Diagnostics::API::Endpoint.new(
26
+ '/debugger/v1/diagnostics',
27
+ Core::Encoding::JSONEncoder,
28
+ )
29
+ end,
30
+ INPUT => Spec.new do |s|
31
+ s.input = Input::API::Endpoint.new(
32
+ '/debugger/v1/input',
33
+ Core::Encoding::JSONEncoder,
34
+ )
35
+ end,
36
+ ]
37
+ end
38
+
39
+ class Instance < Core::Transport::HTTP::API::Instance
40
+ include Diagnostics::API::Instance
41
+ include Input::API::Instance
42
+ end
43
+
44
+ class Spec < Core::Transport::HTTP::API::Spec
45
+ include Diagnostics::API::Spec
46
+ include Input::API::Spec
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../core/transport/http/env'
4
+ require_relative '../../../core/transport/http/response'
5
+
6
+ # TODO: Decouple transport/http/client
7
+ #
8
+ # The standard one does `include Transport::HTTP::Statistics` and performs
9
+ # stats updates, which may or may not be desirable in general.
10
+
11
+ module Datadog
12
+ module DI
13
+ module Transport
14
+ module HTTP
15
+ # Routes, encodes, and sends DI data to the trace agent via HTTP.
16
+ class Client
17
+ attr_reader :api
18
+
19
+ def initialize(api)
20
+ @api = api
21
+ end
22
+
23
+ def send_request(request, &block)
24
+ # Build request into env
25
+ env = build_env(request)
26
+
27
+ # Get responses from API
28
+ yield(api, env)
29
+ rescue => e
30
+ message =
31
+ "Internal error during #{self.class.name} request. Cause: #{e.class.name} #{e.message} " \
32
+ "Location: #{Array(e.backtrace).first}"
33
+
34
+ Datadog.logger.debug(message)
35
+
36
+ Datadog::Core::Transport::InternalErrorResponse.new(e)
37
+ end
38
+
39
+ def build_env(request)
40
+ Datadog::Core::Transport::HTTP::Env.new(request)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end