datadog 2.23.0 → 2.24.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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -2
  3. data/ext/datadog_profiling_native_extension/collectors_stack.c +17 -5
  4. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  5. data/ext/datadog_profiling_native_extension/extconf.rb +4 -1
  6. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
  7. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
  8. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  9. data/lib/datadog/appsec/context.rb +2 -1
  10. data/lib/datadog/appsec/remote.rb +1 -9
  11. data/lib/datadog/appsec/security_engine/result.rb +2 -1
  12. data/lib/datadog/core/configuration/config_helper.rb +1 -1
  13. data/lib/datadog/core/configuration/deprecations.rb +2 -2
  14. data/lib/datadog/core/configuration/option_definition.rb +4 -2
  15. data/lib/datadog/core/configuration/options.rb +8 -5
  16. data/lib/datadog/core/configuration/settings.rb +14 -3
  17. data/lib/datadog/core/configuration/supported_configurations.rb +2 -1
  18. data/lib/datadog/core/environment/cgroup.rb +52 -25
  19. data/lib/datadog/core/environment/container.rb +140 -46
  20. data/lib/datadog/core/environment/ext.rb +1 -0
  21. data/lib/datadog/core/environment/process.rb +9 -1
  22. data/lib/datadog/core/rate_limiter.rb +9 -1
  23. data/lib/datadog/core/remote/client.rb +14 -6
  24. data/lib/datadog/core/remote/component.rb +6 -4
  25. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  26. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  27. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  28. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  29. data/lib/datadog/core/remote/transport/config.rb +3 -16
  30. data/lib/datadog/core/remote/transport/http/config.rb +4 -44
  31. data/lib/datadog/core/remote/transport/http/negotiation.rb +0 -39
  32. data/lib/datadog/core/remote/transport/http.rb +13 -24
  33. data/lib/datadog/core/remote/transport/negotiation.rb +7 -16
  34. data/lib/datadog/core/telemetry/component.rb +52 -13
  35. data/lib/datadog/core/telemetry/event/app_started.rb +34 -0
  36. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  37. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  38. data/lib/datadog/core/telemetry/request.rb +17 -3
  39. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +2 -32
  40. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  41. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -10
  42. data/lib/datadog/core/telemetry/worker.rb +88 -32
  43. data/lib/datadog/core/transport/ext.rb +2 -0
  44. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  45. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  46. data/lib/datadog/core/transport/http/builder.rb +9 -5
  47. data/lib/datadog/core/transport/http/client.rb +19 -8
  48. data/lib/datadog/core/transport/http.rb +22 -19
  49. data/lib/datadog/core/transport/response.rb +9 -0
  50. data/lib/datadog/core/transport/transport.rb +90 -0
  51. data/lib/datadog/core/utils/only_once_successful.rb +2 -0
  52. data/lib/datadog/core/utils/time.rb +1 -1
  53. data/lib/datadog/core/workers/async.rb +10 -1
  54. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  55. data/lib/datadog/core/workers/polling.rb +2 -0
  56. data/lib/datadog/core/workers/queue.rb +100 -1
  57. data/lib/datadog/data_streams/processor.rb +1 -1
  58. data/lib/datadog/data_streams/transport/http/stats.rb +1 -36
  59. data/lib/datadog/data_streams/transport/http.rb +5 -6
  60. data/lib/datadog/data_streams/transport/stats.rb +3 -17
  61. data/lib/datadog/di/contrib/active_record.rb +31 -5
  62. data/lib/datadog/di/el/compiler.rb +8 -4
  63. data/lib/datadog/di/error.rb +5 -0
  64. data/lib/datadog/di/instrumenter.rb +17 -4
  65. data/lib/datadog/di/probe_builder.rb +2 -1
  66. data/lib/datadog/di/probe_manager.rb +37 -31
  67. data/lib/datadog/di/probe_notification_builder.rb +15 -2
  68. data/lib/datadog/di/remote.rb +89 -84
  69. data/lib/datadog/di/transport/diagnostics.rb +7 -35
  70. data/lib/datadog/di/transport/http/diagnostics.rb +1 -31
  71. data/lib/datadog/di/transport/http/input.rb +1 -31
  72. data/lib/datadog/di/transport/http.rb +28 -17
  73. data/lib/datadog/di/transport/input.rb +7 -34
  74. data/lib/datadog/di.rb +61 -5
  75. data/lib/datadog/open_feature/evaluation_engine.rb +2 -1
  76. data/lib/datadog/open_feature/remote.rb +3 -10
  77. data/lib/datadog/open_feature/transport.rb +9 -11
  78. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  79. data/lib/datadog/opentelemetry/configuration/settings.rb +2 -2
  80. data/lib/datadog/opentelemetry/metrics.rb +21 -14
  81. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +5 -8
  82. data/lib/datadog/profiling/collectors/code_provenance.rb +27 -2
  83. data/lib/datadog/profiling/collectors/info.rb +2 -1
  84. data/lib/datadog/profiling/component.rb +12 -11
  85. data/lib/datadog/profiling/http_transport.rb +4 -1
  86. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  87. data/lib/datadog/tracing/contrib/karafka/patcher.rb +31 -32
  88. data/lib/datadog/tracing/contrib/status_range_matcher.rb +2 -1
  89. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  90. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +6 -3
  91. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  92. data/lib/datadog/tracing/remote.rb +1 -9
  93. data/lib/datadog/tracing/span_event.rb +2 -2
  94. data/lib/datadog/tracing/span_operation.rb +9 -4
  95. data/lib/datadog/tracing/trace_operation.rb +44 -6
  96. data/lib/datadog/tracing/tracer.rb +42 -16
  97. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  98. data/lib/datadog/tracing/transport/http.rb +15 -9
  99. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  100. data/lib/datadog/tracing/transport/traces.rb +6 -66
  101. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  102. data/lib/datadog/tracing/writer.rb +1 -0
  103. data/lib/datadog/version.rb +2 -2
  104. metadata +7 -13
  105. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  106. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  107. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  108. data/lib/datadog/data_streams/transport/http/api.rb +0 -33
  109. data/lib/datadog/data_streams/transport/http/client.rb +0 -21
  110. data/lib/datadog/di/transport/http/api.rb +0 -42
  111. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  112. data/lib/datadog/tracing/transport/http/api.rb +0 -44
