datadog 2.23.0 → 2.25.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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -1
  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/ai_guard/api_client.rb +82 -0
  10. data/lib/datadog/ai_guard/component.rb +42 -0
  11. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  12. data/lib/datadog/ai_guard/configuration/settings.rb +98 -0
  13. data/lib/datadog/ai_guard/configuration.rb +11 -0
  14. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  15. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +34 -0
  16. data/lib/datadog/ai_guard/evaluation/request.rb +81 -0
  17. data/lib/datadog/ai_guard/evaluation/result.rb +43 -0
  18. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  19. data/lib/datadog/ai_guard/evaluation.rb +72 -0
  20. data/lib/datadog/ai_guard/ext.rb +16 -0
  21. data/lib/datadog/ai_guard.rb +153 -0
  22. data/lib/datadog/appsec/context.rb +2 -1
  23. data/lib/datadog/appsec/remote.rb +5 -12
  24. data/lib/datadog/appsec/security_engine/engine.rb +3 -3
  25. data/lib/datadog/appsec/security_engine/result.rb +2 -1
  26. data/lib/datadog/appsec/security_engine/runner.rb +2 -2
  27. data/lib/datadog/core/configuration/components.rb +6 -0
  28. data/lib/datadog/core/configuration/config_helper.rb +1 -1
  29. data/lib/datadog/core/configuration/deprecations.rb +2 -2
  30. data/lib/datadog/core/configuration/option_definition.rb +4 -2
  31. data/lib/datadog/core/configuration/options.rb +8 -5
  32. data/lib/datadog/core/configuration/settings.rb +14 -3
  33. data/lib/datadog/core/configuration/supported_configurations.rb +8 -1
  34. data/lib/datadog/core/environment/cgroup.rb +52 -25
  35. data/lib/datadog/core/environment/container.rb +140 -46
  36. data/lib/datadog/core/environment/ext.rb +1 -0
  37. data/lib/datadog/core/environment/process.rb +9 -1
  38. data/lib/datadog/core/error.rb +6 -6
  39. data/lib/datadog/core/pin.rb +4 -0
  40. data/lib/datadog/core/rate_limiter.rb +9 -1
  41. data/lib/datadog/core/remote/client.rb +14 -6
  42. data/lib/datadog/core/remote/component.rb +6 -4
  43. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  44. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  45. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  46. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  47. data/lib/datadog/core/remote/transport/config.rb +3 -16
  48. data/lib/datadog/core/remote/transport/http/config.rb +4 -44
  49. data/lib/datadog/core/remote/transport/http/negotiation.rb +0 -39
  50. data/lib/datadog/core/remote/transport/http.rb +13 -24
  51. data/lib/datadog/core/remote/transport/negotiation.rb +7 -16
  52. data/lib/datadog/core/semaphore.rb +1 -4
  53. data/lib/datadog/core/telemetry/component.rb +52 -13
  54. data/lib/datadog/core/telemetry/event/app_started.rb +36 -1
  55. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  56. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  57. data/lib/datadog/core/telemetry/request.rb +17 -3
  58. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +2 -32
  59. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  60. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -10
  61. data/lib/datadog/core/telemetry/worker.rb +88 -32
  62. data/lib/datadog/core/transport/ext.rb +2 -0
  63. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  64. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  65. data/lib/datadog/core/transport/http/builder.rb +9 -5
  66. data/lib/datadog/core/transport/http/client.rb +19 -8
  67. data/lib/datadog/core/transport/http.rb +22 -19
  68. data/lib/datadog/core/transport/response.rb +12 -1
  69. data/lib/datadog/core/transport/transport.rb +90 -0
  70. data/lib/datadog/core/utils/only_once_successful.rb +2 -0
  71. data/lib/datadog/core/utils/safe_dup.rb +2 -2
  72. data/lib/datadog/core/utils/sequence.rb +2 -0
  73. data/lib/datadog/core/utils/time.rb +1 -1
  74. data/lib/datadog/core/workers/async.rb +10 -1
  75. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  76. data/lib/datadog/core/workers/polling.rb +2 -0
  77. data/lib/datadog/core/workers/queue.rb +100 -1
  78. data/lib/datadog/data_streams/processor.rb +1 -1
  79. data/lib/datadog/data_streams/transport/http/stats.rb +1 -36
  80. data/lib/datadog/data_streams/transport/http.rb +5 -6
  81. data/lib/datadog/data_streams/transport/stats.rb +3 -17
  82. data/lib/datadog/di/boot.rb +4 -2
  83. data/lib/datadog/di/contrib/active_record.rb +30 -5
  84. data/lib/datadog/di/el/compiler.rb +8 -4
  85. data/lib/datadog/di/error.rb +5 -0
  86. data/lib/datadog/di/instrumenter.rb +26 -7
  87. data/lib/datadog/di/logger.rb +2 -2
  88. data/lib/datadog/di/probe_builder.rb +2 -1
  89. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  90. data/lib/datadog/di/probe_manager.rb +37 -31
  91. data/lib/datadog/di/probe_notification_builder.rb +15 -2
  92. data/lib/datadog/di/probe_notifier_worker.rb +5 -5
  93. data/lib/datadog/di/remote.rb +89 -84
  94. data/lib/datadog/di/transport/diagnostics.rb +7 -35
  95. data/lib/datadog/di/transport/http/diagnostics.rb +1 -31
  96. data/lib/datadog/di/transport/http/input.rb +1 -31
  97. data/lib/datadog/di/transport/http.rb +28 -17
  98. data/lib/datadog/di/transport/input.rb +7 -34
  99. data/lib/datadog/di.rb +61 -5
  100. data/lib/datadog/error_tracking/filters.rb +2 -2
  101. data/lib/datadog/kit/appsec/events/v2.rb +2 -3
  102. data/lib/datadog/open_feature/evaluation_engine.rb +2 -1
  103. data/lib/datadog/open_feature/remote.rb +3 -10
  104. data/lib/datadog/open_feature/transport.rb +9 -11
  105. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  106. data/lib/datadog/opentelemetry/configuration/settings.rb +2 -2
  107. data/lib/datadog/opentelemetry/metrics.rb +21 -14
  108. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +5 -8
  109. data/lib/datadog/profiling/collectors/code_provenance.rb +27 -2
  110. data/lib/datadog/profiling/collectors/info.rb +5 -4
  111. data/lib/datadog/profiling/component.rb +12 -11
  112. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +18 -0
  113. data/lib/datadog/profiling/http_transport.rb +4 -1
  114. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  115. data/lib/datadog/tracing/contrib/karafka/patcher.rb +31 -32
  116. data/lib/datadog/tracing/contrib/status_range_matcher.rb +2 -1
  117. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  118. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +6 -3
  119. data/lib/datadog/tracing/contrib/waterdrop.rb +4 -0
  120. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  121. data/lib/datadog/tracing/distributed/baggage.rb +3 -2
  122. data/lib/datadog/tracing/remote.rb +1 -9
  123. data/lib/datadog/tracing/sampling/priority_sampler.rb +3 -1
  124. data/lib/datadog/tracing/span.rb +1 -1
  125. data/lib/datadog/tracing/span_event.rb +2 -2
  126. data/lib/datadog/tracing/span_operation.rb +20 -9
  127. data/lib/datadog/tracing/trace_operation.rb +44 -6
  128. data/lib/datadog/tracing/tracer.rb +42 -16
  129. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  130. data/lib/datadog/tracing/transport/http.rb +15 -9
  131. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  132. data/lib/datadog/tracing/transport/traces.rb +6 -66
  133. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  134. data/lib/datadog/tracing/writer.rb +1 -0
  135. data/lib/datadog/version.rb +2 -2
  136. data/lib/datadog.rb +1 -0
  137. metadata +24 -17
  138. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  139. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  140. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  141. data/lib/datadog/data_streams/transport/http/api.rb +0 -33
  142. data/lib/datadog/data_streams/transport/http/client.rb +0 -21
  143. data/lib/datadog/di/transport/http/api.rb +0 -42
  144. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  145. data/lib/datadog/tracing/transport/http/api.rb +0 -44
