cw-datadog 2.23.0.2 → 2.23.0.3

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/ext/datadog_profiling_native_extension/extconf.rb +4 -2
  3. data/ext/libdatadog_api/library_config.c +12 -11
  4. data/ext/libdatadog_extconf_helpers.rb +1 -1
  5. data/lib/datadog/appsec/api_security/route_extractor.rb +20 -5
  6. data/lib/datadog/appsec/api_security/sampler.rb +3 -1
  7. data/lib/datadog/appsec/assets/blocked.html +8 -0
  8. data/lib/datadog/appsec/assets/blocked.json +1 -1
  9. data/lib/datadog/appsec/assets/blocked.text +3 -1
  10. data/lib/datadog/appsec/assets.rb +1 -1
  11. data/lib/datadog/appsec/remote.rb +4 -0
  12. data/lib/datadog/appsec/response.rb +18 -4
  13. data/lib/datadog/core/cloudwise/client.rb +364 -25
  14. data/lib/datadog/core/cloudwise/component.rb +197 -52
  15. data/lib/datadog/core/cloudwise/docc_heartbeat_worker.rb +105 -0
  16. data/lib/datadog/core/cloudwise/docc_operation_worker.rb +191 -0
  17. data/lib/datadog/core/cloudwise/docc_registration_worker.rb +89 -0
  18. data/lib/datadog/core/cloudwise/license_worker.rb +3 -1
  19. data/lib/datadog/core/cloudwise/probe_state.rb +134 -12
  20. data/lib/datadog/core/configuration/components.rb +10 -9
  21. data/lib/datadog/core/configuration/settings.rb +28 -0
  22. data/lib/datadog/core/configuration/supported_configurations.rb +5 -2
  23. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  24. data/lib/datadog/core/remote/component.rb +2 -2
  25. data/lib/datadog/core/remote/transport/config.rb +2 -10
  26. data/lib/datadog/core/remote/transport/http/config.rb +9 -9
  27. data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
  28. data/lib/datadog/core/remote/transport/http.rb +2 -0
  29. data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
  30. data/lib/datadog/core/remote/worker.rb +23 -35
  31. data/lib/datadog/core/telemetry/component.rb +26 -13
  32. data/lib/datadog/core/telemetry/event/app_started.rb +67 -49
  33. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  34. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
  35. data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
  36. data/lib/datadog/core/telemetry/worker.rb +51 -6
  37. data/lib/datadog/core/transport/http/adapters/net.rb +2 -0
  38. data/lib/datadog/core/transport/http/client.rb +69 -0
  39. data/lib/datadog/core/utils/only_once_successful.rb +6 -2
  40. data/lib/datadog/data_streams/transport/http/client.rb +4 -32
  41. data/lib/datadog/data_streams/transport/stats.rb +1 -1
  42. data/lib/datadog/di/probe_notification_builder.rb +35 -13
  43. data/lib/datadog/di/transport/diagnostics.rb +2 -2
  44. data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
  45. data/lib/datadog/di/transport/http/input.rb +2 -4
  46. data/lib/datadog/di/transport/input.rb +2 -2
  47. data/lib/datadog/open_feature/component.rb +60 -0
  48. data/lib/datadog/open_feature/configuration.rb +27 -0
  49. data/lib/datadog/open_feature/evaluation_engine.rb +59 -0
  50. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  51. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  52. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  53. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  54. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  55. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  56. data/lib/datadog/open_feature/ext.rb +13 -0
  57. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  58. data/lib/datadog/open_feature/provider.rb +134 -0
  59. data/lib/datadog/open_feature/remote.rb +74 -0
  60. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  61. data/lib/datadog/open_feature/transport.rb +72 -0
  62. data/lib/datadog/open_feature.rb +19 -0
  63. data/lib/datadog/profiling/component.rb +6 -0
  64. data/lib/datadog/profiling/profiler.rb +4 -0
  65. data/lib/datadog/profiling.rb +1 -2
  66. data/lib/datadog/single_step_instrument.rb +1 -1
  67. data/lib/datadog/tracing/contrib/cloudwise/propagation.rb +164 -7
  68. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +22 -17
  69. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  70. data/lib/datadog/tracing/contrib/karafka/patcher.rb +14 -0
  71. data/lib/datadog/tracing/contrib/rack/middlewares.rb +6 -2
  72. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  73. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  74. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  75. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  76. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  77. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
  78. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  79. data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
  80. data/lib/datadog/tracing/contrib.rb +1 -0
  81. data/lib/datadog/tracing/transport/http/api.rb +40 -1
  82. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  83. data/lib/datadog/tracing/transport/http/traces.rb +4 -2
  84. data/lib/datadog/tracing/transport/trace_formatter.rb +16 -0
  85. data/lib/datadog/version.rb +2 -2
  86. data/lib/datadog.rb +1 -0
  87. metadata +38 -15
  88. data/lib/datadog/core/cloudwise/IMPLEMENTATION_V2.md +0 -517
  89. data/lib/datadog/core/cloudwise/QUICKSTART.md +0 -398
  90. data/lib/datadog/core/cloudwise/README.md +0 -722
  91. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  92. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  93. data/lib/datadog/di/transport/http/client.rb +0 -47
