cw-datadog 2.23.0.2 → 2.23.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/ext/datadog_profiling_native_extension/extconf.rb +4 -2
  3. data/ext/libdatadog_api/library_config.c +12 -11
  4. data/ext/libdatadog_extconf_helpers.rb +1 -1
  5. data/lib/datadog/appsec/api_security/route_extractor.rb +20 -5
  6. data/lib/datadog/appsec/api_security/sampler.rb +3 -1
  7. data/lib/datadog/appsec/assets/blocked.html +8 -0
  8. data/lib/datadog/appsec/assets/blocked.json +1 -1
  9. data/lib/datadog/appsec/assets/blocked.text +3 -1
  10. data/lib/datadog/appsec/assets.rb +1 -1
  11. data/lib/datadog/appsec/remote.rb +4 -0
  12. data/lib/datadog/appsec/response.rb +18 -4
  13. data/lib/datadog/core/cloudwise/client.rb +412 -25
  14. data/lib/datadog/core/cloudwise/component.rb +195 -52
  15. data/lib/datadog/core/cloudwise/docc_heartbeat_worker.rb +105 -0
  16. data/lib/datadog/core/cloudwise/docc_operation_worker.rb +191 -0
  17. data/lib/datadog/core/cloudwise/docc_registration_worker.rb +89 -0
  18. data/lib/datadog/core/cloudwise/license_worker.rb +90 -4
  19. data/lib/datadog/core/cloudwise/probe_state.rb +134 -12
  20. data/lib/datadog/core/configuration/components.rb +10 -9
  21. data/lib/datadog/core/configuration/settings.rb +43 -0
  22. data/lib/datadog/core/configuration/supported_configurations.rb +6 -2
  23. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  24. data/lib/datadog/core/remote/component.rb +2 -2
  25. data/lib/datadog/core/remote/transport/config.rb +2 -10
  26. data/lib/datadog/core/remote/transport/http/config.rb +9 -9
  27. data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
  28. data/lib/datadog/core/remote/transport/http.rb +2 -0
  29. data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
  30. data/lib/datadog/core/remote/worker.rb +23 -35
  31. data/lib/datadog/core/telemetry/component.rb +26 -13
  32. data/lib/datadog/core/telemetry/event/app_started.rb +67 -49
  33. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  34. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
  35. data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
  36. data/lib/datadog/core/telemetry/worker.rb +51 -6
  37. data/lib/datadog/core/transport/http/adapters/net.rb +2 -0
  38. data/lib/datadog/core/transport/http/client.rb +69 -0
  39. data/lib/datadog/core/utils/only_once_successful.rb +6 -2
  40. data/lib/datadog/data_streams/transport/http/client.rb +4 -32
  41. data/lib/datadog/data_streams/transport/stats.rb +1 -1
  42. data/lib/datadog/di/probe_notification_builder.rb +35 -13
  43. data/lib/datadog/di/transport/diagnostics.rb +2 -2
  44. data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
  45. data/lib/datadog/di/transport/http/input.rb +2 -4
  46. data/lib/datadog/di/transport/input.rb +2 -2
  47. data/lib/datadog/open_feature/component.rb +60 -0
  48. data/lib/datadog/open_feature/configuration.rb +27 -0
  49. data/lib/datadog/open_feature/evaluation_engine.rb +59 -0
  50. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  51. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  52. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  53. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  54. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  55. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  56. data/lib/datadog/open_feature/ext.rb +13 -0
  57. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  58. data/lib/datadog/open_feature/provider.rb +134 -0
  59. data/lib/datadog/open_feature/remote.rb +74 -0
  60. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  61. data/lib/datadog/open_feature/transport.rb +72 -0
  62. data/lib/datadog/open_feature.rb +19 -0
  63. data/lib/datadog/profiling/component.rb +6 -0
  64. data/lib/datadog/profiling/profiler.rb +4 -0
  65. data/lib/datadog/profiling.rb +1 -2
  66. data/lib/datadog/single_step_instrument.rb +1 -1
  67. data/lib/datadog/tracing/contrib/cloudwise/propagation.rb +164 -7
  68. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +22 -17
  69. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  70. data/lib/datadog/tracing/contrib/karafka/patcher.rb +14 -0
  71. data/lib/datadog/tracing/contrib/rack/middlewares.rb +6 -2
  72. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  73. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  74. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  75. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  76. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  77. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
  78. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  79. data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
  80. data/lib/datadog/tracing/contrib.rb +1 -0
  81. data/lib/datadog/tracing/transport/http/api.rb +73 -1
  82. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  83. data/lib/datadog/tracing/transport/http/traces.rb +4 -2
  84. data/lib/datadog/tracing/transport/trace_formatter.rb +16 -0
  85. data/lib/datadog/version.rb +2 -2
  86. data/lib/datadog.rb +1 -0
  87. metadata +38 -15
  88. data/lib/datadog/core/cloudwise/IMPLEMENTATION_V2.md +0 -517
  89. data/lib/datadog/core/cloudwise/QUICKSTART.md +0 -398
  90. data/lib/datadog/core/cloudwise/README.md +0 -722
  91. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  92. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  93. data/lib/datadog/di/transport/http/client.rb +0 -47
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../core/utils/time'
4
+
5
+ module Datadog
6
+ module OpenFeature
7
+ module Exposures
8
+ # A data model for an exposure event
9
+ module Event
10
+ TARGETING_KEY_FIELD = 'targeting_key'
11
+ ALLOWED_FIELD_TYPES = [String, Integer, Float, TrueClass, FalseClass].freeze
12
+
13
+ class << self
14
+ def cache_key(result, flag_key:, context:)
15
+ "#{flag_key}:#{context.targeting_key}"
16
+ end
17
+
18
+ def cache_value(result, flag_key:, context:)
19
+ "#{result.allocation_key}:#{result.variant}"
20
+ end
21
+
22
+ def build(result, flag_key:, context:)
23
+ {
24
+ timestamp: current_timestamp_ms,
25
+ allocation: {
26
+ key: result.allocation_key
27
+ },
28
+ flag: {
29
+ key: flag_key
30
+ },
31
+ variant: {
32
+ key: result.variant
33
+ },
34
+ subject: {
35
+ id: context.targeting_key,
36
+ attributes: extract_attributes(context)
37
+ }
38
+ }.freeze
39
+ end
40
+
41
+ private
42
+
43
+ # NOTE: We take all filds of the context that does not support nesting
44
+ # and will ignore targeting key as it will be set as `subject.id`
45
+ def extract_attributes(context)
46
+ context.fields.select do |key, value|
47
+ next false if key == TARGETING_KEY_FIELD
48
+
49
+ ALLOWED_FIELD_TYPES.include?(value.class)
50
+ end
51
+ end
52
+
53
+ def current_timestamp_ms
54
+ (Datadog::Core::Utils::Time.now.to_f * 1000).to_i
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'event'
4
+ require_relative 'deduplicator'
5
+
6
+ module Datadog
7
+ module OpenFeature
8
+ module Exposures
9
+ # This class is responsible for reporting exposures to the Agent
10
+ class Reporter
11
+ def initialize(worker, telemetry:, logger:)
12
+ @worker = worker
13
+ @logger = logger
14
+ @telemetry = telemetry
15
+ @deduplicator = Deduplicator.new
16
+ end
17
+
18
+ # NOTE: Reporting expects evaluation context to be always present, but it
19
+ # might be missing depending on the customer way of using flags evaluation API.
20
+ # In addition to that the evaluation result must be marked for reporting.
21
+ def report(result, flag_key:, context:)
22
+ return false if context.nil?
23
+ return false unless result.log?
24
+
25
+ key = Event.cache_key(result, flag_key: flag_key, context: context)
26
+ value = Event.cache_value(result, flag_key: flag_key, context: context)
27
+ return false if @deduplicator.duplicate?(key, value)
28
+
29
+ event = Event.build(result, flag_key: flag_key, context: context)
30
+ @worker.enqueue(event)
31
+ rescue => e
32
+ @logger.debug { "OpenFeature: Failed to report resolution details: #{e.class}: #{e.message}" }
33
+ @telemetry.report(e, description: 'OpenFeature: Failed to report resolution details')
34
+
35
+ false
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../core/utils/time'
4
+ require_relative '../../core/workers/queue'
5
+ require_relative '../../core/workers/polling'
6
+
7
+ require_relative 'buffer'
8
+ require_relative 'batch_builder'
9
+
10
+ module Datadog
11
+ module OpenFeature
12
+ module Exposures
13
+ # This class is responsible for sending exposures to the Agent
14
+ class Worker
15
+ include Core::Workers::Queue
16
+ include Core::Workers::Polling
17
+
18
+ GRACEFUL_SHUTDOWN_EXTRA_SECONDS = 5
19
+ GRACEFUL_SHUTDOWN_WAIT_INTERVAL_SECONDS = 0.5
20
+
21
+ DEFAULT_FLUSH_INTERVAL_SECONDS = 30
22
+ DEFAULT_BUFFER_LIMIT = Buffer::DEFAULT_LIMIT
23
+
24
+ def initialize(
25
+ settings:,
26
+ transport:,
27
+ telemetry:,
28
+ logger:,
29
+ flush_interval_seconds: DEFAULT_FLUSH_INTERVAL_SECONDS,
30
+ buffer_limit: DEFAULT_BUFFER_LIMIT
31
+ )
32
+ @logger = logger
33
+ @transport = transport
34
+ @telemetry = telemetry
35
+ @batch_builder = BatchBuilder.new(settings)
36
+ @buffer_limit = buffer_limit
37
+
38
+ self.buffer = Buffer.new(buffer_limit)
39
+ self.fork_policy = Core::Workers::Async::Thread::FORK_POLICY_RESTART
40
+ self.loop_base_interval = flush_interval_seconds
41
+ self.enabled = true
42
+ end
43
+
44
+ def start
45
+ return if !enabled? || running?
46
+
47
+ perform
48
+ end
49
+
50
+ def stop(force_stop = false, timeout = Core::Workers::Polling::DEFAULT_SHUTDOWN_TIMEOUT)
51
+ buffer.close if running?
52
+
53
+ super
54
+ end
55
+
56
+ def enqueue(event)
57
+ buffer.push(event)
58
+ start unless running?
59
+
60
+ true
61
+ end
62
+
63
+ def dequeue
64
+ [buffer.pop, buffer.dropped_count]
65
+ end
66
+
67
+ def perform(*args)
68
+ events, dropped = args
69
+ send_events(Array(events), dropped.to_i)
70
+ end
71
+
72
+ def graceful_shutdown
73
+ return false unless enabled? || !run_loop?
74
+
75
+ self.enabled = false
76
+
77
+ started = Core::Utils::Time.get_time
78
+ wait_time = loop_base_interval + GRACEFUL_SHUTDOWN_EXTRA_SECONDS
79
+
80
+ loop do
81
+ break if buffer.empty? && !in_iteration?
82
+
83
+ sleep(GRACEFUL_SHUTDOWN_WAIT_INTERVAL_SECONDS)
84
+ break if Core::Utils::Time.get_time - started > wait_time
85
+ end
86
+
87
+ stop(true)
88
+ end
89
+
90
+ private
91
+
92
+ def send_events(events, dropped)
93
+ return if events.empty?
94
+
95
+ if dropped.positive?
96
+ @logger.debug { "OpenFeature: Resolution details worker dropped #{dropped} event(s) due to full buffer" }
97
+ end
98
+
99
+ payload = @batch_builder.payload_for(events)
100
+ response = @transport.send_exposures(payload)
101
+
102
+ unless response&.ok?
103
+ @logger.debug { "OpenFeature: Resolution details upload response was not OK: #{response.inspect}" }
104
+ end
105
+
106
+ response
107
+ rescue => e
108
+ @logger.debug { "OpenFeature: Failed to flush resolution details events: #{e.class}: #{e.message}" }
109
+ @telemetry.report(e, description: 'OpenFeature: Failed to flush resolution details events')
110
+
111
+ nil
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module OpenFeature
5
+ module Ext
6
+ ERROR = 'ERROR'
7
+ INITIALIZING = 'INITIALIZING'
8
+ UNKNOWN_TYPE = 'UNKNOWN_TYPE'
9
+ PROVIDER_FATAL = 'PROVIDER_FATAL'
10
+ PROVIDER_NOT_READY = 'PROVIDER_NOT_READY'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ext'
4
+ require_relative 'resolution_details'
5
+
6
+ module Datadog
7
+ module OpenFeature
8
+ # This class is a noop interface of evaluation logic
9
+ class NoopEvaluator
10
+ def initialize(_configuration)
11
+ # no-op
12
+ end
13
+
14
+ def get_assignment(_flag_key, default_value, _context, _expected_type)
15
+ ResolutionDetails.new(
16
+ value: default_value,
17
+ log?: false,
18
+ error?: true,
19
+ error_code: Ext::PROVIDER_NOT_READY,
20
+ error_message: 'Waiting for universal flag configuration',
21
+ reason: Ext::INITIALIZING
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ext'
4
+ require 'open_feature/sdk'
5
+
6
+ module Datadog
7
+ module OpenFeature
8
+ # OpenFeature feature flagging provider backed by Datadog Remote Configuration.
9
+ #
10
+ # Implementation follows the OpenFeature contract of Provider SDK.
11
+ # For details see:
12
+ # - https://github.com/open-feature/ruby-sdk/blob/v0.4.1/README.md#develop-a-provider
13
+ # - https://github.com/open-feature/ruby-sdk/blob/v0.4.1/lib/open_feature/sdk/provider/no_op_provider.rb
14
+ #
15
+ # In the example below you can see how to configure the OpenFeature SDK
16
+ # https://github.com/open-feature/ruby-sdk to use the Datadog feature flags provider.
17
+ #
18
+ # Example:
19
+ #
20
+ # Make sure to enable Remote Configuration and OpenFeature in the Datadog configuration.
21
+ #
22
+ # ```ruby
23
+ # # FILE: initializers/datadog.rb
24
+ # Datadog.configure do |config|
25
+ # config.remote.enabled = true
26
+ # config.open_feature.enabled = true
27
+ # end
28
+ # ```
29
+ #
30
+ # And configure the OpenFeature SDK to use the Datadog feature flagging provider.
31
+ #
32
+ # ```ruby
33
+ # # FILE: initializers/open_feature.rb
34
+ # require 'open_feature/sdk'
35
+ # require 'datadog/open_feature/provider'
36
+ #
37
+ # OpenFeature::SDK.configure do |config|
38
+ # config.set_provider(Datadog::OpenFeature::Provider.new)
39
+ # end
40
+ # ```
41
+ #
42
+ # Now you can create OpenFeature SDK client and use it to fetch feature flag values.
43
+ #
44
+ # ```ruby
45
+ # client = OpenFeature::SDK.build_client
46
+ # context = OpenFeature::SDK::EvaluationContext.new('email' => 'john.doe@gmail.com')
47
+ #
48
+ # client.fetch_string_value(
49
+ # flag_key: 'banner', default_value: 'Greetings!', evaluation_context: context
50
+ # )
51
+ # # => 'Welcome back!'
52
+ # ```
53
+ class Provider
54
+ NAME = 'Datadog Feature Flagging Provider'
55
+
56
+ attr_reader :metadata
57
+
58
+ def initialize
59
+ @metadata = ::OpenFeature::SDK::Provider::ProviderMetadata.new(name: NAME).freeze
60
+ end
61
+
62
+ def init
63
+ # no-op
64
+ end
65
+
66
+ def shutdown
67
+ # no-op
68
+ end
69
+
70
+ def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil)
71
+ evaluate(flag_key, default_value: default_value, expected_type: 'boolean', evaluation_context: evaluation_context)
72
+ end
73
+
74
+ def fetch_string_value(flag_key:, default_value:, evaluation_context: nil)
75
+ evaluate(flag_key, default_value: default_value, expected_type: 'string', evaluation_context: evaluation_context)
76
+ end
77
+
78
+ def fetch_number_value(flag_key:, default_value:, evaluation_context: nil)
79
+ evaluate(flag_key, default_value: default_value, expected_type: 'number', evaluation_context: evaluation_context)
80
+ end
81
+
82
+ def fetch_integer_value(flag_key:, default_value:, evaluation_context: nil)
83
+ evaluate(flag_key, default_value: default_value, expected_type: 'integer', evaluation_context: evaluation_context)
84
+ end
85
+
86
+ def fetch_float_value(flag_key:, default_value:, evaluation_context: nil)
87
+ evaluate(flag_key, default_value: default_value, expected_type: 'float', evaluation_context: evaluation_context)
88
+ end
89
+
90
+ def fetch_object_value(flag_key:, default_value:, evaluation_context: nil)
91
+ evaluate(flag_key, default_value: default_value, expected_type: 'object', evaluation_context: evaluation_context)
92
+ end
93
+
94
+ private
95
+
96
+ def evaluate(flag_key, default_value:, expected_type:, evaluation_context:)
97
+ engine = OpenFeature.engine
98
+ return component_not_configured_default(default_value) if engine.nil?
99
+
100
+ result = engine.fetch_value(
101
+ flag_key: flag_key,
102
+ default_value: default_value,
103
+ expected_type: expected_type,
104
+ evaluation_context: evaluation_context
105
+ )
106
+
107
+ if result.error?
108
+ return ::OpenFeature::SDK::Provider::ResolutionDetails.new(
109
+ value: result.value,
110
+ error_code: result.error_code,
111
+ error_message: result.error_message,
112
+ reason: result.reason
113
+ )
114
+ end
115
+
116
+ ::OpenFeature::SDK::Provider::ResolutionDetails.new(
117
+ value: result.value,
118
+ variant: result.variant,
119
+ reason: result.reason,
120
+ flag_metadata: result.flag_metadata
121
+ )
122
+ end
123
+
124
+ def component_not_configured_default(value)
125
+ ::OpenFeature::SDK::Provider::ResolutionDetails.new(
126
+ value: value,
127
+ error_code: Ext::PROVIDER_FATAL,
128
+ error_message: "Datadog's OpenFeature component must be configured",
129
+ reason: Ext::ERROR
130
+ )
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/remote/dispatcher'
4
+
5
+ module Datadog
6
+ module OpenFeature
7
+ # This module contains the remote configuration functionality for OpenFeature
8
+ module Remote
9
+ ReadError = Class.new(StandardError)
10
+
11
+ class << self
12
+ FFE_FLAG_CONFIGURATION_RULES = 1 << 46
13
+ FFE_PRODUCTS = ['FFE_FLAGS'].freeze
14
+ FFE_CAPABILITIES = [FFE_FLAG_CONFIGURATION_RULES].freeze
15
+
16
+ def capabilities
17
+ FFE_CAPABILITIES
18
+ end
19
+
20
+ def products
21
+ FFE_PRODUCTS
22
+ end
23
+
24
+ def receivers(telemetry)
25
+ matcher = Core::Remote::Dispatcher::Matcher::Product.new(FFE_PRODUCTS)
26
+ receiver = Core::Remote::Dispatcher::Receiver.new(matcher) do |repository, changes|
27
+ engine = OpenFeature.engine
28
+ break unless engine
29
+
30
+ changes.each do |change|
31
+ content = repository[change.path]
32
+
33
+ unless content || change.type == :delete
34
+ next telemetry.error("OpenFeature: Remote Configuration change is not present on #{change.type}")
35
+ end
36
+
37
+ # NOTE: In the current RC implementation we immediately apply the configuration,
38
+ # but that might change if we need to apply patches instead.
39
+ case change.type
40
+ when :insert, :update
41
+ begin
42
+ # @type var content: Core::Remote::Configuration::Content
43
+ engine.reconfigure!(read_content(content))
44
+ content.applied
45
+ rescue ReadError => e
46
+ content.errored("Error reading Remote Configuration content: #{e.message}")
47
+ rescue EvaluationEngine::ReconfigurationError => e
48
+ content.errored("Error applying OpenFeature configuration: #{e.message}")
49
+ end
50
+ when :delete
51
+ # NOTE: For now, we treat deletion as clearing the configuration
52
+ # In a multi-config scenario, we might track configs per path
53
+ engine.reconfigure!(nil)
54
+ end
55
+ end
56
+ end
57
+
58
+ [receiver]
59
+ end
60
+
61
+ private
62
+
63
+ 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
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ext'
4
+
5
+ module Datadog
6
+ module OpenFeature
7
+ # This class is based on the `OpenFeature::SDK::Provider::ResolutionDetails` class
8
+ #
9
+ # See: https://github.com/open-feature/ruby-sdk/blob/v0.4.1/lib/open_feature/sdk/provider/resolution_details.rb
10
+ class ResolutionDetails < Struct.new(
11
+ :value,
12
+ :reason,
13
+ :variant,
14
+ :error_code,
15
+ :error_message,
16
+ :flag_metadata,
17
+ :allocation_key,
18
+ :extra_logging,
19
+ :log?,
20
+ :error?,
21
+ keyword_init: true
22
+ )
23
+ def self.build_error(value:, error_code:, error_message:, reason: Ext::ERROR)
24
+ new(
25
+ value: value,
26
+ error_code: error_code,
27
+ error_message: error_message,
28
+ reason: reason,
29
+ error?: true,
30
+ log?: false
31
+ ).freeze
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/transport/http'
4
+ require_relative '../core/transport/http/env'
5
+ require_relative '../core/transport/parcel'
6
+ require_relative '../core/transport/request'
7
+
8
+ module Datadog
9
+ module OpenFeature
10
+ module Transport
11
+ class EncodedParcel
12
+ include Core::Transport::Parcel
13
+
14
+ def encode_with(encoder)
15
+ encoder.encode(data)
16
+ end
17
+ end
18
+
19
+ class HTTP
20
+ class Spec < Core::Transport::HTTP::API::Spec
21
+ def initialize
22
+ @endpoint = Core::Transport::HTTP::API::Endpoint.new(
23
+ :post, '/evp_proxy/v2/api/v2/exposures'
24
+ )
25
+
26
+ super
27
+ end
28
+
29
+ def call(env, &block)
30
+ @endpoint.call(env) do |request_env|
31
+ request_env.headers['Content-Type'] = Core::Encoding::JSONEncoder.content_type
32
+ request_env.headers['X-Datadog-EVP-Subdomain'] = 'event-platform-intake'
33
+ request_env.body = env.request.parcel.encode_with(Core::Encoding::JSONEncoder)
34
+
35
+ block.call(request_env)
36
+ end
37
+ end
38
+ end
39
+
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
+ def self.build(agent_settings:, logger:)
47
+ Core::Transport::HTTP.build(
48
+ api_instance_class: HTTP::Instance,
49
+ agent_settings: agent_settings,
50
+ logger: logger
51
+ ) { |t| t.api('exposures', HTTP::Spec.new) }.to_transport(self)
52
+ end
53
+
54
+ def initialize(apis, default_api, logger:)
55
+ @api = apis[default_api]
56
+ @logger = logger
57
+ end
58
+
59
+ def send_exposures(payload)
60
+ request = Core::Transport::Request.new(EncodedParcel.new(payload))
61
+ @api.send_exposures(Core::Transport::HTTP::Env.new(request))
62
+ rescue => e
63
+ message = "Internal error during request. Cause: #{e.class.name} #{e.message} " \
64
+ "Location: #{Array(e.backtrace).first}"
65
+ @logger.debug(message)
66
+
67
+ Core::Transport::InternalErrorResponse.new(e)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core/configuration'
4
+ require_relative 'open_feature/configuration'
5
+
6
+ module Datadog
7
+ # A namespace for the OpenFeature component.
8
+ module OpenFeature
9
+ Core::Configuration::Settings.extend(Configuration::Settings)
10
+
11
+ def self.enabled?
12
+ Datadog.configuration.open_feature.enabled
13
+ end
14
+
15
+ def self.engine
16
+ Datadog.send(:components).open_feature&.engine
17
+ end
18
+ end
19
+ end
@@ -220,6 +220,12 @@ module Datadog
220
220
  "Please upgrade to Ruby >= 3.1 in order to use this feature. Heap profiling has been disabled."
