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
@@ -12,16 +12,16 @@ module Datadog
12
12
  module HTTP
13
13
  # Add adapters to registry
14
14
  Builder::REGISTRY.set(
15
- Transport::HTTP::Adapters::Net,
15
+ Core::Transport::HTTP::Adapters::Net,
16
16
  Core::Configuration::Ext::Agent::HTTP::ADAPTER
17
17
  )
18
18
  Builder::REGISTRY.set(
19
- Transport::HTTP::Adapters::Test,
20
- Transport::Ext::Test::ADAPTER
19
+ Core::Transport::HTTP::Adapters::Test,
20
+ Core::Transport::Ext::Test::ADAPTER
21
21
  )
22
22
  Builder::REGISTRY.set(
23
- Transport::HTTP::Adapters::UnixSocket,
24
- Transport::Ext::UnixSocket::ADAPTER
23
+ Core::Transport::HTTP::Adapters::UnixSocket,
24
+ Core::Transport::Ext::UnixSocket::ADAPTER
25
25
  )
26
26
 
27
27
  module_function
@@ -29,8 +29,13 @@ module Datadog
29
29
  # Helper function that delegates to Builder.new
30
30
  # but is under HTTP namespace so that client code requires this file
31
31
  # to get the adapters configured, and not the builder directly.
32
- def build(api_instance_class:, agent_settings:, logger: Datadog.logger, api_version: nil, headers: nil, &block)
33
- Builder.new(api_instance_class: api_instance_class, logger: logger) do |transport|
32
+ def build(
33
+ agent_settings:,
34
+ logger: Datadog.logger,
35
+ headers: nil,
36
+ &block
37
+ )
38
+ Builder.new(logger: logger) do |transport|
34
39
  transport.adapter(agent_settings)
35
40
  transport.headers(default_headers)
36
41
 
@@ -38,34 +43,32 @@ module Datadog
38
43
  yield transport
39
44
 
40
45
  # Apply any settings given by options
41
- transport.default_api = api_version if api_version
42
46
  transport.headers(headers) if headers
43
47
  end
44
48
  end
45
49
 
46
50
  def default_headers
47
51
  {
48
- Datadog::Core::Transport::Ext::HTTP::HEADER_CLIENT_COMPUTED_TOP_LEVEL => '1',
49
- Datadog::Core::Transport::Ext::HTTP::HEADER_META_LANG =>
52
+ Core::Transport::Ext::HTTP::HEADER_CLIENT_COMPUTED_TOP_LEVEL => '1',
53
+ Core::Transport::Ext::HTTP::HEADER_META_LANG =>
50
54
  Datadog::Core::Environment::Ext::LANG,
51
- Datadog::Core::Transport::Ext::HTTP::HEADER_META_LANG_VERSION =>
55
+ Core::Transport::Ext::HTTP::HEADER_META_LANG_VERSION =>
52
56
  Datadog::Core::Environment::Ext::LANG_VERSION,
53
- Datadog::Core::Transport::Ext::HTTP::HEADER_META_LANG_INTERPRETER =>
57
+ Core::Transport::Ext::HTTP::HEADER_META_LANG_INTERPRETER =>
54
58
  Datadog::Core::Environment::Ext::LANG_INTERPRETER,
55
- Datadog::Core::Transport::Ext::HTTP::HEADER_META_LANG_INTERPRETER_VENDOR =>
59
+ Core::Transport::Ext::HTTP::HEADER_META_LANG_INTERPRETER_VENDOR =>
56
60
  Core::Environment::Ext::LANG_ENGINE,
57
- Datadog::Core::Transport::Ext::HTTP::HEADER_META_TRACER_VERSION =>
61
+ Core::Transport::Ext::HTTP::HEADER_META_TRACER_VERSION =>
58
62
  Datadog::Core::Environment::Ext::GEM_DATADOG_VERSION
59
63
  }.tap do |headers|
60
- # Add container ID, if present.
61
- if (container_id = Datadog::Core::Environment::Container.container_id)
62
- headers[Datadog::Core::Transport::Ext::HTTP::HEADER_CONTAINER_ID] = container_id
63
- end
64
+ # Add application container info
65
+ headers.merge!(Core::Environment::Container.to_headers)
66
+
64
67
  # TODO: inject configuration rather than reading from global here