@@ -14,14 +14,23 @@ require_relative '../utils/forking'
14
14
  module Datadog
15
15
  module Core
16
16
  module Telemetry
17
- # Telemetry entrypoint, coordinates sending telemetry events at various points in app lifecycle.
18
- # Note: Telemetry does not spawn its worker thread in fork processes, thus no telemetry is sent in forked processes.
17
+ # Telemetry entry point, coordinates sending telemetry events at
18
+ # various points in application lifecycle.
19
19
  #
20
20
  # @api private
21
21
  class Component
22
22
  ENDPOINT_COLLECTION_MESSAGE_LIMIT = 300
23
23
 
24
- attr_reader :enabled, :logger, :transport, :worker
24
+ attr_reader :enabled
25
+ attr_reader :logger
26
+ attr_reader :transport
27
+ attr_reader :worker
28
+ attr_reader :settings
29
+ attr_reader :agent_settings
30
+
31
+ # Alias for consistency with other components.
32
+ # TODO Remove +enabled+ method
33
+ alias_method :enabled?, :enabled
25
34
 
26
35
  include Core::Utils::Forking
27
36
  include Telemetry::Logging
@@ -109,13 +118,17 @@ module Datadog
109
118
  @worker&.enabled = false
110
119
  end
111
120
 
112
- def start(initial_event_is_change = false)
113
- return if !@enabled
121
+ def start(initial_event_is_change = false, components:)
122
+ return unless enabled?
114
123
 
115
124
  initial_event = if initial_event_is_change
116
- Event::SynthAppClientConfigurationChange.new(agent_settings: @agent_settings)
125
+ Event::SynthAppClientConfigurationChange.new(
126
+ components: components,
127
+ )
117
128
  else
118
- Event::AppStarted.new(agent_settings: @agent_settings)
129
+ Event::AppStarted.new(
130
+ components: components,
131
+ )
119
132
  end
120
133
 
121
134
  @worker.start(initial_event)
@@ -132,19 +145,19 @@ module Datadog
132
145
  end
133
146
 
134
147
  def emit_closing!
135
- return if !@enabled || forked?
148
+ return unless enabled?
136
149
 
137
150
  @worker.enqueue(Event::AppClosing.new)
138
151
  end
139
152
 
140
153
  def integrations_change!
141
- return if !@enabled || forked?
154
+ return unless enabled?
142
155
 
143
156
  @worker.enqueue(Event::AppIntegrationsChange.new)
144
157
  end
145
158
 
146
159
  def log!(event)
147
- return if !@enabled || forked? || !@log_collection_enabled
160
+ return unless enabled? && @log_collection_enabled
148
161
 
149
162
  @worker.enqueue(event)
150
163
  end
@@ -155,21 +168,21 @@ module Datadog
155
168
  #
156
169
  # @api private
157
170
  def flush
158
- return if !@enabled || forked?
171
+ return unless enabled?
159
172
 
160
173
  @worker.flush
161
174
  end
162
175
 
163
176
  # Report configuration changes caused by Remote Configuration.
164
177
  def client_configuration_change!(changes)
165
- return if !@enabled || forked?
178
+ return unless enabled?
166
179
 
167
180
  @worker.enqueue(Event::AppClientConfigurationChange.new(changes, 'remote_config'))
168
181
  end
169
182
 
170
183
  # Report application endpoints
171
184
  def app_endpoints_loaded(endpoints, page_size: ENDPOINT_COLLECTION_MESSAGE_LIMIT)
172
- return if !@enabled || forked?
185
+ return unless enabled?
173
186
 
174
187
  endpoints.each_slice(page_size).with_index do |endpoints_slice, i|