@@ -46,7 +46,7 @@ module Datadog
46
46
  build_snapshot(context)
47
47
  end
48
48
 
49
- NANOSECONDS = 10**9
49
+ NANOSECONDS = 1_000_000_000
50
50
  MILLISECONDS = 1000
51
51
 
52
52
  def build_snapshot(context)
@@ -134,7 +134,7 @@ module Datadog
134
134
  format_caller_locations(caller_locations)
135
135
  end
136
136
 
137
- {
137
+ payload = {
138
138
  service: settings.service,
139
139
  debugger: {
140
140
  type: 'snapshot',
@@ -189,6 +189,10 @@ module Datadog
189
189
  message: message,
190
190
  timestamp: timestamp,
191
191
  }
192
+
193
+ tag_process_tags!(payload, settings)
194
+
195
+ payload
192
196
  end
193
197
 
194
198
  def build_status(probe, message:, status:)
@@ -236,6 +240,15 @@ module Datadog
236
240
  [message, evaluation_errors]
237
241
  end
238
242
 
243
+ def tag_process_tags!(payload, settings)
244
+ return unless settings.experimental_propagate_process_tags_enabled
245
+
246
+ process_tags = Core::Environment::Process.serialized
247
+ return if process_tags.empty?
248
+
249
+ payload[:process_tags] = process_tags
250
+ end
251
+
239
252
  def timestamp_now
240
253
  (Core::Utils::Time.now.to_f * MILLISECONDS).to_i
241
254
  end
@@ -12,8 +12,6 @@ module Datadog
12
12
  #
13
13
  # @api private
14
14
  module Remote
15
- class ReadError < StandardError; end
16
-
17
15
  class << self
18
16
  PRODUCT = 'LIVE_DEBUGGING'
19
17
 
@@ -28,7 +26,7 @@ module Datadog
28
26
  end
29
27
 
30
28
  def receivers(telemetry)
31
- receiver do |repository, _changes|
29
+ receiver do |repository, changes|
32
30
  # DEV: Filter our by product. Given it will be very common
33
31
  # DEV: we can filter this out before we receive the data in this method.
34
32
  # DEV: Apply this refactor to AppSec as well if implemented.
@@ -41,84 +39,23 @@ module Datadog
41
39
  # If the component is nil for some reason, we also don't have a
42
40
  # logger instance to report the issue.
43
41
  if component
44
-
45
- probe_manager = component.probe_manager
46
- probe_notification_builder = component.probe_notification_builder
47
- probe_notifier_worker = component.probe_notifier_worker
48
-
49
- current_probe_ids = {}
50
- repository.contents.each do |content|
51
- case content.path.product
52
- when PRODUCT
53
- begin
54
- probe_spec = parse_content(content)
55
- probe = component.parse_probe_spec_and_notify(probe_spec)
56
- component.logger.debug { "di: received #{probe.type} probe at #{probe.location} (#{probe.id}) via RC" }
57
-
58
- begin
59
- # TODO test exception capture
60
- probe_manager.add_probe(probe)
61
- content.applied
62
- rescue DI::Error::DITargetNotInRegistry => exc
63
- component.telemetry&.report(exc, description: "Line probe is targeting a loaded file that is not in code tracker")
64
-
65
- payload = probe_notification_builder.build_errored(probe, exc)
66
- probe_notifier_worker.add_status(payload)
67
-
68
- # If a probe fails to install, we will mark the content
69
- # as errored. On subsequent remote configuration application
70
- # attemps, probe manager will raise the "previously errored"
71
- # exception and we'll rescue it here, again marking the
72
- # content as errored but with a somewhat different exception
73
- # message.
74
- # TODO assert content state (errored for this example)
75
- content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
76
- rescue => exc
77
- raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
78
-
79
- component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc}" }
80
- component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
81
-
82
- # TODO test this path
83
- payload = probe_notification_builder.build_errored(probe, exc)
84
- probe_notifier_worker.add_status(payload)
85
-
86
- # If a probe fails to install, we will mark the content
87
- # as errored. On subsequent remote configuration application
88
- # attemps, probe manager will raise the "previously errored"
89
- # exception and we'll rescue it here, again marking the
90
- # content as errored but with a somewhat different exception
91
- # message.
92
- # TODO assert content state (errored for this example)
93
- content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
94
- end
95
-
96
- # Important: even if processing fails for this probe config,
97
- # we need to note it as being current so that we do not
98
- # try to remove instrumentation that is still supposed to be
99
- # active.
100
- current_probe_ids[probe_spec.fetch('id')] = true
101
- rescue => exc
102
- raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
103
-
104
- component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc}" }
105
- component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
106
-
107
- # TODO assert content state (errored for this example)
108
- content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
109
- end
42
+ changes.each do |change|
43
+ case change.type
44
+ when :insert
45
+ add_probe(change.content, component)
46
+ when :update
47
+ # We do not implement updates at the moment, remove the
48
+ # probe and reinstall.
49
+ remove_probe(change.content, component)
50
+ add_probe(change.content, component)
51
+ when :delete
52
+ remove_probe(change.previous, component)
53
+ else
54
+ # This really should never happen since we generate the
55
+ # change types in the library.
56
+ component.logger.debug { "di: unrecognized change type: #{change.type}" }
110
57
  end