65
68
  unless Datadog.configuration.apm.tracing.enabled
66
69
  # Sending this header to the agent will disable metrics computation (and billing) on the agent side
67
70
  # by pretending it has already been done on the library side.
68
- headers[Datadog::Core::Transport::Ext::HTTP::HEADER_CLIENT_COMPUTED_STATS] = 'yes'
71
+ headers[Core::Transport::Ext::HTTP::HEADER_CLIENT_COMPUTED_STATS] = 'yes'
69
72
  end
70
73
  end
71
74
  end
@@ -35,7 +35,18 @@ module Datadog
35
35
 
36
36
  def inspect
37
37
  maybe_code = if respond_to?(:code)
38
- " code:#{code}," # steep:ignore
38
+ # Steep: `code` method may be defined by classes extending this module.
39
+ # There seem to be no annotation for this.
40
+ " code:#{code}," # steep:ignore NoMethod
41
+ end
42
+ payload = self.payload
43
+ # Truncation thresholds are arbitrary but we need to truncate the
44
+ # payload here because outputting multi-MB request body to the
45
+ # log is not useful.
46
+ #
47
+ # Note that payload can be nil here.
48
+ if payload && payload.length > 2000 # steep:ignore
49
+ payload = Utils::Truncation.truncate_in_middle(payload, 1500, 500) # steep:ignore
39
50
  end
40
51
  "#{self.class} ok?:#{ok?},#{maybe_code} unsupported?:#{unsupported?}, " \
41
52
  "not_found?:#{not_found?}, client_error?:#{client_error?}, " \
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'http/client'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Transport
8
+ # Raised when configured with an unknown API version
9
+ class UnknownApiVersionError < StandardError
10
+ attr_reader :version
11
+
12
+ def initialize(version)
13
+ super
14
+
15
+ @version = version
16
+ end
17
+
18
+ def message
19
+ "No matching transport API for version #{version}!"
20
+ end
21
+ end
22
+
23
+ # Raised when the API verson to downgrade to does not map to a
24
+ # defined API.
25
+ class NoDowngradeAvailableError < StandardError
26
+ attr_reader :version
27
+
28
+ def initialize(version)
29
+ super
30
+
31
+ @version = version
32
+ end
33
+
34
+ def message
35
+ "No downgrade from transport API version #{version} is available!"
36
+ end
37
+ end
38
+
39
+ # Base class for transports.
40
+ class Transport
41
+ attr_reader :client, :apis, :default_api, :current_api_id, :logger
42
+
43
+ class << self
44
+ # The HTTP client class to use for requests, derived from
45
+ # Core::Transport::HTTP::Client.
46
+ #
47
+ # Important: this attribute is NOT inherited by derived classes -
48
+ # it must be set by every Transport class that wants to have a
49
+ # non-default HTTP::Client instance.
50
+ attr_accessor :http_client_class
51
+ end
52
+
53
+ def initialize(apis, default_api, logger:)
54
+ @apis = apis
55
+ @default_api = default_api
56
+ @logger = logger
57
+
58
+ set_api!(default_api)
59
+ end
60
+
61
+ def current_api
62
+ apis[current_api_id]
63
+ end
64
+
65
+ private
66
+
67
+ def set_api!(api_id)
68
+ raise UnknownApiVersionError, api_id unless apis.key?(api_id)
69
+
70
+ @current_api_id = api_id
71
+ client_class = self.class.http_client_class || Core::Transport::HTTP::Client
72
+ @client = client_class.new(current_api, logger: logger) # steep:ignore
73
+ end
74
+
75
+ def downgrade?(response)
76
+ return false unless apis.fallbacks.key?(current_api_id)
77
+
78
+ response.not_found? || response.unsupported?
79
+ end
80
+
81
+ def downgrade!
82
+ downgrade_api_id = apis.fallbacks[current_api_id]
83
+ raise NoDowngradeAvailableError, current_api_id if downgrade_api_id.nil?
84
+
85
+ set_api!(downgrade_api_id)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -67,7 +67,9 @@ module Datadog
67
67
 
68
68
  private
69
69
 