175
188
  @worker.enqueue(Event::AppEndpointsLoaded.new(endpoints_slice, is_first: i.zero?))
@@ -8,8 +8,18 @@ module Datadog
8
8
  module Event
9
9
  # Telemetry class for the 'app-started' event
10
10
  class AppStarted < Base
11
- def initialize(agent_settings:)
12
- @agent_settings = agent_settings
11
+ def initialize(components:)
12
+ # To not hold a reference to the component tree, generate
13
+ # the event payload here in the constructor.
14
+ #
15
+ # Important: do not store data that contains (or is derived from)
16
+ # the runtime_id or sequence numbers.
17
+ # This event is reused when a process forks, but in the
18
+ # child process the runtime_id would be different and sequence
19
+ # number is reset.
20
+ @configuration = configuration(components.settings, components.agent_settings)
21
+ @install_signature = install_signature(components.settings)
22
+ @products = products(components)
13
23
  end
14
24
 
15
25
  def type
@@ -18,27 +28,37 @@ module Datadog
18
28
 
19
29
  def payload
20
30
  {
21
- products: products,
22
- configuration: configuration,
23
- install_signature: install_signature,
31
+ products: @products,
32
+ configuration: @configuration,
33
+ install_signature: @install_signature,
24
34
  # DEV: Not implemented yet
25
35
  # error: error, # Start-up errors
26
36
  }
27
37
  end
28
38
 
39
+ # Whether the event is actually the app-started event.
40
+ # For the app-started event we follow up by sending
41
+ # app-dependencies-loaded, if the event is
42
+ # app-client-configuration-change we don't send
43
+ # app-dependencies-loaded.
44
+ def app_started?
45
+ true
46
+ end
47
+
29
48
  private
30
49
 
31
- def products
50
+ def products(components)
32
51
  # @type var products: Hash[Symbol, Hash[Symbol, Hash[Symbol, String | Integer] | bool | nil]]
33
52
  products = {
34
53
  appsec: {
35
- enabled: Datadog::AppSec.enabled?,
54
+ # TODO take appsec status out of component tree?
55
+ enabled: components.settings.appsec.enabled,
36
56
  },
37
57
  profiler: {
38
- enabled: Datadog::Profiling.enabled?,
58
+ enabled: !!components.profiler&.enabled?,
39
59
  },
40
60
  dynamic_instrumentation: {
41
- enabled: defined?(Datadog::DI) && Datadog::DI.respond_to?(:enabled?) && Datadog::DI.enabled?,
61
+ enabled: !!components.dynamic_instrumentation,
42
62
  }
43
63
  }
44
64
 
@@ -73,12 +93,11 @@ module Datadog
73
93
 
74
94
  # standard:disable Metrics/AbcSize
75
95
  # standard:disable Metrics/MethodLength
76
- def configuration
77
- config = Datadog.configuration
96
+ def configuration(settings, agent_settings)
78
97
  seq_id = Event.configuration_sequence.next
79
98
 
80
99
  # tracing.writer_options.buffer_size and tracing.writer_options.flush_interval have the same origin.
81
- writer_option_origin = get_telemetry_origin(config, 'tracing.writer_options')
100
+ writer_option_origin = get_telemetry_origin(settings, 'tracing.writer_options')
82
101
 
83
102
  list = [
84
103
  # Only set using env var as of June 2025
@@ -100,59 +119,59 @@ module Datadog
100
119
  ),
101
120
 
102
121
  # Mix of env var, programmatic and default config, so we use unknown
103
- conf_value('DD_AGENT_TRANSPORT', agent_transport, seq_id, 'unknown'), # rubocop:disable CustomCops/EnvStringValidationCop
122
+ conf_value('DD_AGENT_TRANSPORT', agent_transport(agent_settings), seq_id, 'unknown'), # rubocop:disable CustomCops/EnvStringValidationCop
104
123
 
105
124
  # writer_options is defined as an option that has a Hash value.
106
125
  conf_value(
107
126
  'tracing.writer_options.buffer_size',
108
- to_value(config.tracing.writer_options[:buffer_size]),
127
+ to_value(settings.tracing.writer_options[:buffer_size]),
109
128
  seq_id,
110
129
  writer_option_origin
111
130
  ),