@@ -11,9 +11,11 @@ module Datadog
11
11
  module Core
12
12
  module Remote
13
13
  # Configures the HTTP transport to communicate with the agent
14
- # to fetch and sync the remote configuration
14
+ # to fetch and sync the remote configuration.
15
+ #
16
+ # @api private
15
17
  class Component
16
- attr_reader :logger, :client, :healthy
18
+ attr_reader :logger, :client, :healthy, :worker
17
19
 
18
20
  def initialize(settings, capabilities, agent_settings, logger:)
19
21
  @logger = logger
@@ -23,7 +25,7 @@ module Datadog
23
25
 
24
26
  @barrier = Barrier.new(settings.remote.boot_timeout_seconds)
25
27
 
26
- @client = Client.new(transport_v7, capabilities, logger: logger)
28
+ @client = Client.new(transport_v7, capabilities, settings: settings, logger: logger)
27
29
  @healthy = false
28
30
  logger.debug { "new remote configuration client: #{@client.id}" }
29
31
 
@@ -55,7 +57,7 @@ module Datadog
55
57
  end
56
58
 
57
59
  # client state is unknown, state might be corrupted
58
- @client = Client.new(transport_v7, capabilities, logger: logger)
60
+ @client = Client.new(transport_v7, capabilities, settings: settings, logger: logger)
59
61
  @healthy = false