221
221
  )
222
222
  return false
223
+ elsif RUBY_VERSION.start_with?("4.")
224
+ logger.warn(
225
+ "Heap profiling is not supported in current Ruby version (#{RUBY_VERSION}) due to https://bugs.ruby-lang.org/issues/21710. " \
226
+ "Heap profiling has been disabled."
227
+ )
228
+ return false
223
229
  end
224
230
 
225
231
  unless allocation_profiling_enabled
@@ -17,6 +17,10 @@ module Datadog
17
17
  @scheduler = scheduler
18
18
  end
19
19
 
20
+ def enabled?
21
+ scheduler.running?
22
+ end
23
+
20
24
  def start
21
25
  after_fork! do
22
26
  worker.reset_after_fork
@@ -62,8 +62,7 @@ module Datadog
62
62
 
63
63
  def self.enabled?
64
64
  profiler = Datadog.send(:components).profiler
65
- # Use .send(...) to avoid exposing the attr_reader as an API to the outside
66
- !!profiler&.send(:scheduler)&.running?
65
+ !!profiler&.enabled?
67
66
  end
68
67
 
69
68
  def self.wait_until_running(timeout_seconds: 5)
@@ -15,7 +15,7 @@ end
15
15
 
16
16
  begin
17
17
  require_relative 'auto_instrument'
18
- Datadog::SingleStepInstrument::LOADED = true
18
+ Datadog::SingleStepInstrument.const_set(:LOADED, true)
19
19
  rescue StandardError, LoadError => e
20
20
  warn "Single step instrumentation failed: #{e.class}:#{e.message}\n\tSource:\n\t#{Array(e.backtrace).join("\n\t")}"
21
21
  end