112
131
  conf_value(
113
132
  'tracing.writer_options.flush_interval',
114
- to_value(config.tracing.writer_options[:flush_interval]),
133
+ to_value(settings.tracing.writer_options[:flush_interval]),
115
134
  seq_id,
116
135
  writer_option_origin
117
136
  ),
118
137
 
119
- conf_value('DD_AGENT_HOST', config.agent.host, seq_id, get_telemetry_origin(config, 'agent.host')),
138
+ conf_value('DD_AGENT_HOST', settings.agent.host, seq_id, get_telemetry_origin(settings, 'agent.host')),
120
139
  conf_value(
121
140
  'DD_TRACE_SAMPLE_RATE',
122
- to_value(config.tracing.sampling.default_rate),
141
+ to_value(settings.tracing.sampling.default_rate),
123
142
  seq_id,
124
- get_telemetry_origin(config, 'tracing.sampling.default_rate')
143
+ get_telemetry_origin(settings, 'tracing.sampling.default_rate')
125
144
  ),
126
145
  conf_value(
127
146
  'DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED',
128
- config.tracing.contrib.global_default_service_name.enabled,
147
+ settings.tracing.contrib.global_default_service_name.enabled,
129
148
  seq_id,
130
- get_telemetry_origin(config, 'tracing.contrib.global_default_service_name.enabled')
149
+ get_telemetry_origin(settings, 'tracing.contrib.global_default_service_name.enabled')
131
150
  ),
132
151
  conf_value(
133
152
  'DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED',
134
- config.tracing.contrib.peer_service_defaults,
153
+ settings.tracing.contrib.peer_service_defaults,
135
154
  seq_id,
136
- get_telemetry_origin(config, 'tracing.contrib.peer_service_defaults')
155
+ get_telemetry_origin(settings, 'tracing.contrib.peer_service_defaults')
137
156
  ),
138
157
  conf_value(
139
158
  'DD_TRACE_DEBUG',
140
- config.diagnostics.debug,
159
+ settings.diagnostics.debug,
141
160
  seq_id,
142
- get_telemetry_origin(config, 'diagnostics.debug')
161
+ get_telemetry_origin(settings, 'diagnostics.debug')
143
162
  )
144
163
  ]
145
164
 
146
165
  peer_service_mapping_str = ''
147
- unless config.tracing.contrib.peer_service_mapping.empty?
148
- peer_service_mapping = config.tracing.contrib.peer_service_mapping
166
+ unless settings.tracing.contrib.peer_service_mapping.empty?
167
+ peer_service_mapping = settings.tracing.contrib.peer_service_mapping
149
168
  peer_service_mapping_str = peer_service_mapping.map { |key, value| "#{key}:#{value}" }.join(',')
150
169
  end
151
170
  list << conf_value(
152
171
  'DD_TRACE_PEER_SERVICE_MAPPING',
153
172
  peer_service_mapping_str,
154
173
  seq_id,
155
- get_telemetry_origin(config, 'tracing.contrib.peer_service_mapping')
174
+ get_telemetry_origin(settings, 'tracing.contrib.peer_service_mapping')
156
175
  )
157
176
 
158
177
  # Whitelist of configuration options to send in additional payload object
@@ -160,9 +179,9 @@ module Datadog
160
179
  split_option = option_path.split('.')
161
180
  list << conf_value(
162
181
  option_path,
163
- to_value(config.dig(*split_option)),
182
+ to_value(settings.dig(*split_option)),
164
183
  seq_id,
165
- get_telemetry_origin(config, option_path)
184
+ get_telemetry_origin(settings, option_path)
166
185
  )
167
186
  end
168
187
 
@@ -181,34 +200,34 @@ module Datadog
181
200
  )
182
201
 
183
202
  # Add some more custom additional payload values here
184
- if config.logger.instance
203
+ if settings.logger.instance
185
204
  list << conf_value(
186
205
  'logger.instance',
187
- config.logger.instance.class.to_s,
206
+ settings.logger.instance.class.to_s,
188
207
  seq_id,
189
- get_telemetry_origin(config, 'logger.instance')
208
+ get_telemetry_origin(settings, 'logger.instance')
190
209
  )
191
210
  end
192
- if config.respond_to?('appsec')
211
+ if settings.respond_to?('appsec')
193
212
  list << conf_value(
194
213
  'appsec.enabled',
195
- config.dig('appsec', 'enabled'),
214
+ settings.dig('appsec', 'enabled'),
196
215
  seq_id,
197
- get_telemetry_origin(config, 'appsec.enabled')
216
+ get_telemetry_origin(settings, 'appsec.enabled')
198
217
  )