60
62
  logger.debug { "new remote configuration client: #{@client.id}" }
61
63
 
@@ -22,6 +22,18 @@ module Datadog
22
22
  attr_accessor :version
23
23
 
24
24
  def initialize(path:, data:)
25
+ if data.nil?
26
+ # +data+ is passed to Digest calculation and also is
27
+ # unconditionally taken length of by +length+ method.
28
+ # As such, the class is not written to expect +data+ to be nil.
29
+ # Detect bad incoming values here to provide earlier diagnostics
30
+ # when developing tests, for example.
31
+ raise ArgumentError, 'data must not be nil'
32
+ end
33
+ unless String === data
34
+ raise ArgumentError, "Invalid type for data: #{data.class}: expected String"
35
+ end
36
+
25
37
  @path = path
26
38
  @data = data
27
39
  @apply_state = ApplyState::UNACKNOWLEDGED
@@ -72,8 +84,9 @@ module Datadog
72
84
  private_class_method :new
73
85
  end
74
86
 
75
- # ContentList stores a list of Conetnt instances
76
- # It provides convinient methods for finding content base on Configuration::Path and Configuration::Target
87
+ # ContentList stores a list of Content instances.
88
+ # It provides convenient methods for finding content based on
89
+ # Configuration::Path and Configuration::Target.
77
90
  class ContentList < Array
78
91
  class << self
79
92
  def parse(array)
@@ -24,10 +24,21 @@ module Datadog
24
24
  class InvalidHashTypeError < StandardError; end
25
25
  attr_reader :type, :hexdigest
26
26
 
27
- DIGEST_CHUNK = 1024
28
-
29
27
  class << self
30
28
  def hexdigest(type, data)
29
+ unless String === data
30
+ # This class (Digest) passes +data+ to the Ruby standard
31
+ # library Digest routines without validating its type.
32
+ # The stdlib Digest requires a String, and the previous
33
+ # implementation of this class that used StringIO
34
+ # unconditionally read from +data+ without validating the
35
+ # type. Meaning, passing +nil+ as +data+ has never worked.
36
+ # It still doesn't work in the present implementation.
37
+ # Flag the nil data now to get earlier diagnostics when
38
+ # developing tests for example.
39
+ raise ArgumentError, "Invalid type for data: #{data.class}: expected String"
40
+ end
41
+
31
42
  d = case type
32
43
  when :sha256
33
44
  ::Digest::SHA256.new
@@ -37,13 +48,9 @@ module Datadog
37
48
  raise InvalidHashTypeError, type
38
49
  end
39
50
 
40
- while (buf = data.read(DIGEST_CHUNK))
41
- d.update(buf)
42
- end
51
+ d.update(data)
43
52
 
44
53
  d.hexdigest
45
- ensure
46
- data.rewind
47
54
  end
48
55
  end
