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
@@ -1,43 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../../core/transport/http/api/instance'
4
- require_relative '../../../core/transport/http/api/spec'
5
- require_relative '../../../core/transport/http/client'
3
+ require_relative '../../../core/transport/http/api/endpoint'
6
4
 
7
5
  module Datadog
8
6
  module DI
9
7
  module Transport
10
8
  module HTTP
11
9
  module Diagnostics
12
- class Client < Core::Transport::HTTP::Client
13
- def send_diagnostics_payload(request)
14
- send_request(request) do |api, env|
15
- api.send_diagnostics(env)
16
- end
17
- end
18
- end
19
-
20
10
  module API
21
- class Instance < Core::Transport::HTTP::API::Instance
22
- def send_diagnostics(env)
23
- raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new('diagnostics', self) unless spec.is_a?(Diagnostics::API::Spec)
24
-
25
- spec.send_diagnostics(env) do |request_env|
26
- call(request_env)
27
- end
28
- end
29
- end
30
-
31
- class Spec < Core::Transport::HTTP::API::Spec
32
- attr_accessor :diagnostics
33
-
34
- def send_diagnostics(env, &block)
35
- raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('diagnostics', self) if diagnostics.nil?
36
-
37
- diagnostics.call(env, &block)
38
- end
39
- end
40
-
41
11
  class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
42
12
  attr_reader :encoder
43
13
 
@@ -1,43 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../../core/transport/http/api/instance'
4
- require_relative '../../../core/transport/http/api/spec'
5
- require_relative '../../../core/transport/http/client'
3
+ require_relative '../../../core/transport/http/api/endpoint'
6
4
 
7
5
  module Datadog
8
6
  module DI
9
7
  module Transport
10
8
  module HTTP
11
9
  module Input
12
- class Client < Core::Transport::HTTP::Client
13
- def send_input_payload(request)
14
- send_request(request) do |api, env|
15
- api.send_input(env)
16
- end
17
- end
18
- end
19
-
20
10
  module API
21
- class Instance < Core::Transport::HTTP::API::Instance
22
- def send_input(env)
23
- raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new('input', self) unless spec.is_a?(Input::API::Spec)
24
-
25
- spec.send_input(env) do |request_env|
26
- call(request_env)
27
- end
28
- end
29
- end
30
-
31
- class Spec < Core::Transport::HTTP::API::Spec
32
- attr_accessor :input
33
-
34
- def send_input(env, &block)
35
- raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('input', self) if input.nil?
36
-
37
- input.call(env, &block)
38
- end
39
- end
40
-
41
11
  class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
42
12
  HEADER_CONTENT_TYPE = 'Content-Type'
43
13
 
@@ -1,31 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../core/encoding'
4
+ require_relative '../../core/transport/http'
3
5
  require_relative 'diagnostics'
4
6
  require_relative 'input'
5
- require_relative 'http/api'
6
- require_relative '../../core/transport/http'
7
7
 
8
8
  module Datadog
9
9
  module DI
10
10
  module Transport
11
11
  # Namespace for HTTP transport components
12
12
  module HTTP
13
- module_function
13
+ DIAGNOSTICS = Diagnostics::API::Endpoint.new(
14
+ '/debugger/v1/diagnostics',
15
+ Core::Encoding::JSONEncoder,
16
+ )
17
+
18
+ INPUT = Input::API::Endpoint.new(
19
+ '/debugger/v2/input',
20
+ Core::Encoding::JSONEncoder,
21
+ )
22
+
23
+ LEGACY_INPUT = Input::API::Endpoint.new(
24
+ # We used to use /debugger/v1/input, but now input
25
+ # payloads should be going to the diagnostics endpoint
26
+ # which I gather performs data redaction.
27
+ '/debugger/v1/diagnostics',
28
+ Core::Encoding::JSONEncoder,
29
+ )
14
30
 
15
31
  # Builds a new Transport::HTTP::Client with default settings
16
32
  # Pass a block to override any settings.