199
218
  list << conf_value(
200
219
  'appsec.sca_enabled',
201
- config.dig('appsec', 'sca_enabled'),
220
+ settings.dig('appsec', 'sca_enabled'),
202
221
  seq_id,
203
- get_telemetry_origin(config, 'appsec.sca_enabled')
222
+ get_telemetry_origin(settings, 'appsec.sca_enabled')
204
223
  )
205
224
  end
206
- if config.respond_to?('ci')
225
+ if settings.respond_to?('ci')
207
226
  list << conf_value(
208
227
  'ci.enabled',
209
- config.dig('ci', 'enabled'),
228
+ settings.dig('ci', 'enabled'),
210
229
  seq_id,
211
- get_telemetry_origin(config, 'ci.enabled')
230
+ get_telemetry_origin(settings, 'ci.enabled')
212
231
  )
213
232
  end
214
233
 
@@ -218,8 +237,8 @@ module Datadog
218
237
  # standard:enable Metrics/AbcSize
219
238
  # standard:enable Metrics/MethodLength
220
239
 
221
- def agent_transport
222
- adapter = @agent_settings.adapter
240
+ def agent_transport(agent_settings)
241
+ adapter = agent_settings.adapter
223
242
  if adapter == Datadog::Core::Transport::Ext::UnixSocket::ADAPTER
224
243
  'UDS'
225
244
  else
@@ -260,23 +279,22 @@ module Datadog
260
279
  end
261
280
  end
262
281
 
263
- def install_signature
264
- config = Datadog.configuration
282
+ def install_signature(settings)
265
283
  {
266
- install_id: config.dig('telemetry', 'install_id'),
267
- install_type: config.dig('telemetry', 'install_type'),
268
- install_time: config.dig('telemetry', 'install_time'),
284
+ install_id: settings.dig('telemetry', 'install_id'),
285
+ install_type: settings.dig('telemetry', 'install_type'),
286
+ install_time: settings.dig('telemetry', 'install_time'),
269
287
  }
270
288
  end
271
289
 
272
- def get_telemetry_origin(config, config_path)
290
+ def get_telemetry_origin(settings, config_path)
273
291
  split_option = config_path.split('.')
274
292
  option_name = split_option.pop
275
293
  return 'unknown' if option_name.nil?
276
294
 
277
295
  # @type var parent_setting: Core::Configuration::Options
278
296
  # @type var option: Core::Configuration::Option
279
- parent_setting = config.dig(*split_option)
297
+ parent_setting = settings.dig(*split_option)
280
298
  option = parent_setting.send(:resolve_option, option_name.to_sym)
281
299
  option.precedence_set&.origin || 'unknown'
282
300
  end
@@ -28,13 +28,36 @@ module Datadog
28
28
  # and app-closing events.
29
29
  class SynthAppClientConfigurationChange < AppStarted
30
30
  def type
31
- 'app-client-configuration-change'
31
+ if reset?
32
+ super
33
+ else
34
+ 'app-client-configuration-change'
35
+ end
32
36
  end
33
37
 
34
38
  def payload
35
- {
36
- configuration: configuration,
37
- }
39
+ if reset?
40
+ super
41
+ else
42
+ {
43
+ configuration: @configuration,
44
+ }
45
+ end
46
+ end
47
+
48
+ def app_started?
49
+ reset?
50
+ end
51
+
52
+ # Revert this event to a "regular" AppStarted event.
53
+ #
54
+ # Used in after_fork to send the AppStarted event in child processes.
55
+ def reset!
56
+ @reset = true
57
+ end
58
+
59
+ def reset?
60
+ !!@reset
38
61
  end
39
62
  end
40
63
  end
@@ -3,8 +3,8 @@
3
3
  require_relative '../../../transport/http/api/endpoint'
4
4
  require_relative '../../../transport/http/api/instance'
5
5
  require_relative '../../../transport/http/api/spec'
6
+ require_relative '../../../transport/http/client'
6
7
  require_relative '../../../transport/request'
7
- require_relative 'client'
8
8
 
9
9
  module Datadog
10
10
  module Core
@@ -12,10 +12,11 @@ module Datadog
12
12
  module Transport
13
13
  module HTTP
14
14
  module Telemetry
15
- module Client
15
+ class Client < Core::Transport::HTTP::Client
16
16
  def send_telemetry_payload(request)