49
56
 
@@ -185,7 +185,7 @@ module Datadog
185
185
  end
186
186
  end
187
187
 
188
- # Update existimng repository's contents
188
+ # Update existing repository's contents
189
189
  class Update
190
190
  attr_reader :path, :target, :content
191
191
 
@@ -11,8 +11,15 @@ module Datadog
11
11
  class TargetMap < Hash
12
12
  class << self
13
13
  def parse(hash)
14
- opaque_backend_state = hash['signed']['custom']['opaque_backend_state']
15
- version = hash['signed']['version']
14
+ signed = hash.fetch('signed')
15
+ # Note that the +dig+ call permits +hash['signed']+ to be
16
+ # missing the +custom+ subtree entirely.
17
+ # Previously the subtree was required but +opaque_backend_state+
18
+ # could still be missing (and obtained here as nil).
19
+ opaque_backend_state = signed.dig('custom', 'opaque_backend_state')
20
+ # The version appears to be optional to the rest of this class,
21
+ # and we have tests that do not provide it.
22
+ version = signed['version']
16
23
 
17
24
  map = new
18
25
 
@@ -21,7 +28,7 @@ module Datadog
21
28
  @version = version
22
29
  end
23
30
 
24
- hash['signed']['targets'].each_with_object(map) do |(p, t), m|
31
+ signed.fetch('targets').each_with_object(map) do |(p, t), m|
25
32
  path = Configuration::Path.parse(p)
26
33
  target = Configuration::Target.parse(t)
27
34
 
@@ -46,9 +53,9 @@ module Datadog
46
53
  class Target
47
54
  class << self
48
55
  def parse(hash)
49
- length = Integer(hash['length'])
50
- digests = Configuration::DigestList.parse(hash['hashes'])
51
- version = Integer(hash['custom']['v'])
56
+ length = Integer(hash.fetch('length'))
57
+ digests = Configuration::DigestList.parse(hash.fetch('hashes'))
58
+ version = Integer(hash.dig('custom', 'v'))
52
59
 
53
60
  new(digests: digests, length: length, version: version)
54
61
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../../../core/transport/request'
4
4
  require_relative '../../../core/transport/parcel'
5
+ require_relative '../../../core/transport/transport'
5
6
  require_relative 'http/config'
6
7
 
7
8
  module Datadog
@@ -23,27 +24,13 @@ module Datadog
23
24
  end
24
25
 
25
26
  # Config transport
26
- class Transport
27
- attr_reader :client, :apis, :default_api, :current_api_id, :logger
28
-
29
- def initialize(apis, default_api, logger: Datadog.logger)
30
- @apis = apis
31
- @logger = logger
32
-
33
- @client = Remote::Transport::HTTP::Config::Client.new(current_api, logger: logger)
34
- end
35
-
36
- ##### there is only one transport! it's negotiation!
27
+ class Transport < Core::Transport::Transport
37
28
  def send_config(payload)
38
29
  json = JSON.dump(payload)
39
30
  parcel = EncodedParcel.new(json)
40
31
  request = Request.new(parcel)
41
32
 
42
- @client.send_config_payload(request)
43
- end
44
-
45
- def current_api
46
- @apis[HTTP::API::V7]
33
+ @client.send_request(:config, request)
47
34
  end
48
35
  end
49
36
  end
@@ -2,11 +2,10 @@
2
2
 
3
3
  require 'json'
4
4
 
5
- require_relative '../../../transport/http/client'
5
+ require_relative '../../../transport/http/api/endpoint'
6
+ require_relative '../../../transport/http/response'
6
7
  require_relative '../../../utils/base64'
7
8
  require_relative '../../../utils/truncation'
8
- require_relative '../../../transport/http/response'
9
- require_relative '../../../transport/http/api/endpoint'
10
9
 
11
10
  module Datadog
12
11
  module Core
@@ -17,7 +16,7 @@ module Datadog
17
16
  module Config
18
17
  # Response from HTTP transport for remote configuration
19
18
  class Response
20
- include Datadog::Core::Transport::HTTP::Response
19
+ include Core::Transport::HTTP::Response
21
20
 