17
- def diagnostics(
33
+ def self.diagnostics(
18
34
  agent_settings:,
19
35
  logger:,
20
- api_version: nil,
21
36
  headers: nil
22
37
  )
23
- Core::Transport::HTTP.build(api_instance_class: Diagnostics::API::Instance,
38
+ Core::Transport::HTTP.build(
24
39
  logger: logger,
25
- agent_settings: agent_settings, api_version: api_version, headers: headers) do |transport|
26
- apis = API.defaults
27
-
28
- transport.api API::DIAGNOSTICS, apis[API::DIAGNOSTICS]
40
+ agent_settings: agent_settings,
41
+ headers: headers,
42
+ ) do |transport|
43
+ transport.api 'diagnostics', DIAGNOSTICS
29
44
 
30
45
  # Call block to apply any customization, if provided
31
46
  yield(transport) if block_given?
@@ -34,22 +49,18 @@ module Datadog
34
49
 
35
50
  # Builds a new Transport::HTTP::Client with default settings
36
51
  # Pass a block to override any settings.
37
- def input(
52
+ def self.input(
38
53
  agent_settings:,
39
54
  logger:,
40
- api_version: nil,
41
55
  headers: nil
42
56
  )
43
57
  Core::Transport::HTTP.build(
44
- api_instance_class: Input::API::Instance,
45
58
  logger: logger,
46
59
  agent_settings: agent_settings,
47
- api_version: api_version,
48
60
  headers: headers,
49
61
  ) do |transport|
50
- apis = API.defaults
51
-
52
- transport.api API::INPUT, apis[API::INPUT]
62
+ transport.api 'input', INPUT, fallback: 'legacy_input', default: true
63
+ transport.api 'legacy_input', LEGACY_INPUT
53
64
 
54
65
  # Call block to apply any customization, if provided
55
66
  yield(transport) if block_given?
@@ -5,6 +5,7 @@ require_relative '../../core/encoding'
5
5
  require_relative '../../core/tag_builder'
6
6
  require_relative '../../core/transport/parcel'
7
7
  require_relative '../../core/transport/request'
8
+ require_relative '../../core/transport/transport'
8
9
  require_relative '../error'
9
10
  require_relative 'http/input'
10
11
 
@@ -26,9 +27,7 @@ module Datadog
26
27
  end
27
28
  end
28
29
 
29
- class Transport
30
- attr_reader :client, :apis, :default_api, :current_api_id, :logger
31
-
30
+ class Transport < Core::Transport::Transport
32
31
  # The limit on an individual snapshot payload, aka "log line",
33
32
  # is 1 MB.
34
33
  #
@@ -48,17 +47,6 @@ module Datadog
48
47
  # max chunk size, it will still get sent out.
49
48
  DEFAULT_CHUNK_SIZE = 2 * 1024 * 1024
50
49
 
51
- def initialize(apis, default_api, logger:)
52
- @apis = apis
53
- @logger = logger
54
-
55
- @client = DI::Transport::HTTP::Input::Client.new(current_api, logger: logger)
56
- end
57
-
58
- def current_api
59
- @apis[HTTP::API::INPUT]
60
- end
61
-
62
50
  def send_input(payload, tags)
63
51
  # Tags are the same for all chunks, serialize them one time.
64
52
  serialized_tags = Core::TagBuilder.serialize_tags(tags)
@@ -101,27 +89,12 @@ module Datadog
101
89
  parcel = EncodedParcel.new(chunked_payload)
102
90
  request = Request.new(parcel, serialized_tags)
103
91
 
104
- response = @client.send_input_payload(request)
105
- unless response.ok?
106
- # TODO Datadog::Core::Transport::InternalErrorResponse
107
- # does not have +code+ method, what is the actual API of
108
- # these response objects?
109
- raise Error::AgentCommunicationError, "send_input failed: #{begin
110
- response.code
111
- rescue
112
- "???"
113
- end}: #{response.payload}"
92
+ client.send_request(:input, request).tap do |response|
93
+ if downgrade?(response)
94
+ downgrade!
95
+ return send_input_chunk(chunked_payload, serialized_tags)
96
+ end
114
97
  end
115
- rescue Error::AgentCommunicationError
116
- raise
117
- # Datadog::Core::Transport does not perform any exception mapping,
118
- # therefore we could have any exception here from failure to parse
119
- # agent URI for example.
120
- # If we ever implement retries for network errors, we should distinguish
121
- # actual network errors from non-network errors that are raised by
122
- # transport code.
123
- rescue => exc
124
- raise Error::AgentCommunicationError, "send_input failed: #{exc.class}: #{exc}"
125
98
  end
126
99
  end
127
100
  end
data/lib/datadog/di.rb CHANGED
@@ -9,16 +9,13 @@ module Datadog
9
9
  #
10
10
  # @api private
11
11
  module DI
12
+ INSTRUMENTED_COUNTERS_LOCK = Mutex.new
13
+
12
14
  class << self
13
15
  def enabled?
14
16
  Datadog.configuration.dynamic_instrumentation.enabled
15
17
  end
16
- end
17
-
18
- # Expose DI to global shared objects
19
- Extensions.activate!
20
18
 
21
- class << self
22
19
  # This method is called from DI Remote handler to issue DI operations
23
20
  # to the probe manager (add or remove probes).
24
21
  #
@@ -31,6 +28,65 @@ module Datadog
31
28
  def component
32
29
  Datadog.send(:components).dynamic_instrumentation
33
30
  end
31
+
32
+ # Track how many outstanding instrumentations are in DI.
33
+ #
34
+ # It is hard to find the actual instrumentations - there is no
35
+ # method provided by Ruby to list all trace points, and we would
36
+ # need to manually track our instrumentation modules for method probes.
37
+ # Plus, tracking the modules could create active references to
38
+ # instrumentation, which is not desired.
39
+ #
40
+ # A simpler solution is to maintain a counter which is increased
41
+ # whenever a probe is installed and decreased when a probe is removed.
42
+ #
43
+ # This counter does not include pending probes - being not installed,
44
+ # those pose no concerns to customer applications.
45
+ def instrumented_count(kind = nil)
46
+ INSTRUMENTED_COUNTERS_LOCK.synchronize do
47
+ if defined?(@instrumented_count)
48
+ if kind
49
+ validate_kind!(kind)
50
+ @instrumented_count[kind] || 0
51
+ else
52
+ @instrumented_count.inject(0) do |sum, (_kind, count)|
53
+ sum + count
54
+ end
55
+ end
56
+ else
57
+ 0
58
+ end
59
+ end
60
+ end
61
+
62
+ def instrumented_count_inc(kind)
63
+ validate_kind!(kind)
64
+ INSTRUMENTED_COUNTERS_LOCK.synchronize do
65
+ @instrumented_count = Hash.new(0) unless defined?(@instrumented_count)
66
+ @instrumented_count[kind] += 1
67
+ end
68
+ end
69
+
70
+ def instrumented_count_dec(kind)
71
+ validate_kind!(kind)
72
+ INSTRUMENTED_COUNTERS_LOCK.synchronize do
73
+ @instrumented_count = Hash.new(0) unless defined?(@instrumented_count)
74
+ if @instrumented_count[kind] <= 0
75
+ Datadog.logger.debug { "di: attempting to decrease instrumented count below zero for #{kind}" }
76
+ return
77
+ end
78
+ @instrumented_count[kind] -= 1
79
+ end
80
+ end
81
+
82
+ private def validate_kind!(kind)
83
+ unless %i[line method].include?(kind)
84
+ raise ArgumentError, "Invalid kind: #{kind}"
85
+ end
86
+ end
34
87
  end
88
+
89
+ # Expose DI to global shared objects
90
+ Extensions.activate!
35
91
  end
36
92
  end
@@ -18,10 +18,10 @@ module Datadog
18
18
  regex_match = regex.match(file_path)
19
19
  return unless regex_match
20
20
 
21
- gem_name = regex_match[1]
21
+ gem_name = regex_match[1] #: String
22
22
 
23
23
  begin
24
- Gem::Specification.find_by_name(gem_name) # steep:ignore
24
+ Gem::Specification.find_by_name(gem_name)
25
25
  rescue Gem::MissingSpecError
26
26
  nil
27
27
  end
@@ -66,8 +66,7 @@ module Datadog
66
66
  record_event_telemetry_metric(LOGIN_SUCCESS_EVENT)
67
67
  ::Datadog::AppSec::Instrumentation.gateway.push('appsec.events.user_lifecycle', LOGIN_SUCCESS_EVENT)
68
68
 
69
- # NOTE: Guard-clause will not work with Steep typechecking
70
- return Kit::Identity.set_user(trace, span, **user_attributes) if user_attributes.key?(:id) # steep:ignore
69
+ return Kit::Identity.set_user(trace, span, **user_attributes) if user_attributes.key?(:id)
71
70
 
72
71
  # NOTE: This is a fallback for the case when we don't have an ID,
73
72
  # but need to trigger WAF.
@@ -156,7 +155,7 @@ module Datadog
156
155
  raise ArgumentError, 'missing required user key `:id`' unless user_or_id.key?(:id)
157
156
  raise TypeError, 'user key `:id` must be a String' unless user_or_id[:id].is_a?(String)
158
157
 
159
- user_or_id.merge(login: login)
158
+ user_or_id.merge(login: login) #: {login: ::String, ?id: ::String?}
160
159
  else
161
160
  raise TypeError, '`user_or_id` argument must be either String or Hash'
162
161
  end
@@ -9,7 +9,8 @@ module Datadog
9
9
  module OpenFeature
10
10
  # This class performs the evaluation of the feature flag
11
11
  class EvaluationEngine
12
- ReconfigurationError = Class.new(StandardError)
12
+ # Steep: https://github.com/soutaro/steep/issues/1880
13
+ ReconfigurationError = Class.new(StandardError) # steep:ignore IncompatibleAssignment
13
14
 
14
15
  ALLOWED_TYPES = %i[boolean string number float integer object].freeze
15
16
 
@@ -6,8 +6,6 @@ module Datadog
6
6
  module OpenFeature
7
7
  # This module contains the remote configuration functionality for OpenFeature
8
8
  module Remote
9
- ReadError = Class.new(StandardError)
10
-
11
9
  class << self
12
10
  FFE_FLAG_CONFIGURATION_RULES = 1 << 46
13
11
  FFE_PRODUCTS = ['FFE_FLAGS'].freeze
@@ -42,8 +40,6 @@ module Datadog
42
40
  # @type var content: Core::Remote::Configuration::Content
43
41
  engine.reconfigure!(read_content(content))
44
42
  content.applied
45
- rescue ReadError => e
46
- content.errored("Error reading Remote Configuration content: #{e.message}")
47
43
  rescue EvaluationEngine::ReconfigurationError => e
48
44
  content.errored("Error applying OpenFeature configuration: #{e.message}")
49
45
  end
@@ -61,12 +57,9 @@ module Datadog
61
57
  private
62
58
 
63
59
  def read_content(content)
64
- data = content.data.read
65
- content.data.rewind
66
-
67
- raise ReadError, 'EOF reached' if data.nil?
68
-
69
- data
60
+ # Unlike all of the other remotes, this one does not JSON-parse
61
+ # the data.
62
+ content.data
70
63
  end
71
64
  end
72
65
  end
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../core/encoding'
3
4
  require_relative '../core/transport/http'
4
5
  require_relative '../core/transport/http/env'
6
+ require_relative '../core/transport/http/api/endpoint'
7
+ require_relative '../core/transport/http/api/instance'
5
8
  require_relative '../core/transport/parcel'
6
9
  require_relative '../core/transport/request'
7
10
 
@@ -17,15 +20,14 @@ module Datadog
17
20
  end
18
21
 
19
22
  class HTTP
20
- class Spec < Core::Transport::HTTP::API::Spec
23
+ class Spec
21
24
  def initialize
22
25
  @endpoint = Core::Transport::HTTP::API::Endpoint.new(
23
26
  :post, '/evp_proxy/v2/api/v2/exposures'
24
27
  )
25
-
26
- super
27
28
  end
28
29
 
30
+ # TODO rename to send_request?
29
31
  def call(env, &block)
30
32
  @endpoint.call(env) do |request_env|
31
33
  request_env.headers['Content-Type'] = Core::Encoding::JSONEncoder.content_type
@@ -37,15 +39,8 @@ module Datadog
37
39
  end
38
40
  end
39
41
 
40
- class Instance < Core::Transport::HTTP::API::Instance
41
- def send_exposures(env)
42
- @spec.call(env) { |request_env| call(request_env) }
43
- end
44
- end
45
-
46
42
  def self.build(agent_settings:, logger:)
47
43
  Core::Transport::HTTP.build(
48
- api_instance_class: HTTP::Instance,
49
44
  agent_settings: agent_settings,
50
45
  logger: logger
51
46
  ) { |t| t.api('exposures', HTTP::Spec.new) }.to_transport(self)
@@ -58,7 +53,10 @@ module Datadog
58
53
 
59
54
  def send_exposures(payload)
60
55
  request = Core::Transport::Request.new(EncodedParcel.new(payload))
61
- @api.send_exposures(Core::Transport::HTTP::Env.new(request))
56
+
57
+ @api.endpoint.call(Core::Transport::HTTP::Env.new(request)) do |env|
58
+ @api.call(env)
59
+ end
62
60
  rescue => e
63
61
  message = "Internal error during request. Cause: #{e.class.name} #{e.message} " \
64
62
  "Location: #{Array(e.backtrace).first}"
@@ -26,7 +26,7 @@ module Datadog
26
26
  # to ::OpenTelemetry::Context.current
27
27
  # @return [Context]
28
28
  def clear(context: ::OpenTelemetry::Context.current)
29
- context.ensure_trace.baggage.clear
29
+ context.ensure_trace&.baggage&.clear
30
30
  context
31
31
  end
32
32
 
@@ -140,13 +140,13 @@ module Datadog
140
140
  option :timeout_millis do |o|
141
141
  o.type :int, nilable: true
142
142
  o.env 'OTEL_EXPORTER_OTLP_METRICS_TIMEOUT'
143
- o.default nil
143
+ o.default 10000
144
144
  end
145
145
 
146
146
  option :protocol do |o|
147
147
  o.type :string, nilable: true
148
148
  o.env 'OTEL_EXPORTER_OTLP_METRICS_PROTOCOL'
149
- o.default nil
149
+ o.default "http/protobuf"
150
150
  o.setter(&Settings.normalize_protocol('OTEL_EXPORTER_OTLP_METRICS_PROTOCOL'))
151
151
  end
152
152
  end
@@ -70,12 +70,7 @@ module Datadog
70
70
  @logger.warn("Failed to configure OTLP metrics exporter: #{e.class}: #{e}")
71
71
  end
72
72
 
73
- def resolve_metrics_endpoint
74
- metrics_config = @settings.opentelemetry.metrics
75
- exporter_config = @settings.opentelemetry.exporter
76
-
77
- return metrics_config.endpoint if metrics_config.endpoint
78
- return exporter_config.endpoint if exporter_config.endpoint
73
+ def default_metrics_endpoint
79
74
  "#{@agent_ssl ? "https" : "http"}://#{@agent_host}:4318/v1/metrics"
80
75
  end
81
76
 
@@ -84,16 +79,19 @@ module Datadog
84
79
  require_relative 'sdk/metrics_exporter'
85
80
 
86
81
  metrics_config = @settings.opentelemetry.metrics
87
- exporter_config = @settings.opentelemetry.exporter
88
- timeout = metrics_config.timeout_millis || exporter_config.timeout_millis
89
- headers = metrics_config.headers || exporter_config.headers || {}
90
-
91
- protocol = metrics_config.protocol || exporter_config.protocol
82
+ endpoint = get_metrics_config_with_fallback(
83
+ option_name: :endpoint,
84
+ computed_default: default_metrics_endpoint
85
+ )
86
+ timeout = get_metrics_config_with_fallback(option_name: :timeout_millis)
87
+ headers = get_metrics_config_with_fallback(option_name: :headers)
88
+ # OpenTelemetry SDK only supports http/protobuf protocol.
89
+ # TODO: Add support for http/json and grpc.
90
+ # protocol = get_metrics_config_with_fallback(option_name: :protocol)
92
91
  exporter = Datadog::OpenTelemetry::SDK::MetricsExporter.new(
93
- endpoint: resolve_metrics_endpoint,
92
+ endpoint: endpoint,
94
93
  timeout: timeout / 1000.0,
95
- headers: headers,
96
- protocol: protocol
94
+ headers: headers
97
95
  )
98
96
 
99
97
  reader = ::OpenTelemetry::SDK::Metrics::Export::PeriodicMetricReader.new(
@@ -105,6 +103,15 @@ module Datadog
105
103
  rescue LoadError => e
106
104
  @logger.warn("Could not load OTLP metrics exporter: #{e.class}: #{e}")
107
105
  end
106
+
107
+ # Returns metrics config value if explicitly set, otherwise falls back to exporter config or computed default value.
108
+ def get_metrics_config_with_fallback(option_name:, computed_default: nil)
109
+ if @settings.opentelemetry.metrics.using_default?(option_name)
110
+ @settings.opentelemetry.exporter.public_send(option_name) || computed_default
111
+ else
112
+ @settings.opentelemetry.metrics.public_send(option_name)
113
+ end
114
+ end
108
115
  end
109
116
  end
110
117
  end
@@ -9,21 +9,18 @@ module Datadog
9
9
  METRIC_EXPORT_ATTEMPTS = 'otel.metrics_export_attempts'
10
10
  METRIC_EXPORT_SUCCESSES = 'otel.metrics_export_successes'
11
11
  METRIC_EXPORT_FAILURES = 'otel.metrics_export_failures'
12
-
13
- def initialize(endpoint:, timeout:, headers:, protocol:)
14
- super(endpoint: endpoint, timeout: timeout, headers: headers)
15
- @telemetry_tags = {'protocol' => protocol, 'encoding' => 'protobuf'}
16
- end
12
+ TELEMETRY_NAMESPACE = 'tracers'
13
+ TELEMETRY_TAGS = {'protocol' => "http", 'encoding' => 'protobuf'}
17
14
 
18
15
  def export(metrics, timeout: nil)
19
- telemetry&.inc('tracers', METRIC_EXPORT_ATTEMPTS, 1, tags: @telemetry_tags)
16
+ telemetry&.inc(TELEMETRY_NAMESPACE, METRIC_EXPORT_ATTEMPTS, 1, tags: TELEMETRY_TAGS)
20
17
  result = super
21
18
  metric_name = (result == 0) ? METRIC_EXPORT_SUCCESSES : METRIC_EXPORT_FAILURES
22
- telemetry&.inc('tracers', metric_name, 1, tags: @telemetry_tags)
19
+ telemetry&.inc(TELEMETRY_NAMESPACE, metric_name, 1, tags: TELEMETRY_TAGS)
23
20
  result
24
21
  rescue => e
25
22
  Datadog.logger.error("Failed to export OpenTelemetry Metrics: #{e.class}: #{e}")
26
- telemetry&.inc('tracers', METRIC_EXPORT_FAILURES, 1, tags: @telemetry_tags)
23
+ telemetry&.inc(TELEMETRY_NAMESPACE, METRIC_EXPORT_FAILURES, 1, tags: TELEMETRY_TAGS)
27
24
  raise
28
25
  end
29
26
 
@@ -22,7 +22,7 @@ module Datadog
22
22
  @libraries_by_path = {}
23
23
  @seen_files = Set.new
24
24
  @seen_libraries = Set.new
25
- @executable_paths = [Gem.bindir, (Bundler.bin_path.to_s if defined?(Bundler))].uniq.compact.freeze
25
+ @executable_paths = [Gem.bindir, bundler_bin_path].uniq.compact.freeze
26
26
 
27
27
  record_library(
28
28
  Library.new(
@@ -116,6 +116,30 @@ module Datadog
116
116
  end
117
117
  end
118
118
 
119
+ # This is intended to mirror bundler's `Bundler.bin_path` with one key difference: the bundler version of the
120
+ # method **tries to create the path** if it doesn't exist, whereas our version doesn't.
121
+ #
122
+ # This "try to create the path" is annoying because creating the path can fail if e.g. the app doesn't have
123
+ # permissions to do that, see https://github.com/DataDog/dd-trace-rb/issues/5137.
124
+ # Thus we have our own version that a) Avoids creating the path, and b) Rescues exceptions to avoid any
125
+ # bundler complaints here impacting the application. (Bundler tends to go "something is wrong, raise!" which
126
+ # I think makes a lot of sense given how bundler is intended to be used, but for this our kind of "ask a few
127
+ # questions usage" it's not what we want.)
128
+ def bundler_bin_path
129
+ return unless defined?(Bundler)
130
+
131
+ path = Bundler.settings[:bin] || "bin"
132
+ root = Bundler.root
133
+ result = Pathname.new(path).expand_path(root).expand_path
134
+ result.to_s
135
+ rescue Exception => e # rubocop:disable Lint/RescueException
136
+ Datadog.logger.debug(
137
+ "CodeProvenance#bundler_bin_path failed. " \
138
+ "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
139
+ )
140
+ nil
141
+ end
142
+
119
143
  # Represents metadata we have for a ruby gem
120
144
  #
121
145
  # Important note: This class gets encoded to JSON with the built-in JSON gem. But, we've found that in some
@@ -137,7 +161,8 @@ module Datadog
137
161
  end
138
162
 
139
163
  def to_json(arg = nil)
140
- {kind: @kind, name: @name, version: @version, paths: @paths}.to_json(arg)
164
+ # Steep: https://github.com/ruby/rbs/pull/2691 (remove after RBS 4.0 release)
165
+ {kind: @kind, name: @name, version: @version, paths: @paths}.to_json(arg) # steep:ignore ArgumentTypeMismatch
141
166
  end
142
167
 
143
168
  def path
@@ -16,7 +16,9 @@ module Datadog
16
16
  class Info
17
17
  def initialize(settings)
18
18
  @profiler_info = nil
19
- @info = {
19
+
20
+ # Steep: https://github.com/soutaro/steep/issues/363
21
+ @info = { # steep:ignore IncompatibleAssignment
20
22
  platform: collect_platform_info,
21
23
  runtime: collect_runtime_info,
22
24
  application: collect_application_info(settings),
@@ -96,7 +98,7 @@ module Datadog
96
98
  end
97
99
 
98
100
  def collect_profiler_info(settings)
99
- unless @profiler_info
101
+ @profiler_info ||= begin
100
102
  lib_datadog_gem = ::Gem.loaded_specs["libdatadog"]
101
103
 
102
104
  libdatadog_version =
@@ -108,13 +110,12 @@ module Datadog
108
110
  "#{Libdatadog::VERSION}-(unknown)"
109
111
  end
110
112
 
111
- @profiler_info = {
113
+ {
112
114
  version: Datadog::Core::Environment::Identity.gem_datadog_version,
113
115
  libdatadog: libdatadog_version,
114
116
  settings: collect_settings_recursively(settings.profiling),
115
117
  }.freeze
116
118
  end
117
- @profiler_info
118
119
  end
119
120
 
120
121
  # The settings/option model isn't directly serializable because