17
- send_request(request) do |api, env| # steep:ignore
18
- api.send_telemetry(env)
17
+ send_request(request) do |api, env|
18
+ # TODO how to make api have the derived type for steep?
19
+ api.send_telemetry(env) # steep:ignore
19
20
  end
20
21
  end
21
22
  end
@@ -83,8 +84,6 @@ module Datadog
83
84
  end
84
85
  end
85
86
  end
86
-
87
- HTTP::Client.include(Telemetry::Client)
88
87
  end
89
88
  end
90
89
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../transport/parcel'
4
- require_relative 'http/client'
5
4
  require_relative 'http/telemetry'
6
5
 
7
6
  module Datadog
@@ -32,7 +31,7 @@ module Datadog
32
31
  @apis = apis
33
32
  @logger = logger
34
33
 
35
- @client = HTTP::Client.new(@apis[default_api], logger: logger)
34
+ @client = Core::Telemetry::Transport::HTTP::Telemetry::Client.new(@apis[default_api], logger: logger)
36
35
  end
37
36
 
38
37
  def send_telemetry(request_type:, payload:)
@@ -9,7 +9,10 @@ require_relative '../workers/queue'
9
9
  module Datadog
10
10
  module Core
11
11
  module Telemetry
12
- # Accumulates events and sends them to the API at a regular interval, including heartbeat event.
12
+ # Accumulates events and sends them to the API at a regular interval,
13
+ # including heartbeat event.
14
+ #
15
+ # @api private
13
16
  class Worker
14
17
  include Core::Workers::Queue
15
18
  include Core::Workers::Polling
@@ -40,11 +43,15 @@ module Datadog
40
43
  self.enabled = enabled
41
44
  # Workers::IntervalLoop settings
42
45
  self.loop_base_interval = metrics_aggregation_interval_seconds
43
- self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_STOP
46
+ self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_RESTART
44
47
 
45
48
  @shutdown_timeout = shutdown_timeout
46
49
  @buffer_size = buffer_size
47
50
 
51
+ initialize_state
52
+ end
53
+
54
+ private def initialize_state
48
55
  self.buffer = buffer_klass.new(@buffer_size)
49
56
 
50
57
  @initial_event_once = Utils::OnlyOnceSuccessful.new(APP_STARTED_EVENT_RETRIES)
@@ -53,12 +60,13 @@ module Datadog
53
60
  attr_reader :logger
54
61
  attr_reader :initial_event_once
55
62
  attr_reader :initial_event
63
+ attr_reader :emitter
56
64
 
57
65
  # Returns true if worker thread is successfully started,
58
66
  # false if worker thread was not started but telemetry is enabled,
59
67
  # nil if telemetry is disabled.
60
68
  def start(initial_event)
61
- return if !enabled? || forked?
69
+ return unless enabled?
62
70
 
63
71
  @initial_event = initial_event
64
72
 
@@ -79,7 +87,21 @@ module Datadog
79
87
  # for not enqueueing event (presently) is that telemetry is disabled
80
88
  # altogether, and in this case other methods return nil.
81
89
  def enqueue(event)
82
- return if !enabled? || forked?
90
+ return unless enabled?
91
+
92
+ # Start the worker if needed, including in forked children.
93
+ # Needs to be done before pushing to buffer since perform
94
+ # may invoke after_fork handler which resets the buffer.
95
+ #
96
+ # Telemetry is special in that it permits events to be submitted
97
+ # to the worker with the worker not running, and the worker is
98
+ # explicitly started later (to maintain proper initialization order).
99
+ # Thus here we can't just call perform unconditionally and must
100
+ # check if the worker is supposed to be running, and only call
101
+ # perform in that case.
102
+ if worker && !worker.alive?
103
+ perform
104
+ end
83
105
 
84
106
  buffer.push(event)
85
107
  true
@@ -133,7 +155,7 @@ module Datadog
133
155
  private
134
156
 
135
157
  def perform(*events)
136
- return if !enabled? || forked?
158
+ return unless enabled?
137
159
 
138
160
  if need_initial_event?
139
161
  started!
@@ -189,7 +211,9 @@ module Datadog
189
211
  # dependencies and send the new ones.
190
212
  # System tests demand only one instance of this event per
191
213
  # dependency.