22
21
  def initialize(http_response, options = {}) # standard:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
23
22
  super(http_response)
@@ -101,7 +100,7 @@ module Datadog
101
100
 
102
101
  {
103
102
  path: h[:path].freeze,
104
- content: StringIO.new(content.freeze),
103
+ content: content.freeze,
105
104
  }
106
105
  end.freeze
107
106
 
@@ -178,46 +177,7 @@ module Datadog
178
177
  end
179
178
  end
180
179
 
181
- # Remote transport HTTP client
182
- class Client < Core::Transport::HTTP::Client
183
- def send_config_payload(request)
184
- send_request(request) do |api, env|
185
- api.send_config(env)
186
- end
187
- end
188
- end
189
-
190
180
  module API
191
- # Extensions for HTTP API Spec
192
- module Spec
193
- attr_reader :config
194
-
195
- def config=(endpoint)
196
- @config = endpoint
197
- end
198
-
199
- def send_config(env, &block)
200
- raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('config', self) if config.nil?
201
-
202
- config.call(env, &block)
203
- end
204
- end
205
-
206
- # Extensions for HTTP API Instance
207
- module Instance
208
- def send_config(env)
209
- unless spec.is_a?(Config::API::Spec)
210
- raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new(
211
- 'config', self
212
- )
213
- end
214
-
215
- spec.send_config(env) do |request_env|
216
- call(request_env)
217
- end
218
- end
219
- end
220
-
221
181
  # Endpoint for remote configuration
222
182
  class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
223
183
  HEADER_CONTENT_TYPE = 'Content-Type'
@@ -43,46 +43,7 @@ module Datadog
43
43
  attr_reader :version, :endpoints, :config, :span_events
44
44
  end
45
45
 
46
- # Remote negotiation HTTP client
47
- class Client < Core::Transport::HTTP::Client
48
- def send_info_payload(request)
49
- send_request(request) do |api, env|
50
- api.send_info(env)
51
- end
52
- end
53
- end
54
-
55
46
  module API
56
- # Extensions for HTTP API Spec
57
- module Spec
58
- attr_reader :info
59
-
60
- def info=(endpoint)
61
- @info = endpoint
62
- end
63
-
64
- def send_info(env, &block)
65
- raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('info', self) if info.nil?
66
-
67
- info.call(env, &block)
68
- end
69
- end
70
-
71
- # Extensions for HTTP API Instance
72
- module Instance
73
- def send_info(env)
74
- unless spec.is_a?(Negotiation::API::Spec)
75
- raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new(
76
- 'info', self
77
- )
78
- end
79
-
80
- spec.send_info(env) do |request_env|
81
- call(request_env)
82
- end
83
- end
84
- end
85
-
86
47
  # Endpoint for negotiation
87
48
  class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
88
49
  def initialize(path)
@@ -7,16 +7,6 @@ require_relative '../../transport/http'
7
7
  require_relative 'config'
8
8
  require_relative 'negotiation'
9
9
 
10
- # TODO: Improve negotiation to allow per endpoint selection
11
- #
12
- # Since endpoint negotiation happens at the `API::Spec` level there can not be
13
- # a mix of endpoints at various versions or versionless without describing all
14
- # the possible combinations as specs. See http/api.
15
- #
16
- # Below should be:
17
- # require_relative '../../transport/http/api'
18
- require_relative 'http/api'
19
-
20
10
  # TODO: Decouple transport/http
21
11
  #
22
12
  # Because a new transport is required for every (API, Client, Transport)
@@ -29,26 +19,30 @@ module Datadog
29
19
  module Transport
30
20
  # Namespace for HTTP transport components
31
21
  module HTTP
22
+ ROOT = Negotiation::API::Endpoint.new(
23
+ '/info',
24
+ )
25
+
26
+ V7 = Config::API::Endpoint.new(
27
+ '/v0.7/config',
28
+ Core::Encoding::JSONEncoder,
29
+ )
30
+
32
31
  module_function
33
32
 
34
33
  # Builds a new Transport::HTTP::Client with default settings
35
34
  # Pass a block to override any settings.