70
+ # Use this method only after checking that limit is not nil.
70
71
  def check_limit!
72
+ # @type ivar @limit: Integer
71
73
  if @retries >= @limit
72
74
  @failed = true
73
75
  @ran_once = true
@@ -5,8 +5,8 @@ module Datadog
5
5
  module Utils
6
6
  # Helper methods for safer dup
7
7
  module SafeDup
8
- # String#+@ was introduced in Ruby 2.3
9
- def self.frozen_or_dup(v)
8
+ # Steep: https://github.com/soutaro/steep/issues/2001
9
+ def self.frozen_or_dup(v) # steep:ignore MethodBodyTypeMismatch
10
10
  # For the case of a String we use the methods +@ and -@.
11
11
  # Those methods are only for String objects
12
12
  # they are faster and chepaer on the memory side.
@@ -12,6 +12,8 @@ module Datadog
12
12
  end
13
13
 
14
14
  def next
15
+ # Steep: https://github.com/soutaro/steep/issues/477
16
+ # @type ivar @next_item: ^(::Integer) -> Integer
15
17
  next_item = @next_item ? @next_item.call(@current) : @current
16
18
  @current += 1
17
19
  next_item
@@ -10,7 +10,7 @@ module Datadog
10
10
  # Current monotonic time
11
11
  #
12
12
  # @param unit [Symbol] unit for the resulting value, same as ::Process#clock_gettime, defaults to :float_second
13
- # @return [Numeric] timestamp in the requested unit, since some unspecified starting point
13
+ # @return [Float|Integer] timestamp in the requested unit, since some unspecified starting point
14
14
  def get_time(unit = :float_second)
15
15
  Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
16
16
  end
@@ -8,6 +8,11 @@ module Datadog
8
8
  module Async
9
9
  # Adds threading behavior to workers
10
10
  # to run tasks asynchronously.
11
+ #
12
+ # This module is included in Polling module, and has no other
13
+ # direct users.
14
+ #
15
+ # @api private
11
16
  module Thread
12
17
  FORK_POLICY_STOP = :stop
13
18
  FORK_POLICY_RESTART = :restart
@@ -24,7 +29,11 @@ module Datadog
24
29
  # Methods that must be prepended
25
30
  module PrependedMethods
26
31
  def perform(*args)
27
- start_async { self.result = super(*args) } unless started?
32
+ unless started?
33
+ start_async do
34
+ self.result = super(*args)
35
+ end
36
+ end
28
37
  end
29
38
  end
30
39
 
@@ -5,6 +5,11 @@ module Datadog
5
5
  module Workers
6
6
  # Adds looping behavior to workers, with a sleep
7
7
  # interval between each loop.
8
+ #
9
+ # This module is included in Polling module, and has no other
10
+ # direct users.
11
+ #
12
+ # @api private
8
13
  module IntervalLoop
9
14
  BACK_OFF_RATIO = 1.2
10
15
  BACK_OFF_MAX = 5
@@ -38,15 +43,39 @@ module Datadog
38
43
 
39
44
  def stop_loop
40
45
  mutex.synchronize do
41
- return false unless run_loop?
42
-
46
+ # Do not call run_loop? from this method to see if the loop
47
+ # is running, because @run_loop is normally initialized by
48
+ # the background thread and if the stop is requested right
49
+ # after the worker starts, the background thread may be created
50
+ # (and scheduled) but hasn't run yet, thus skipping the
51
+ # write to @run_loop here would leave the thread running forever.
43
52
  @run_loop = false
53
+
54
+ # It is possible that we don't need to signal shutdown if
55
+ # @run_loop was not initialized (i.e. we changed it from not
56
+ # defined to false above). But let's be safe and signal the
57
+ # shutdown anyway, I don't see what harm it can cause.
44
58
  shutdown.signal
45
59
  end
46
60
 
61
+ # Previously, this method would return false (and do nothing)
62
+ # if the worker was not running the loop. However, this was racy -
63
+ # see https://github.com/DataDog/ruby-guild/issues/279.
64
+ # stop_loop now always sets the state to "stop requested" and,
65
+ # correspondingly, always returns true.
66
+ #
67
+ # There is some test code that returns false when mocking this
68
+ # method - most likely this method should be treated as a void one
69
+ # and the caller should assume that the stop was always requested.
47
70
  true