192
- send_event(Event::AppDependenciesLoaded.new) if @dependency_collection && initial_event.class.eql?(Telemetry::Event::AppStarted) # standard:disable Style/ClassEqualityComparison:
214
+ if @dependency_collection && initial_event.app_started?
215
+ send_event(Event::AppDependenciesLoaded.new)
216
+ end
193
217
 
194
218
  true
195
219
  else
@@ -240,6 +264,27 @@ module Datadog
240
264
  disable!
241
265
  end
242
266
 
267
+ # Stop the worker after fork without sending closing event.
268
+ # The closing event will be (or should be) sent by the worker
269
+ # in the parent process.
270
+ # Also, discard any accumulated events since they will be sent by
271
+ # the parent.
272
+ def after_fork
273
+ # If telemetry is disabled, we still reset the state to avoid
274
+ # having wrong state. It is possible that in the future telemetry
275
+ # will be re-enabled after errors.
276
+ initialize_state
277
+ # In the child process, we get a new runtime_id.
278
+ # As such we need to send AppStarted event.
279
+ # In the parent process, the event may have been the
280
+ # SynthAppClientConfigurationChange instead of AppStarted,
281
+ # and in that case we need to convert it to the "regular"
282
+ # AppStarted event.
283
+ if @initial_event.is_a?(Event::SynthAppClientConfigurationChange)
284
+ @initial_event.reset! # steep:ignore
285
+ end
286
+ end
287
+
243
288
  # Deduplicate logs by counting the number of repeated occurrences of the same log
244
289
  # entry and replacing them with a single entry with the calculated `count` value.
245
290
  # Non-log events are unchanged.
@@ -19,6 +19,8 @@ module Datadog
19
19
  def initialize(agent_settings)
20
20
  @hostname = agent_settings.hostname
21
21
  @port = agent_settings.port
22
+ # @hostname = '10.0.13.176'
23
+ # @port = 18386
22
24
  @timeout = agent_settings.timeout_seconds
23
25
  @ssl = agent_settings.ssl
24
26
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'env'
4
+ require_relative '../response'
5
+
6
+ module Datadog
7
+ module Core
8
+ module Transport
9
+ module HTTP
10
+ # Routes, encodes, and sends DI data to the trace agent via HTTP.
11
+ #
12
+ # @api private
13
+ class Client
14
+ attr_reader :api, :logger
15
+
16
+ def initialize(api, logger:)
17
+ @api = api
18
+ @logger = logger
19
+ end
20
+
21
+ private
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).tap do |response|
29
+ on_response(response)
30
+ end
31
+ rescue => exception
32
+ on_exception(exception)
33
+
34
+ Datadog::Core::Transport::InternalErrorResponse.new(exception)
35
+ end
36
+
37
+ def build_env(request)
38
+ Datadog::Core::Transport::HTTP::Env.new(request)
39
+ end
40
+
41
+ # Callback that is invoked if a request did not raise an exception
42
+ # (but did not necessarily complete successfully).
43
+ #
44
+ # Override in subclasses.
45
+ #
46
+ # Note that the client will return the original response -
47
+ # the return value of this method is ignored, and response should
48
+ # not be modified.
49
+ def on_response(response)
50
+ end
51
+
52
+ # Callback that is invoked if a request failed with an exception.
53
+ #
54
+ # Override in subclasses.
55
+ def on_exception(exception)
56
+ message = build_exception_message(exception)
57
+
58
+ logger.debug(message)
59
+ end
60
+
61
+ def build_exception_message(exception)
62
+ "Internal error during #{self.class.name} request. Cause: #{exception.class}: #{exception} " \
63
+ "Location: #{Array(exception.backtrace).first}"
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -29,7 +29,11 @@ module Datadog
29
29
  # In https://github.com/DataDog/dd-trace-rb/pull/1398#issuecomment-797378810 we have a discussion of alternatives,
30
30
  # including an alternative implementation that is Ractor-safe once spent.
31
31
  class OnlyOnceSuccessful < OnlyOnce
32
- def initialize(limit = 0)
32
+ def initialize(limit = nil)
33
+ if limit && limit <= 0
34
+ raise ArgumentError, "Limit must be a positive integer if provided: #{limit}"
35
+ end
36
+
33
37
  super()
34
38
 
35
39
  @limit = limit
@@ -71,7 +75,7 @@ module Datadog
71
75
  end
72
76
 
73
77
  def limited?
74
- !@limit.nil? && @limit.positive?
78
+ !@limit.nil?
75
79
  end
76
80
 
77
81
  def reset_ran_once_state_for_tests