111
58
  end
112
-
113
- begin
114
- # TODO test exception capture
115
- probe_manager.remove_other_probes(current_probe_ids.keys)
116
- rescue => exc
117
- raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
118
-
119
- component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
120
- component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
121
- end
122
59
  end
123
60
  end
124
61
  end
@@ -130,14 +67,82 @@ module Datadog
130
67
 
131
68
  private
132
69
 
133
- def parse_content(content)
134
- data = content.data.read
70
+ def add_probe(content, component)
71
+ probe_spec = parse_content(content)
72
+ probe = component.parse_probe_spec_and_notify(probe_spec)
73
+ component.logger.debug { "di: received #{probe.type} probe at #{probe.location} (#{probe.id}) via RC" }
74
+
75
+ begin
76
+ # TODO test exception capture
77
+ component.probe_manager.add_probe(probe)
78
+ content.applied
79
+ rescue DI::Error::DITargetNotInRegistry => exc
80
+ component.telemetry&.report(exc, description: "Line probe is targeting a loaded file that is not in code tracker")
81
+
82
+ payload = component.probe_notification_builder.build_errored(probe, exc)
83
+ component.probe_notifier_worker.add_status(payload)
84
+
85
+ # If a probe fails to install, we will mark the content
86
+ # as errored. On subsequent remote configuration application
87
+ # attemps, probe manager will raise the "previously errored"
88
+ # exception and we'll rescue it here, again marking the
89
+ # content as errored but with a somewhat different exception
90
+ # message.
91
+ # TODO assert content state (errored for this example)
92
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
93
+ rescue => exc
94
+ raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
95
+
96
+ component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc}" }
97
+ component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
98
+
99
+ # TODO test this path
100
+ payload = component.probe_notification_builder.build_errored(probe, exc)
101
+ component.probe_notifier_worker.add_status(payload)
102
+
103
+ # If a probe fails to install, we will mark the content
104
+ # as errored. On subsequent remote configuration application
105
+ # attemps, probe manager will raise the "previously errored"
106
+ # exception and we'll rescue it here, again marking the
107
+ # content as errored but with a somewhat different exception
108
+ # message.
109
+ # TODO assert content state (errored for this example)
110
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
111
+ end
112
+
113
+ # Important: even if processing fails for this probe config,
114
+ # we need to note it as being current so that we do not
115
+ # try to remove instrumentation that is still supposed to be
116
+ # active.
117
+ #current_probe_ids[probe_spec.fetch('id')] = true
118
+ rescue => exc
119
+ raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
120
+
121
+ component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc}" }
122
+ component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
135
123
 