36
35
  def root(
37
36
  agent_settings:,
38
- logger: Datadog.logger,
39
- api_version: nil,
37
+ logger:,
40
38
  headers: nil
41
39
  )
42
40
  Core::Transport::HTTP.build(
43
- api_instance_class: API::Instance,
44
41
  agent_settings: agent_settings,
45
42
  logger: logger,
46
- api_version: api_version,
47
43
  headers: headers
48
44
  ) do |transport|
49
- apis = API.defaults
50
-
51
- transport.api API::ROOT, apis[API::ROOT]
45
+ transport.api 'root', ROOT
52
46
 
53
47
  # Call block to apply any customization, if provided
54
48
  yield(transport) if block_given?
@@ -59,20 +53,15 @@ module Datadog
59
53
  # Pass a block to override any settings.
60
54
  def v7(
61
55
  agent_settings:,
62
- logger: Datadog.logger,
63
- api_version: nil,
56
+ logger:,
64
57
  headers: nil
65
58
  )
66
59
  Core::Transport::HTTP.build(
67
- api_instance_class: API::Instance,
68
60
  agent_settings: agent_settings,
69
61
  logger: logger,
70
- api_version: api_version,
71
62
  headers: headers
72
63
  ) do |transport|
73
- apis = API.defaults
74
-
75
- transport.api API::V7, apis[API::V7]
64
+ transport.api 'v7', V7
76
65
 
77
66
  # Call block to apply any customization, if provided
78
67
  yield(transport) if block_given?
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../../core/transport/request'
4
+ require_relative '../../../core/transport/transport'
4
5
  require_relative 'http/negotiation'
5
6
 
6
7
  # TODO: Resolve conceptual conundrum
@@ -32,24 +33,14 @@ module Datadog
32
33
  end
33
34
 
34
35
  # Negotiation transport
35
- class Transport
36
- attr_reader :client, :apis, :default_api, :current_api_id, :logger
37
-
38
- def initialize(apis, default_api, logger: Datadog.logger)
39
- @apis = apis
40
- @logger = logger
41
-
42
- @client = Remote::Transport::HTTP::Negotiation::Client.new(current_api, logger: logger)
43
- end
44
-
45
- def send_info
36
+ class Transport < Core::Transport::Transport
37
+ # TODO steep is complaining about this method, I tried to make
38
+ # it work but between module inclusions and steep diagnostics
39
+ # I don't understand what the problem is or what the steep wants.
40
+ def send_info # steep:ignore
46
41
  request = Request.new
47
42
 
48
- @client.send_info_payload(request)
49
- end
50
-
51
- def current_api
52
- @apis[HTTP::API::ROOT]
43
+ @client.send_request(:info, request)
53
44
  end
54
45
  end
55
46
  end
@@ -20,10 +20,7 @@ module Datadog
20
20
 
21
21
  def wait(timeout = nil)
22
22
  wake_lock.synchronize do
23
- # steep specifies that the second argument to wait is of type
24
- # ::Time::_Timeout which for some reason is not Numeric and is not
25
- # castable from Numeric.
26
- wake.wait(wake_lock, timeout) # steep:ignore
23
+ wake.wait(wake_lock, timeout)
27
24
  end
28
25
  end
29
26
 
@@ -14,14 +14,26 @@ 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
+ ONLY_ONCE = Utils::OnlyOnce.new
25
+
26
+ attr_reader :enabled
27
+ attr_reader :logger
28
+ attr_reader :transport
29
+ attr_reader :worker
30
+ attr_reader :settings
31
+ attr_reader :agent_settings
32
+ attr_reader :metrics_manager
33
+
34
+ # Alias for consistency with other components.
35
+ # TODO Remove +enabled+ method
36
+ alias_method :enabled?, :enabled
25
37
 
26
38
  include Core::Utils::Forking
27
39
  include Telemetry::Logging
@@ -50,6 +62,17 @@ module Datadog
50
62
  logger:,
51
63
  enabled:
52
64
  )