48
71
  end
49
72
 
73
+ # TODO This overwrites Queue's +work_pending?+ method with an
74
+ # implementation that, to me, is at leat questionable semantically:
75
+ # the Queue's idea of pending work is if the buffer is not empty,
76
+ # but this module says that work is pending if the work processing
77
+ # loop is scheduled to run (in other words, as long as the background
78
+ # thread is running, there is always pending work).
50
79
  def work_pending?
51
80
  run_loop?
52
81
  end
@@ -104,7 +133,19 @@ module Datadog
104
133
 
105
134
  def perform_loop
106
135
  mutex.synchronize do
107
- @run_loop = true
136
+ unless defined?(@run_loop)
137
+ # This write must only happen if @run_loop is not defined
138
+ # (i.e., not initialized). In the case when the worker is
139
+ # asked to stop right after it is created, the thread may not
140
+ # have run yet by the time +stop_loop+ is invoked and
141
+ # we need to preserve the stop-requested state from
142
+ # +stop_loop+ to +perform_loop+.
143
+ #
144
+ # If the workers are refactored to use classes and inheritance
145
+ # and their state, such as @run_loop, is initialized in
146
+ # constructors, the write can be made unconditional.
147
+ @run_loop = true
148
+ end
108
149
 
109
150
  shutdown.wait(mutex, loop_wait_time) if loop_wait_before_first_iteration?
110
151
  end
@@ -7,6 +7,8 @@ module Datadog
7
7
  module Core
8
8
  module Workers
9
9
  # Adds polling (async looping) behavior to workers
10
+ #
11
+ # @api private
10
12
  module Polling
11
13
  DEFAULT_SHUTDOWN_TIMEOUT = 1
12
14
 
@@ -5,6 +5,17 @@ module Datadog
5
5
  module Workers
6
6
  # Adds queue behavior to workers, with a buffer
7
7
  # to which items can be queued then dequeued.
8
+ #
9
+ # This module is included in some but not all workers.
10
+ # Notably, Data Streams Processor uses a queue but implements it
11
+ # inline rather than using this module.
12
+ #
13
+ # The workers that do include Queue also include Polling, which
14
+ # in turn includes Async::Thread and IntervalLoop. This means
15
+ # we have e.g. +in_iteration?+ always available in any worker
16
+ # that includes Queue.
17
+ #
18
+ # @api private
8
19
  module Queue
9
20
  def self.included(base)
10
21
  base.prepend(PrependedMethods)
@@ -13,11 +24,16 @@ module Datadog
13
24
  # Methods that must be prepended
14
25
  module PrependedMethods
15
26
  def perform(*args)
16
- super(*dequeue) if work_pending?
27
+ if work_pending?
28
+ work = dequeue
29
+ super(*work)
30
+ end
17
31
  end
18
32
  end
19
33
 
20
34
  def buffer
35
+ # Why is this an unsynchronized Array and not a Core::Buffer
36
+ # instance?
21
37
  @buffer ||= []
22
38
  end
23
39
 
@@ -34,10 +50,93 @@ module Datadog
34
50
  !buffer.empty?
35
51
  end
36
52
 