136
- content.data.rewind
124
+ # TODO assert content state (errored for this example)
125
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
126
+ end
137
127
 
138
- raise ReadError, 'EOF reached' if data.nil?
128
+ # This method does not mark +previous_content+ as succeeded or errored,
129
+ # because that content is from a previous RC response and has already
130
+ # been marked. Removal of probes happens when an RC entry disappears,
131
+ # as such there is nothing to mark.
132
+ def remove_probe(previous_content, component)
133
+ # TODO test exception capture
134
+ probe_spec = parse_content(previous_content)
135
+ probe_id = probe_spec.fetch('id')
136
+ component.probe_manager.remove_probe(probe_id)
137
+ rescue => exc
138
+ raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
139
+
140
+ component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
141
+ component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
142
+ end
139
143
 
140
- JSON.parse(data)
144
+ def parse_content(content)
145
+ JSON.parse(content.data)
141
146
  end
142
147
  end
143
148
  end
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  require_relative '../../core/transport/parcel'
6
+ require_relative '../../core/transport/request'
7
+ require_relative '../../core/transport/transport'
4
8
  require_relative 'http/diagnostics'
5
9
 
6
10
  module Datadog
@@ -14,46 +18,14 @@ module Datadog
14
18
  class Request < Datadog::Core::Transport::Request
15
19
  end
16
20
 
17
- class Transport
18
- attr_reader :client, :apis, :default_api, :current_api_id, :logger
19
-
20
- def initialize(apis, default_api, logger:)
21
- @apis = apis
22
- @logger = logger
23
-
24
- @client = DI::Transport::HTTP::Diagnostics::Client.new(current_api, logger: logger)
25
- end
26
-
27
- def current_api
28
- @apis[HTTP::API::DIAGNOSTICS]
29
- end
30
-
21
+ class Transport < Core::Transport::Transport
31
22
  def send_diagnostics(payload)
23
+ # TODO use transport encoder functionality?
32
24
  json = JSON.dump(payload)
33
25
  parcel = EncodedParcel.new(json)
34
26
  request = Request.new(parcel)
35
27
 
36
- response = @client.send_diagnostics_payload(request)
37
- unless response.ok?
38
- # TODO Datadog::Core::Transport::InternalErrorResponse
39
- # does not have +code+ method, what is the actual API of
40
- # these response objects?
41
- raise Error::AgentCommunicationError, "send_diagnostics failed: #{begin
42
- response.code
43
- rescue
44
- "???"
45
- end}: #{response.payload}"
46
- end
47
- rescue Error::AgentCommunicationError
48
- raise
49
- # Datadog::Core::Transport does not perform any exception mapping,
50
- # therefore we could have any exception here from failure to parse
51
- # agent URI for example.
52
- # If we ever implement retries for network errors, we should distinguish
53
- # actual network errors from non-network errors that are raised by
54
- # transport code.
55
- rescue => exc
56
- raise Error::AgentCommunicationError, "send_diagnostics failed: #{exc.class}: #{exc}"
28
+ client.send_request(:diagnostics, request)
57
29
  end