65
+ ONLY_ONCE.run do
66
+ Utils::AtForkMonkeyPatch.apply!
67
+
68
+ # All of the other at fork monkey patch callbacks reference
69
+ # globals, follow that pattern here to avoid having the component
70
+ # referenced via the at fork callbacks.
71
+ Datadog::Core::Utils::AtForkMonkeyPatch.at_fork(:child) do
72
+ Datadog.send(:components, allow_initialization: false)&.telemetry&.after_fork
73
+ end
74
+ end
75
+
53
76
  @enabled = enabled
54
77
  @log_collection_enabled = settings.telemetry.log_collection_enabled
55
78
  @logger = logger
@@ -110,7 +133,7 @@ module Datadog
110
133
  end
111
134
 
112
135
  def start(initial_event_is_change = false, components:)
113
- return if !@enabled
136
+ return unless enabled?
114
137
 
115
138
  initial_event = if initial_event_is_change
116
139
  Event::SynthAppClientConfigurationChange.new(
@@ -136,44 +159,44 @@ module Datadog
136
159
  end
137
160
 
138
161
  def emit_closing!
139
- return if !@enabled || forked?
162
+ return unless enabled?
140
163
 
141
164
  @worker.enqueue(Event::AppClosing.new)
142
165
  end
143
166
 
144
167
  def integrations_change!
145
- return if !@enabled || forked?
168
+ return unless enabled?
146
169
 
147
170
  @worker.enqueue(Event::AppIntegrationsChange.new)
148
171
  end
149
172
 
150
173
  def log!(event)
151
- return if !@enabled || forked? || !@log_collection_enabled
174
+ return unless enabled? && @log_collection_enabled
152
175
 
153
176
  @worker.enqueue(event)
154
177
  end
155
178
 
156
179
  # Wait for the worker to send out all events that have already
157
180
  # been queued, up to 15 seconds. Returns whether all events have
158
- # been flushed.
181
+ # been flushed, or nil if telemetry is disabled.
159
182
  #
160
183
  # @api private
161
- def flush
162
- return if !@enabled || forked?
184
+ def flush(timeout: nil)
185
+ return unless enabled?
163
186
 
164
- @worker.flush
187
+ @worker.flush(timeout: timeout)
165
188
  end
166
189
 
167
190
  # Report configuration changes caused by Remote Configuration.
168
191
  def client_configuration_change!(changes)
169
- return if !@enabled || forked?
192
+ return unless enabled?
170
193
 
171
194
  @worker.enqueue(Event::AppClientConfigurationChange.new(changes, 'remote_config'))
172
195
  end
173
196
 
174
197
  # Report application endpoints
175
198
  def app_endpoints_loaded(endpoints, page_size: ENDPOINT_COLLECTION_MESSAGE_LIMIT)
176
- return if !@enabled || forked?
199
+ return unless enabled?
177
200
 
178
201
  endpoints.each_slice(page_size).with_index do |endpoints_slice, i|
179
202
  @worker.enqueue(Event::AppEndpointsLoaded.new(endpoints_slice, is_first: i.zero?))
@@ -204,6 +227,22 @@ module Datadog
204
227
  def distribution(namespace, metric_name, value, tags: {}, common: true)
205
228
  @metrics_manager.distribution(namespace, metric_name, value, tags: tags, common: common)
206
229
  end
230
+
231
+ # When a fork happens, we generally need to do two things inside the
232
+ # child proess:
233
+ # 1. Restart the worker.
234
+ # 2. Discard any events and metrics that were submitted in the
235
+ # parent process (because they will be sent out in the parent
236
+ # process, sending them in the child would cause duplicate
237
+ # submission).
238
+ def after_fork
239
+ # We cannot simply create a new instance of metrics manager because
240
+ # it is referenced from other objects (e.g. the worker).
241
+ # We must reset the existing instance.
242
+ @metrics_manager.clear
243
+
244
+ worker&.send(:after_fork_monkey_patched)
245
+ end
207
246
  end
208
247
  end
209
248
  end
@@ -11,6 +11,12 @@ module Datadog
11
11
  def initialize(components:)
12
12
  # To not hold a reference to the component tree, generate
13
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.
14
20
  @configuration = configuration(components.settings, components.agent_settings)
15
21
  @install_signature = install_signature(components.settings)
16
22
  @products = products(components)
@@ -30,10 +36,19 @@ module Datadog
30
36
  }