53
+ # Wait for the worker to finish handling all work that has already
54
+ # been submitted to it.
55
+ #
56
+ # If the worker is not enabled, returns nil.
57
+ # If the worker is enabled, returns whether, at the point of return,
58
+ # there was no pending or in progress work.
59
+ #
60
+ # Flushing can time out because there is a constant stream of work
61
+ # submitted at the same or higher rate than it is processed.
62
+ # Flushing can also fail if the worker thread is not running -
63
+ # this method will not flush from the calling thread.
64
+ def flush(timeout: nil)
65
+ # Default timeout is 5 seconds.
66
+ # Specific workers can override it to be more or less
67
+ timeout ||= 5
68
+
69
+ # Nothing needs to be done if the worker is not enabled.
70
+ return nil unless enabled?
71
+
72
+ unless running?
73
+ unless buffer.empty?
74
+ # If we are asked to flush but the worker is not running,
75
+ # do not flush from the caller thread. If the buffer is not
76
+ # empty, it will not be flushed. Log a warning to this effect.
77
+ #
78
+ # We are not guaranteed to have a logger as an instance method,
79
+ # reference the global for now - all other worker methods
80
+ # also reference the logger globally.
81
+ # TODO inject it into worker instances.
82
+ Datadog.logger.debug { "Asked to flush #{self} when the worker is not running" }
83
+ return false
84
+ end
85
+ end
86
+
87
+ started = Utils::Time.get_time
88
+ loop do
89
+ # The AppStarted event is triggered by the worker itself,
90
+ # from the worker thread. As such the main thread has no way
91
+ # to delay itself until that event is queued and we need some
92
+ # way to wait until that event is sent out to assert on it in
93
+ # the test suite. Check the run once flag which *should*
94
+ # indicate the event has been queued (at which point our queue
95
+ # depth check should wait until it's sent).
96
+ # This is still a hack because the flag can be overridden
97
+ # either way with or without the event being sent out.
98
+ # Note that if the AppStarted sending fails, this check
99
+ # will return false and flushing will be blocked until the
100
+ # 15 second timeout.
101
+ # Note that the first wait interval between telemetry event
102
+ # sending is 10 seconds, the timeout needs to be strictly
103
+ # greater than that.
104
+ return true if idle?
105
+
106
+ return false if Utils::Time.get_time - started > timeout
107
+
108
+ sleep 0.5
109
+ end
110
+ end
111
+
37
112
  protected
38
113
 
39
114
  attr_writer \
40
115
  :buffer
116
+
117
+ # Returns whether this worker has no pending work and is not actively
118
+ # working.
119
+ #
120
+ # The reason why "actively working" is considered is that we use
121
+ # flushing to ensure all work is completed before asserting on the
122
+ # outcome in the tests - if work is happening in a background thread,
123
+ # it's too early to assert on its results.
124
+ def idle?
125
+ # We have a +work_pending?+ method in this class that semantically
126
+ # would be appropriate here instead of calling +buffer.empty?+.
127
+ # Unfortunately IntervalLoop replaces our implementation of
128
+ # +work_pending?+ with one that doesn't make sense at least for the
129
+ # Queue. And we can't change the order of module includes because
130
+ # they all override +perform+ and the correct behavior depends on
131
+ # placing IntervalLoop after Queue.
132
+ #
133
+ # The TraceWriter worker then defines +work_pending?+ to be the
134
+ # same as Queue implementation here... Essentially, it demands
135
+ # the behavior that perhaps should be applied to all workers.
136
+ #
137
+ # Until this mess is untangled, call +buffer.empty?+ here.
138
+ buffer.empty? && !in_iteration?
139
+ end
41
140
  end
42
141
  end
43
142
  end
@@ -242,7 +242,7 @@ module Datadog
242
242
  current_context = get_current_context
243
243
  tags = tags.sort
244
244
 
245
- direction = nil
245
+ direction = nil #: ::String?
246
246
  tags.each do |tag|
247
247
  if tag.start_with?('direction:')
248
248
  direction = tag
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../stats'
4
- require_relative 'client'
5
- require_relative '../../../core/transport/http/response'
6
4
  require_relative '../../../core/transport/http/api/endpoint'
7
- require_relative '../../../core/transport/http/api/spec'
8
- require_relative '../../../core/transport/http/api/instance'
5
+ require_relative '../../../core/transport/http/response'
9
6
 
10
7
  module Datadog
11
8
  module DataStreams
@@ -23,38 +20,6 @@ module Datadog
23
20
  end
24
21
 
25
22
  module API
26
- # HTTP API Spec for DSM
27
- class Spec < Core::Transport::HTTP::API::Spec
28
- attr_accessor :stats
29
-
30
- def send_stats(env, &block)
31
- raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('stats', self) if stats.nil?
32
-
33
- stats.call(env, &block)
34
- end
35
-
36
- def encoder
37
- # DSM handles encoding in the transport layer (MessagePack + gzip)
38
- # so we don't need an encoder at the API level
39
- nil
40
- end
41
- end
42
-
43
- # HTTP API Instance for DSM
44
- class Instance < Core::Transport::HTTP::API::Instance
45
- def send_stats(env)
46
- unless spec.is_a?(Stats::API::Spec)
47
- raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new(
48
- 'stats', self
49
- )
50
- end
51
-
52
- spec.send_stats(env) do |request_env|
53
- call(request_env)
54
- end
55
- end
56
- end
57
-
58
23
  # Endpoint for submitting DSM stats data