58
30
  end
59
31
  end
@@ -1,43 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../../core/transport/http/api/instance'
4
- require_relative '../../../core/transport/http/api/spec'
5
- require_relative '../../../core/transport/http/client'
3
+ require_relative '../../../core/transport/http/api/endpoint'
6
4
 
7
5
  module Datadog
8
6
  module DI
9
7
  module Transport
10
8
  module HTTP
11
9
  module Diagnostics
12
- class Client < Core::Transport::HTTP::Client
13
- def send_diagnostics_payload(request)
14
- send_request(request) do |api, env|
15
- api.send_diagnostics(env)
16
- end
17
- end
18
- end
19
-
20
10
  module API
21
- class Instance < Core::Transport::HTTP::API::Instance
22
- def send_diagnostics(env)
23
- raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new('diagnostics', self) unless spec.is_a?(Diagnostics::API::Spec)
24
-
25
- spec.send_diagnostics(env) do |request_env|
26
- call(request_env)
27
- end
28
- end
29
- end
30
-
31
- class Spec < Core::Transport::HTTP::API::Spec
32
- attr_accessor :diagnostics
33
-
34
- def send_diagnostics(env, &block)
35
- raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('diagnostics', self) if diagnostics.nil?
36
-
37
- diagnostics.call(env, &block)
38
- end
39
- end
40
-
41
11
  class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
42
12
  attr_reader :encoder
43
13
 
@@ -1,43 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../../core/transport/http/api/instance'
4
- require_relative '../../../core/transport/http/api/spec'
5
- require_relative '../../../core/transport/http/client'
3
+ require_relative '../../../core/transport/http/api/endpoint'
6
4
 
7
5
  module Datadog
8
6
  module DI
9
7
  module Transport
10
8
  module HTTP
11
9
  module Input
12
- class Client < Core::Transport::HTTP::Client
13
- def send_input_payload(request)
14
- send_request(request) do |api, env|
15
- api.send_input(env)
16
- end
17
- end
18
- end
19
-
20
10
  module API
21
- class Instance < Core::Transport::HTTP::API::Instance
22
- def send_input(env)
23
- raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new('input', self) unless spec.is_a?(Input::API::Spec)
24
-
25
- spec.send_input(env) do |request_env|
26
- call(request_env)
27
- end
28
- end
29
- end
30
-
31
- class Spec < Core::Transport::HTTP::API::Spec
32
- attr_accessor :input
33
-
34
- def send_input(env, &block)
35
- raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('input', self) if input.nil?
36
-
37
- input.call(env, &block)
38
- end
39
- end
40
-
41
11
  class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
42
12
  HEADER_CONTENT_TYPE = 'Content-Type'
43
13
 
@@ -1,31 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../core/encoding'
4
+ require_relative '../../core/transport/http'
3
5
  require_relative 'diagnostics'
4
6
  require_relative 'input'
5
- require_relative 'http/api'
6
- require_relative '../../core/transport/http'
7
7
 
8
8
  module Datadog
9
9
  module DI
10
10
  module Transport
11
11
  # Namespace for HTTP transport components
12
12
  module HTTP
13
- module_function
13
+ DIAGNOSTICS = Diagnostics::API::Endpoint.new(
14
+ '/debugger/v1/diagnostics',
15
+ Core::Encoding::JSONEncoder,
16
+ )
17
+
18
+ INPUT = Input::API::Endpoint.new(
19
+ '/debugger/v2/input',
20
+ Core::Encoding::JSONEncoder,
21
+ )
22
+
23
+ LEGACY_INPUT = Input::API::Endpoint.new(
24
+ # We used to use /debugger/v1/input, but now input
25
+ # payloads should be going to the diagnostics endpoint
26
+ # which I gather performs data redaction.
27
+ '/debugger/v1/diagnostics',
28
+ Core::Encoding::JSONEncoder,
29
+ )
14
30
 