31
37
  end
32
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
+
33
48
  private
34
49
 
35
50
  def products(components)
36
- # @type var products: Hash[Symbol, Hash[Symbol, Hash[Symbol, String | Integer] | bool | nil]]
51
+ # @type var products: telemetry_products
37
52
  products = {
38
53
  appsec: {
39
54
  # TODO take appsec status out of component tree?
@@ -159,6 +174,25 @@ module Datadog
159
174
  get_telemetry_origin(settings, 'tracing.contrib.peer_service_mapping')
160
175
  )
161
176
 
177
+ # OpenTelemetry configuration options (using environment variable names)
178
+ otel_exporter_headers_string = settings.opentelemetry.exporter.headers&.map { |key, value| "#{key}=#{value}" }&.join(',')
179
+ otel_exporter_metrics_headers_string = settings.opentelemetry.metrics.headers&.map { |key, value| "#{key}=#{value}" }&.join(',')
180
+ list.push(
181
+ conf_value('OTEL_EXPORTER_OTLP_ENDPOINT', settings.opentelemetry.exporter.endpoint, seq_id, get_telemetry_origin(settings, 'opentelemetry.exporter.endpoint')),
182
+ conf_value('OTEL_EXPORTER_OTLP_HEADERS', otel_exporter_headers_string, seq_id, get_telemetry_origin(settings, 'opentelemetry.exporter.headers')),
183
+ conf_value('OTEL_EXPORTER_OTLP_PROTOCOL', settings.opentelemetry.exporter.protocol, seq_id, get_telemetry_origin(settings, 'opentelemetry.exporter.protocol')),
184
+ conf_value('OTEL_EXPORTER_OTLP_TIMEOUT', settings.opentelemetry.exporter.timeout_millis, seq_id, get_telemetry_origin(settings, 'opentelemetry.exporter.timeout_millis')),
185
+ conf_value('DD_METRICS_OTEL_ENABLED', settings.opentelemetry.metrics.enabled, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.enabled')),
186
+ conf_value('OTEL_METRICS_EXPORTER', settings.opentelemetry.metrics.exporter, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.exporter')),
187
+ conf_value('OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', settings.opentelemetry.metrics.endpoint, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.endpoint')),
188
+ conf_value('OTEL_EXPORTER_OTLP_METRICS_HEADERS', otel_exporter_metrics_headers_string, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.headers')),
189
+ conf_value('OTEL_EXPORTER_OTLP_METRICS_PROTOCOL', settings.opentelemetry.metrics.protocol, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.protocol')),
190
+ conf_value('OTEL_EXPORTER_OTLP_METRICS_TIMEOUT', settings.opentelemetry.metrics.timeout_millis, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.timeout_millis')),
191
+ conf_value('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE', settings.opentelemetry.metrics.temporality_preference, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.temporality_preference')),
192
+ conf_value('OTEL_METRIC_EXPORT_INTERVAL', settings.opentelemetry.metrics.export_interval_millis, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.export_interval_millis')),
193
+ conf_value('OTEL_METRIC_EXPORT_TIMEOUT', settings.opentelemetry.metrics.export_timeout_millis, seq_id, get_telemetry_origin(settings, 'opentelemetry.metrics.export_timeout_millis')),
194
+ )
195
+
162
196
  # Whitelist of configuration options to send in additional payload object
163
197
  TARGET_OPTIONS.each do |option_path|
164
198
  split_option = option_path.split('.')
@@ -243,6 +277,7 @@ module Datadog
243
277
  # - `default`: set when the user has not set any configuration for the key (defaults to a value)
244
278
  # - `unknown`: set for cases where it is difficult/not possible to determine the source of a config.
245
279
  def conf_value(name, value, seq_id, origin)
280
+ # @type var result: telemetry_configuration
246
281
  result = {name: name, value: value, origin: origin, seq_id: seq_id}
247
282
  if origin == 'fleet_stable_config'
248
283
  fleet_id = Core::Configuration::StableConfig.configuration.dig(:fleet, :id)