59
24
  class Endpoint < Core::Transport::HTTP::API::Endpoint
60
25
  def initialize(path)
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../core/transport/http'
4
- require_relative 'http/api'
5
- require_relative 'http/client'
6
4
  require_relative 'http/stats'
7
5
  require_relative 'stats'
8
6
 
@@ -11,6 +9,10 @@ module Datadog
11
9
  module Transport
12
10
  # HTTP transport for Data Streams Monitoring
13
11
  module HTTP
12
+ V01 = Stats::API::Endpoint.new(
13
+ '/v0.1/pipeline_stats'
14
+ )
15
+
14
16
  module_function
15
17
 
16
18
  # Builds a new Transport::HTTP::Client with default settings
@@ -19,7 +21,6 @@ module Datadog
19
21
  logger:
20
22
  )
21
23
  Core::Transport::HTTP.build(
22
- api_instance_class: Stats::API::Instance,
23
24
  agent_settings: agent_settings,
24
25
  logger: logger,
25
26
  headers: {
@@ -27,9 +28,7 @@ module Datadog
27
28
  'Content-Encoding' => 'gzip'
28
29
  }
29
30
  ) do |transport|
30
- apis = API.defaults
31
-
32
- transport.api API::V01, apis[API::V01], default: true
31
+ transport.api 'v0.1', V01, default: true
33
32
 
34
33
  # Call block to apply any customization, if provided
35
34
  yield(transport) if block_given?
@@ -4,6 +4,7 @@ require 'msgpack'
4
4
  require 'zlib'
5
5
  require_relative '../../core/transport/parcel'
6
6
  require_relative '../../core/transport/request'
7
+ require_relative '../../core/transport/transport'
7
8
 
8
9
  module Datadog
9
10
  module DataStreams
@@ -25,18 +26,7 @@ module Datadog
25
26
  end
26
27
 
27
28
  # Transport for Data Streams Monitoring stats
28
- class Transport
29
- attr_reader :client, :apis, :current_api_id, :logger
30
-
31
- def initialize(apis, default_api, logger:)
32
- @apis = apis
33
- @logger = logger
34
- @default_api = default_api
35
- @current_api_id = default_api
36
-
37
- @client = DataStreams::Transport::HTTP::Client.new(current_api, logger: @logger)
38
- end
39
-
29
+ class Transport < Core::Transport::Transport
40
30
  def send_stats(payload)
41
31
  # MessagePack encode and gzip compress the payload
42
32
  msgpack_data = MessagePack.pack(payload)
@@ -47,11 +37,7 @@ module Datadog
47
37
  request = Request.new(parcel)
48
38
 
49
39
  # Send to agent
50
- client.send_stats_payload(request)
51
- end
52
-
53
- def current_api
54
- apis[@current_api_id]
40
+ client.send_request(:stats, request)
55
41
  end
56
42
  end
57
43
  end
@@ -17,7 +17,8 @@ require_relative 'serializer'
17
17
  require_relative 'transport/http'
18
18
  require_relative 'utils'
19
19
 
20
- if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore
20
+ # Steep: https://github.com/ruby/rbs/pull/2715
21
+ if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore ArgumentTypeMismatch
21
22
 
22
23
  # For initial release of Dynamic Instrumentation, activate code tracking
23
24
  # only if DI is explicitly requested in the environment.
@@ -35,7 +36,8 @@ require_relative 'contrib'
35
36
 
36
37
  Datadog::DI::Contrib.load_now_or_later
37
38
 
38
- if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore
39
+ # Steep: https://github.com/ruby/rbs/pull/2715
40
+ if %w[1 true yes].include?(Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore ArgumentTypeMismatch
39
41
  if Datadog::DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE']
40
42
  require_relative 'probe_file_loader'
41
43
  Datadog::DI::ProbeFileLoader.load_now_or_later