15
31
  # Builds a new Transport::HTTP::Client with default settings
16
32
  # Pass a block to override any settings.
17
- def diagnostics(
33
+ def self.diagnostics(
18
34
  agent_settings:,
19
35
  logger:,
20
- api_version: nil,
21
36
  headers: nil
22
37
  )
23
- Core::Transport::HTTP.build(api_instance_class: Diagnostics::API::Instance,
38
+ Core::Transport::HTTP.build(
24
39
  logger: logger,
25
- agent_settings: agent_settings, api_version: api_version, headers: headers) do |transport|
26
- apis = API.defaults
27
-
28
- transport.api API::DIAGNOSTICS, apis[API::DIAGNOSTICS]
40
+ agent_settings: agent_settings,
41
+ headers: headers,
42
+ ) do |transport|
43
+ transport.api 'diagnostics', DIAGNOSTICS
29
44
 
30
45
  # Call block to apply any customization, if provided
31
46
  yield(transport) if block_given?
@@ -34,22 +49,18 @@ module Datadog
34
49
 
35
50
  # Builds a new Transport::HTTP::Client with default settings
36
51
  # Pass a block to override any settings.
37
- def input(
52
+ def self.input(
38
53
  agent_settings:,
39
54
  logger:,
40
- api_version: nil,
41
55
  headers: nil
42
56
  )
43
57
  Core::Transport::HTTP.build(
44
- api_instance_class: Input::API::Instance,
45
58
  logger: logger,
46
59
  agent_settings: agent_settings,
47
- api_version: api_version,
48
60
  headers: headers,
49
61
  ) do |transport|
50
- apis = API.defaults
51
-
52
- transport.api API::INPUT, apis[API::INPUT]
62
+ transport.api 'input', INPUT, fallback: 'legacy_input', default: true
63
+ transport.api 'legacy_input', LEGACY_INPUT
53
64
 
54
65
  # Call block to apply any customization, if provided
55
66
  yield(transport) if block_given?
@@ -5,6 +5,7 @@ require_relative '../../core/encoding'
5
5
  require_relative '../../core/tag_builder'
6
6
  require_relative '../../core/transport/parcel'
7
7
  require_relative '../../core/transport/request'
8
+ require_relative '../../core/transport/transport'
8
9
  require_relative '../error'
9
10
  require_relative 'http/input'
10
11
 
@@ -26,9 +27,7 @@ module Datadog
26
27
  end
27
28
  end
28
29
 
29
- class Transport
30
- attr_reader :client, :apis, :default_api, :current_api_id, :logger
31
-
30
+ class Transport < Core::Transport::Transport
32
31
  # The limit on an individual snapshot payload, aka "log line",
33
32
  # is 1 MB.
34
33
  #
@@ -48,17 +47,6 @@ module Datadog
48
47
  # max chunk size, it will still get sent out.
49
48
  DEFAULT_CHUNK_SIZE = 2 * 1024 * 1024
50
49
 
51
- def initialize(apis, default_api, logger:)
52
- @apis = apis
53
- @logger = logger
54
-
55
- @client = DI::Transport::HTTP::Input::Client.new(current_api, logger: logger)
56
- end
57
-
58
- def current_api
59
- @apis[HTTP::API::INPUT]
60
- end
61
-
62
50
  def send_input(payload, tags)
63
51
  # Tags are the same for all chunks, serialize them one time.
64
52
  serialized_tags = Core::TagBuilder.serialize_tags(tags)
@@ -101,27 +89,12 @@ module Datadog
101
89
  parcel = EncodedParcel.new(chunked_payload)
102
90
  request = Request.new(parcel, serialized_tags)
103
91
 
104
- response = @client.send_input_payload(request)
105
- unless response.ok?
106
- # TODO Datadog::Core::Transport::InternalErrorResponse
107
- # does not have +code+ method, what is the actual API of
108
- # these response objects?
109
- raise Error::AgentCommunicationError, "send_input failed: #{begin
110
- response.code
111
- rescue
112
- "???"
113
- end}: #{response.payload}"
92
+ client.send_request(:input, request).tap do |response|
93
+ if downgrade?(response)
94
+ downgrade!
95
+ return send_input_chunk(chunked_payload, serialized_tags)
96
+ end
114
97
  end
115
- rescue Error::AgentCommunicationError
116
- raise
117
- # Datadog::Core::Transport does not perform any exception mapping,
118
- # therefore we could have any exception here from failure to parse
119
- # agent URI for example.
120
- # If we ever implement retries for network errors, we should distinguish
121
- # actual network errors from non-network errors that are raised by
122
- # transport code.
123
- rescue => exc
124
- raise Error::AgentCommunicationError, "send_input failed: #{exc.class}: #{exc}"
125
98
  end
126
99
  end
127
100
  end
data/lib/datadog/di.rb CHANGED
@@ -9,16 +9,13 @@ module Datadog
9
9
  #
10
10
  # @api private
11
11
  module DI
12
+ INSTRUMENTED_COUNTERS_LOCK = Mutex.new
13
+
12
14
  class << self
13
15
  def enabled?
14
16
  Datadog.configuration.dynamic_instrumentation.enabled
15
17
  end
16
- end
17
-
18
- # Expose DI to global shared objects
19
- Extensions.activate!
20
18
 
21
- class << self
22
19
  # This method is called from DI Remote handler to issue DI operations
23
20
  # to the probe manager (add or remove probes).
24
21
  #
@@ -31,6 +28,65 @@ module Datadog
31
28
  def component
32
29
  Datadog.send(:components).dynamic_instrumentation
33
30
  end
31
+
32
+ # Track how many outstanding instrumentations are in DI.
33
+ #
34
+ # It is hard to find the actual instrumentations - there is no
35
+ # method provided by Ruby to list all trace points, and we would
36
+ # need to manually track our instrumentation modules for method probes.
37
+ # Plus, tracking the modules could create active references to
38
+ # instrumentation, which is not desired.
39
+ #
40
+ # A simpler solution is to maintain a counter which is increased
41
+ # whenever a probe is installed and decreased when a probe is removed.
42
+ #
43
+ # This counter does not include pending probes - being not installed,
44
+ # those pose no concerns to customer applications.
45
+ def instrumented_count(kind = nil)
46
+ INSTRUMENTED_COUNTERS_LOCK.synchronize do
47
+ if defined?(@instrumented_count)
48
+ if kind
49
+ validate_kind!(kind)
50
+ @instrumented_count[kind] || 0
51
+ else
52
+ @instrumented_count.inject(0) do |sum, (_kind, count)|
53
+ sum + count
54
+ end
55
+ end
56
+ else
57
+ 0
58
+ end
59
+ end
60
+ end
61
+
62
+ def instrumented_count_inc(kind)
63
+ validate_kind!(kind)
64
+ INSTRUMENTED_COUNTERS_LOCK.synchronize do
65
+ @instrumented_count = Hash.new(0) unless defined?(@instrumented_count)
66
+ @instrumented_count[kind] += 1
67
+ end
68
+ end
69
+
70
+ def instrumented_count_dec(kind)
71
+ validate_kind!(kind)
72
+ INSTRUMENTED_COUNTERS_LOCK.synchronize do
73
+ @instrumented_count = Hash.new(0) unless defined?(@instrumented_count)
74
+ if @instrumented_count[kind] <= 0
75
+ Datadog.logger.debug { "di: attempting to decrease instrumented count below zero for #{kind}" }
76
+ return
77
+ end
78
+ @instrumented_count[kind] -= 1
79
+ end
80
+ end
81
+
82
+ private def validate_kind!(kind)
83
+ unless %i[line method].include?(kind)
84
+ raise ArgumentError, "Invalid kind: #{kind}"
85
+ end
86
+ end
34
87
  end
88
+
89
+ # Expose DI to global shared objects
90
+ Extensions.activate!
35
91
  end
36
92
  end
@@ -9,7 +9,8 @@ module Datadog
9
9
  module OpenFeature
10
10
  # This class performs the evaluation of the feature flag
11
11
  class EvaluationEngine
12
- ReconfigurationError = Class.new(StandardError)
12
+ # Steep: https://github.com/soutaro/steep/issues/1880
13
+ ReconfigurationError = Class.new(StandardError) # steep:ignore IncompatibleAssignment
13
14
 
14
15
  ALLOWED_TYPES = %i[boolean string number float integer object].freeze
15
16