datadog 2.31.0 → 2.32.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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +17 -7
  3. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +11 -4
  4. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +6 -0
  5. data/ext/datadog_profiling_native_extension/extconf.rb +5 -4
  6. data/ext/datadog_profiling_native_extension/http_transport.c +10 -5
  7. data/ext/libdatadog_api/di.c +48 -0
  8. data/ext/libdatadog_api/extconf.rb +7 -4
  9. data/ext/libdatadog_extconf_helpers.rb +37 -0
  10. data/lib/datadog/ai_guard/configuration.rb +105 -2
  11. data/lib/datadog/ai_guard/evaluation.rb +1 -0
  12. data/lib/datadog/ai_guard/ext.rb +1 -0
  13. data/lib/datadog/appsec/autoload.rb +1 -1
  14. data/lib/datadog/appsec/component.rb +1 -1
  15. data/lib/datadog/appsec/configuration.rb +414 -1
  16. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +2 -1
  17. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  18. data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
  19. data/lib/datadog/appsec/metrics/telemetry.rb +13 -1
  20. data/lib/datadog/appsec/security_engine/runner.rb +1 -1
  21. data/lib/datadog/appsec/trace_keeper.rb +18 -6
  22. data/lib/datadog/appsec/utils/http/url_encoded.rb +2 -2
  23. data/lib/datadog/core/configuration/components.rb +1 -1
  24. data/lib/datadog/core/configuration/settings.rb +3 -0
  25. data/lib/datadog/core/configuration/supported_configurations.rb +2 -0
  26. data/lib/datadog/core/configuration.rb +1 -1
  27. data/lib/datadog/core/contrib/rails/utils.rb +1 -1
  28. data/lib/datadog/core/crashtracking/component.rb +3 -3
  29. data/lib/datadog/core/diagnostics/environment_logger.rb +3 -1
  30. data/lib/datadog/core/environment/container.rb +2 -2
  31. data/lib/datadog/core/feature_flags.rb +1 -1
  32. data/lib/datadog/core/metrics/client.rb +5 -5
  33. data/lib/datadog/core/remote/client.rb +1 -1
  34. data/lib/datadog/core/remote/component.rb +2 -2
  35. data/lib/datadog/core/runtime/metrics.rb +1 -1
  36. data/lib/datadog/core/telemetry/emitter.rb +1 -1
  37. data/lib/datadog/core/telemetry/event/app_started.rb +2 -2
  38. data/lib/datadog/core/transport/http.rb +2 -0
  39. data/lib/datadog/core/utils.rb +1 -1
  40. data/lib/datadog/core/workers/async.rb +1 -1
  41. data/lib/datadog/core.rb +1 -1
  42. data/lib/datadog/data_streams/configuration.rb +40 -1
  43. data/lib/datadog/data_streams/pathway_context.rb +1 -1
  44. data/lib/datadog/data_streams/processor.rb +1 -1
  45. data/lib/datadog/data_streams.rb +1 -1
  46. data/lib/datadog/di/base.rb +8 -5
  47. data/lib/datadog/di/code_tracker.rb +179 -1
  48. data/lib/datadog/di/component.rb +1 -1
  49. data/lib/datadog/di/configuration.rb +235 -2
  50. data/lib/datadog/di/instrumenter.rb +46 -26
  51. data/lib/datadog/di/probe_builder.rb +1 -1
  52. data/lib/datadog/di/probe_file_loader.rb +2 -2
  53. data/lib/datadog/di/probe_manager.rb +6 -6
  54. data/lib/datadog/di/probe_notification_builder.rb +1 -1
  55. data/lib/datadog/di/probe_notifier_worker.rb +2 -2
  56. data/lib/datadog/di/remote.rb +6 -6
  57. data/lib/datadog/di/serializer.rb +1 -1
  58. data/lib/datadog/di/transport/input.rb +3 -3
  59. data/lib/datadog/error_tracking/configuration.rb +55 -2
  60. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  61. data/lib/datadog/open_feature/component.rb +18 -1
  62. data/lib/datadog/open_feature/evaluation_engine.rb +3 -3
  63. data/lib/datadog/open_feature/exposures/reporter.rb +1 -1
  64. data/lib/datadog/open_feature/exposures/worker.rb +1 -1
  65. data/lib/datadog/open_feature/hooks/flag_eval_hook.rb +49 -0
  66. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  67. data/lib/datadog/open_feature/provider.rb +19 -1
  68. data/lib/datadog/open_feature/remote.rb +1 -1
  69. data/lib/datadog/open_feature/transport.rb +1 -1
  70. data/lib/datadog/opentelemetry/metrics.rb +3 -3
  71. data/lib/datadog/opentelemetry/sdk/configurator.rb +1 -1
  72. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +1 -1
  73. data/lib/datadog/profiling/collectors/code_provenance.rb +35 -9
  74. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +31 -2
  75. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +8 -2
  76. data/lib/datadog/profiling/collectors/info.rb +16 -3
  77. data/lib/datadog/profiling/component.rb +3 -5
  78. data/lib/datadog/profiling/exporter.rb +37 -12
  79. data/lib/datadog/profiling/ext.rb +0 -2
  80. data/lib/datadog/profiling/flush.rb +21 -12
  81. data/lib/datadog/profiling/http_transport.rb +12 -1
  82. data/lib/datadog/profiling/load_native_extension.rb +1 -1
  83. data/lib/datadog/profiling/profiler.rb +13 -1
  84. data/lib/datadog/profiling/scheduler.rb +2 -2
  85. data/lib/datadog/profiling/tasks/exec.rb +8 -3
  86. data/lib/datadog/profiling/tasks/help.rb +1 -0
  87. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  88. data/lib/datadog/single_step_instrument.rb +1 -1
  89. data/lib/datadog/symbol_database/configuration.rb +65 -0
  90. data/lib/datadog/symbol_database/extractor.rb +915 -0
  91. data/lib/datadog/symbol_database/file_hash.rb +46 -0
  92. data/lib/datadog/symbol_database/logger.rb +43 -0
  93. data/lib/datadog/symbol_database/scope.rb +98 -0
  94. data/lib/datadog/symbol_database/service_version.rb +57 -0
  95. data/lib/datadog/symbol_database/symbol.rb +66 -0
  96. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  97. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  98. data/lib/datadog/symbol_database/transport.rb +54 -0
  99. data/lib/datadog/symbol_database/uploader.rb +166 -0
  100. data/lib/datadog/symbol_database.rb +49 -0
  101. data/lib/datadog/tracing/buffer.rb +3 -3
  102. data/lib/datadog/tracing/configuration/settings.rb +1 -1
  103. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -3
  104. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  105. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  106. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  107. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  108. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  109. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  110. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  111. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  112. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  113. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +2 -2
  114. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  115. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  116. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  117. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
  118. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  119. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
  120. data/lib/datadog/tracing/contrib/component.rb +1 -1
  121. data/lib/datadog/tracing/contrib/configuration/resolver.rb +7 -4
  122. data/lib/datadog/tracing/contrib/dalli/quantize.rb +1 -1
  123. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
  124. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
  125. data/lib/datadog/tracing/contrib/extensions.rb +9 -0
  126. data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
  127. data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -5
  128. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +2 -2
  129. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +2 -2
  130. data/lib/datadog/tracing/contrib/http/instrumentation.rb +2 -2
  131. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -2
  132. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +2 -2
  133. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +2 -2
  134. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +2 -2
  135. data/lib/datadog/tracing/contrib/karafka/patcher.rb +1 -1
  136. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +3 -3
  137. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
  138. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +3 -3
  139. data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
  140. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  141. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  142. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  143. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
  144. data/lib/datadog/tracing/contrib/redis/quantize.rb +1 -1
  145. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
  146. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +1 -1
  147. data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
  148. data/lib/datadog/tracing/contrib.rb +8 -0
  149. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -1
  150. data/lib/datadog/tracing/distributed/baggage.rb +59 -5
  151. data/lib/datadog/tracing/distributed/datadog.rb +11 -11
  152. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +1 -1
  153. data/lib/datadog/tracing/distributed/propagation.rb +2 -2
  154. data/lib/datadog/tracing/distributed/trace_context.rb +74 -32
  155. data/lib/datadog/tracing/event.rb +1 -1
  156. data/lib/datadog/tracing/metadata/tagging.rb +2 -2
  157. data/lib/datadog/tracing/pipeline.rb +1 -1
  158. data/lib/datadog/tracing/remote.rb +1 -1
  159. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  160. data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -2
  161. data/lib/datadog/tracing/sampling/span/rule_parser.rb +2 -2
  162. data/lib/datadog/tracing/span_operation.rb +3 -3
  163. data/lib/datadog/tracing/trace_operation.rb +4 -4
  164. data/lib/datadog/tracing/tracer.rb +5 -5
  165. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  166. data/lib/datadog/tracing/workers.rb +2 -1
  167. data/lib/datadog/version.rb +1 -1
  168. metadata +18 -9
  169. data/lib/datadog/ai_guard/configuration/settings.rb +0 -113
  170. data/lib/datadog/appsec/configuration/settings.rb +0 -423
  171. data/lib/datadog/data_streams/configuration/settings.rb +0 -49
  172. data/lib/datadog/di/configuration/settings.rb +0 -243
  173. data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
@@ -30,7 +30,7 @@ module Datadog
30
30
  Process.setrlimit(:CORE, maximum_size)
31
31
  rescue => e
32
32
  Kernel.warn(
33
- "[datadog] Failed to enable core dumps. Cause: #{e.class}: #{e} " \
33
+ "[datadog] Failed to enable core dumps. Cause: #{e.class}: #{e.message} " \
34
34
  "Location: #{Array(e.backtrace).first}"
35
35
  )
36
36
  return
@@ -6,12 +6,13 @@ require_relative 'exposures/buffer'
6
6
  require_relative 'exposures/worker'
7
7
  require_relative 'exposures/deduplicator'
8
8
  require_relative 'exposures/reporter'
9
+ require_relative 'metrics/flag_eval_metrics'
9
10
 
10
11
  module Datadog
11
12
  module OpenFeature
12
13
  # This class is the entry point for the OpenFeature component
13
14
  class Component
14
- attr_reader :engine
15
+ attr_reader :engine, :flag_eval_hook
15
16
 
16
17
  def self.build(settings, agent_settings, logger:, telemetry:)
17
18
  return unless settings.respond_to?(:open_feature) && settings.open_feature.enabled
@@ -50,11 +51,27 @@ module Datadog
50
51
 
51
52
  reporter = Exposures::Reporter.new(@worker, telemetry: telemetry, logger: logger)
52
53
  @engine = EvaluationEngine.new(reporter, telemetry: telemetry, logger: logger)
54
+
55
+ @telemetry = telemetry
56
+ @logger = logger
57
+ @flag_eval_hook = create_flag_eval_hook
53
58
  end
54
59
 
55
60
  def shutdown!
56
61
  @worker.graceful_shutdown
57
62
  end
63
+
64
+ private
65
+
66
+ def create_flag_eval_hook
67
+ require_relative 'hooks/flag_eval_hook'
68
+ return unless Hooks::FlagEvalHook.available?
69
+
70
+ metrics = Metrics::FlagEvalMetrics.new(telemetry: @telemetry, logger: @logger)
71
+ Hooks::FlagEvalHook.new(metrics)
72
+ rescue LoadError
73
+ nil
74
+ end
58
75
  end
59
76
  end
60
77
  end
@@ -42,7 +42,7 @@ module Datadog
42
42
  @telemetry.report(e, description: 'OpenFeature: Failed to fetch flag value')
43
43
 
44
44
  ResolutionDetails.build_error(
45
- value: default_value, error_code: Ext::GENERAL, error_message: e.message
45
+ value: default_value, error_code: Ext::GENERAL, error_message: "#{e.class}: #{e.message}"
46
46
  )
47
47
  end
48
48
 
@@ -60,10 +60,10 @@ module Datadog
60
60
  rescue => e
61
61
  message = 'OpenFeature: Failed to reconfigure, reverting to the previous configuration'
62
62
 
63
- @logger.error("#{message}, #{e.class}: #{e}")
63
+ @logger.error("#{message}, #{e.class}: #{e.message}")
64
64
  @telemetry.report(e, description: "#{message} (#{e.class})")
65
65
 
66
- raise ReconfigurationError, e.message
66
+ raise ReconfigurationError, "#{e.class}: #{e.message}"
67
67
  end
68
68
  end
69
69
  end
@@ -29,7 +29,7 @@ module Datadog
29
29
  event = Event.build(result, flag_key: flag_key, context: context)
30
30
  @worker.enqueue(event)
31
31
  rescue => e
32
- @logger.debug { "OpenFeature: Failed to report resolution details: #{e.class}: #{e}" }
32
+ @logger.debug { "OpenFeature: Failed to report resolution details: #{e.class}: #{e.message}" }
33
33
  @telemetry.report(e, description: 'OpenFeature: Failed to report resolution details')
34
34
 
35
35
  false
@@ -105,7 +105,7 @@ module Datadog
105
105
 
106
106
  response
107
107
  rescue => e
108
- @logger.debug { "OpenFeature: Failed to flush resolution details events: #{e.class}: #{e}" }
108
+ @logger.debug { "OpenFeature: Failed to flush resolution details events: #{e.class}: #{e.message}" }
109
109
  @telemetry.report(e, description: 'OpenFeature: Failed to flush resolution details events')
110
110
 
111
111
  nil
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module OpenFeature
5
+ module Hooks
6
+ # Records flag evaluation metrics via OpenTelemetry hook
7
+ #
8
+ # Compatible with OpenFeature SDK >= 0.5.0 which provides the Hooks::Hook module,
9
+ # but also works with older versions since the SDK uses respond_to?(:finally)
10
+ # to detect hook capabilities.
11
+ class FlagEvalHook
12
+ # Include the Hook module if available (SDK >= 0.5.0) for interface documentation
13
+ # and default implementations of other hook methods (before, after, error)
14
+ include ::OpenFeature::SDK::Hooks::Hook if defined?(::OpenFeature::SDK::Hooks::Hook)
15
+
16
+ # Returns true if the OpenFeature SDK supports hooks (>= 0.5.0)
17
+ def self.available?
18
+ !!defined?(::OpenFeature::SDK::Hooks::Hook)
19
+ end
20
+
21
+ def initialize(metrics)
22
+ @metrics = metrics
23
+ end
24
+
25
+ def finally(hook_context:, evaluation_details:, **_opts)
26
+ metrics = @metrics
27
+ return unless metrics
28
+
29
+ metrics.record(
30
+ hook_context.flag_key,
31
+ variant: evaluation_details.variant,
32
+ reason: evaluation_details.reason,
33
+ error_code: evaluation_details.error_code,
34
+ allocation_key: extract_allocation_key(evaluation_details),
35
+ )
36
+ end
37
+
38
+ private
39
+
40
+ def extract_allocation_key(evaluation_details)
41
+ metadata = evaluation_details.flag_metadata
42
+ return unless metadata.is_a?(Hash)
43
+
44
+ metadata['__dd_allocation_key']
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module OpenFeature
5
+ module Metrics
6
+ # Records flag evaluation metrics via OpenTelemetry
7
+ class FlagEvalMetrics
8
+ METER_NAME = 'ddtrace.openfeature'
9
+ METRIC_NAME = 'feature_flag.evaluations'
10
+ METRIC_UNIT = '{evaluation}'
11
+ METRIC_DESCRIPTION = 'Number of feature flag evaluations'
12
+
13
+ ATTR_FLAG_KEY = 'feature_flag.key'
14
+ ATTR_VARIANT = 'feature_flag.result.variant'
15
+ ATTR_REASON = 'feature_flag.result.reason'
16
+ ATTR_ALLOCATION_KEY = 'feature_flag.result.allocation_key'
17
+ ATTR_ERROR_TYPE = 'error.type'
18
+
19
+ DEFAULT_ERROR_TYPE = 'general'
20
+
21
+ ERROR_TYPE_MAP = {
22
+ 'FLAG_NOT_FOUND' => 'flag_not_found',
23
+ 'TYPE_MISMATCH' => 'type_mismatch',
24
+ 'PARSE_ERROR' => 'parse_error',
25
+ 'PROVIDER_NOT_READY' => 'provider_not_ready',
26
+ 'TARGETING_KEY_MISSING' => 'targeting_key_missing',
27
+ 'INVALID_CONTEXT' => 'invalid_context',
28
+ 'PROVIDER_FATAL' => 'provider_fatal',
29
+ 'GENERAL' => DEFAULT_ERROR_TYPE,
30
+ }.freeze
31
+
32
+ # Reasons that should not include allocation_key in metrics
33
+ EXCLUDE_ALLOCATION_KEY_REASONS = %w[error default disabled].freeze
34
+
35
+ def initialize(telemetry:, logger:)
36
+ @telemetry = telemetry
37
+ @logger = logger
38
+ @enabled = Datadog.configuration.opentelemetry.metrics.enabled
39
+ @counter = nil
40
+ @mutex = Mutex.new
41
+
42
+ unless @enabled
43
+ @logger.debug { 'OpenFeature: OTel metrics not enabled (DD_METRICS_OTEL_ENABLED=false), flag evaluation metrics disabled' }
44
+ end
45
+ end
46
+
47
+ def record(flag_key, variant:, reason:, error_code: nil, allocation_key: nil)
48
+ return unless @enabled
49
+
50
+ counter = get_or_create_counter
51
+ return unless counter
52
+
53
+ attributes = build_attributes(
54
+ flag_key,
55
+ variant: variant,
56
+ reason: reason,
57
+ error_code: error_code,
58
+ allocation_key: allocation_key,
59
+ )
60
+ counter.add(1, attributes: attributes)
61
+ rescue => e
62
+ @logger.debug { "OpenFeature: Failed to record evaluation metric: #{e.class}: #{e.message}" }
63
+ @telemetry.report(e, description: 'OpenFeature: Failed to record evaluation metric')
64
+ end
65
+
66
+ private
67
+
68
+ # Counter is created lazily because OTel SDK may not be initialized
69
+ # when the OpenFeature component is created.
70
+ def get_or_create_counter
71
+ @mutex.synchronize do
72
+ return @counter if @counter
73
+
74
+ meter_provider = get_or_initialize_meter_provider
75
+ return unless meter_provider
76
+
77
+ meter = meter_provider.meter(METER_NAME)
78
+ @counter = meter.create_counter(
79
+ METRIC_NAME,
80
+ unit: METRIC_UNIT,
81
+ description: METRIC_DESCRIPTION
82
+ )
83
+ end
84
+ rescue => e
85
+ @logger.debug { "OpenFeature: Failed to create metrics counter: #{e.class}: #{e.message}" }
86
+ nil
87
+ end
88
+
89
+ # Fetch an available OTel meter provider, initializing if needed.
90
+ # Returns the meter provider if available, nil otherwise.
91
+ def get_or_initialize_meter_provider
92
+ meter_provider = defined?(::OpenTelemetry) ? ::OpenTelemetry.meter_provider : nil
93
+ return meter_provider if sdk_meter_provider?(meter_provider)
94
+
95
+ @logger.debug { 'OpenFeature: Initializing OTel meter provider directly' }
96
+ require 'opentelemetry-metrics-sdk'
97
+ require 'datadog/opentelemetry/metrics'
98
+ Datadog::OpenTelemetry::Metrics.initialize!(Datadog.send(:components))
99
+
100
+ meter_provider = ::OpenTelemetry.meter_provider
101
+ sdk_meter_provider?(meter_provider) ? meter_provider : nil
102
+ rescue LoadError => e
103
+ @logger.debug { "OpenFeature: Failed to initialize OTel metrics: #{e.class}: #{e.message}" }
104
+ nil
105
+ end
106
+
107
+ def sdk_meter_provider?(meter_provider)
108
+ return false if meter_provider.nil?
109
+ return false unless defined?(::OpenTelemetry::SDK::Metrics::MeterProvider)
110
+
111
+ meter_provider.is_a?(::OpenTelemetry::SDK::Metrics::MeterProvider)
112
+ end
113
+
114
+ def build_attributes(flag_key, variant:, reason:, error_code:, allocation_key:)
115
+ reason_downcase = normalize_reason(reason)
116
+
117
+ attrs = {
118
+ ATTR_FLAG_KEY => flag_key,
119
+ ATTR_VARIANT => variant.to_s,
120
+ ATTR_REASON => reason_downcase,
121
+ }
122
+
123
+ if allocation_key && !allocation_key.empty? && !exclude_allocation_key?(reason_downcase)
124
+ attrs[ATTR_ALLOCATION_KEY] = allocation_key
125
+ end
126
+
127
+ if error_code
128
+ attrs[ATTR_ERROR_TYPE] = normalize_error_type(error_code)
129
+ end
130
+
131
+ attrs
132
+ end
133
+
134
+ def normalize_reason(reason)
135
+ reason = reason.to_s
136
+ reason.empty? ? 'unknown' : reason.downcase
137
+ end
138
+
139
+ def normalize_error_type(error_code)
140
+ ERROR_TYPE_MAP.fetch(error_code.to_s, DEFAULT_ERROR_TYPE)
141
+ end
142
+
143
+ def exclude_allocation_key?(reason_downcase)
144
+ EXCLUDE_ALLOCATION_KEY_REASONS.include?(reason_downcase)
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -7,6 +7,8 @@ module Datadog
7
7
  module OpenFeature
8
8
  # OpenFeature feature flagging provider backed by Datadog Remote Configuration.
9
9
  #
10
+ # Requires openfeature-sdk >= 0.5.1 for flag evaluation metrics support.
11
+ #
10
12
  # Implementation follows the OpenFeature contract of Provider SDK.
11
13
  # For details see:
12
14
  # - https://github.com/open-feature/ruby-sdk/blob/v0.4.1/README.md#develop-a-provider
@@ -67,6 +69,11 @@ module Datadog
67
69
  # no-op
68
70
  end
69
71
 
72
+ def hooks
73
+ hook = Datadog.send(:components).open_feature&.flag_eval_hook
74
+ [hook].compact
75
+ end
76
+
70
77
  def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil)
71
78
  evaluate(flag_key, default_value: default_value, expected_type: :boolean, evaluation_context: evaluation_context)
72
79
  end
@@ -117,7 +124,7 @@ module Datadog
117
124
  value: result.value,
118
125
  variant: result.variant,
119
126
  reason: result.reason,
120
- flag_metadata: result.flag_metadata
127
+ flag_metadata: build_flag_metadata(result),
121
128
  )
122
129
  rescue => e
123
130
  ::OpenFeature::SDK::Provider::ResolutionDetails.new(
@@ -128,6 +135,17 @@ module Datadog
128
135
  )
129
136
  end
130
137
 
138
+ def build_flag_metadata(result)
139
+ metadata = result.flag_metadata || {}
140
+ allocation_key = result.allocation_key
141
+ if allocation_key && !allocation_key.empty?
142
+ metadata = metadata.dup
143
+ metadata['__dd_allocation_key'] = allocation_key
144
+ end
145
+
146
+ metadata
147
+ end
148
+
131
149
  def component_not_configured_default(value)
132
150
  ::OpenFeature::SDK::Provider::ResolutionDetails.new(
133
151
  value: value,
@@ -41,7 +41,7 @@ module Datadog
41
41
  engine.reconfigure!(read_content(content))
42
42
  content.applied
43
43
  rescue EvaluationEngine::ReconfigurationError => e
44
- content.errored("Error applying OpenFeature configuration: #{e.class}: #{e}")
44
+ content.errored("Error applying OpenFeature configuration: #{e.class}: #{e.message}")
45
45
  end
46
46
  when :delete
47
47
  # NOTE: For now, we treat deletion as clearing the configuration
@@ -55,7 +55,7 @@ module Datadog
55
55
  @api.call(env)
56
56
  end
57
57
  rescue => e
58
- message = "Internal error during request. Cause: #{e.class}: #{e} " \
58
+ message = "Internal error during request. Cause: #{e.class}: #{e.message} " \
59
59
  "Location: #{Array(e.backtrace).first}"
60
60
  @logger.debug(message)
61
61
 
@@ -11,7 +11,7 @@ module Datadog
11
11
  new(components).configure_metrics_sdk
12
12
  true
13
13
  rescue => exc
14
- components.logger.error("Failed to initialize OpenTelemetry metrics: #{exc.class}: #{exc}: #{exc.backtrace.join("\n")}")
14
+ components.logger.error("Failed to initialize OpenTelemetry metrics: #{exc.class}: #{exc.message}: #{exc.backtrace.join("\n")}")
15
15
  false
16
16
  end
17
17
 
@@ -67,7 +67,7 @@ module Datadog
67
67
 
68
68
  configure_otlp_exporter(provider)
69
69
  rescue => e
70
- @logger.warn("Failed to configure OTLP metrics exporter: #{e.class}: #{e}")
70
+ @logger.warn("Failed to configure OTLP metrics exporter: #{e.class}: #{e.message}")
71
71
  end
72
72
 
73
73
  def default_metrics_endpoint
@@ -101,7 +101,7 @@ module Datadog
101
101
  )
102
102
  provider.add_metric_reader(reader)
103
103
  rescue LoadError => e
104
- @logger.warn("Could not load OTLP metrics exporter: #{e.class}: #{e}")
104
+ @logger.warn("Could not load OTLP metrics exporter: #{e.class}: #{e.message}")
105
105
  end
106
106
 
107
107
  # Returns metrics config value if explicitly set, otherwise falls back to exporter config or computed default value.
@@ -37,7 +37,7 @@ module Datadog
37
37
  begin
38
38
  require 'opentelemetry-metrics-sdk'
39
39
  rescue LoadError => exc
40
- components.logger.warn("Failed to load OpenTelemetry metrics gems: #{exc.class}: #{exc}")
40
+ components.logger.warn("Failed to load OpenTelemetry metrics gems: #{exc.class}: #{exc.message}")
41
41
  return super
42
42
  end
43
43
 
@@ -19,7 +19,7 @@ module Datadog
19
19
  telemetry&.inc(TELEMETRY_NAMESPACE, metric_name, 1, tags: TELEMETRY_TAGS)
20
20
  result
21
21
  rescue => e
22
- Datadog.logger.error("Failed to export OpenTelemetry Metrics: #{e.class}: #{e}")
22
+ Datadog.logger.error("Failed to export OpenTelemetry Metrics: #{e.class}: #{e.message}")
23
23
  telemetry&.inc(TELEMETRY_NAMESPACE, METRIC_EXPORT_FAILURES, 1, tags: TELEMETRY_TAGS)
24
24
  raise
25
25
  end
@@ -14,6 +14,9 @@ module Datadog
14
14
  #
15
15
  # This class acts both as a collector (collecting data) as well as a recorder (records/serializes it)
16
16
  class CodeProvenance
17
+ # @rbs standard_library_path: ::String
18
+ # @rbs ruby_native_filename: ::String?
19
+ # @rbs return: void
17
20
  def initialize(
18
21
  standard_library_path: RbConfig::CONFIG.fetch("rubylibdir"),
19
22
  ruby_native_filename: Datadog::Profiling::Collectors::Stack._native_ruby_native_filename
@@ -35,6 +38,9 @@ module Datadog
35
38
  )
36
39
  end
37
40
 
41
+ # @rbs loaded_files: ::Array[::String]
42
+ # @rbs loaded_specs: ::Array[::Gem::BasicSpecification]
43
+ # @rbs return: self
38
44
  def refresh(loaded_files: $LOADED_FEATURES, loaded_specs: Gem.loaded_specs.values)
39
45
  record_loaded_specs(loaded_specs)
40
46
  record_loaded_files(loaded_files)
@@ -42,19 +48,20 @@ module Datadog
42
48
  self
43
49
  end
44
50
 
51
+ #: () -> ::String
45
52
  def generate_json
46
53
  JSON.generate(v1: seen_libraries.to_a)
47
54
  end
48
55
 
49
56
  private
50
57
 
51
- attr_reader \
52
- :libraries_by_name,
53
- :libraries_by_path,
54
- :seen_files,
55
- :seen_libraries,
56
- :executable_paths
58
+ attr_reader :libraries_by_name #: ::Hash[::String, Library]
59
+ attr_reader :libraries_by_path #: ::Hash[::String, Library]
60
+ attr_reader :seen_files #: ::Set[::String]
61
+ attr_reader :seen_libraries #: ::Set[Library]
62
+ attr_reader :executable_paths #: ::Array[::String]
57
63
 
64
+ #: (Library) -> void
58
65
  def record_library(library)
59
66
  libraries_by_name[library.name] = library
60
67
  libraries_by_path[library.path] = library
@@ -71,10 +78,12 @@ module Datadog
71
78
  #
72
79
  # Alternatively/in the future we could instead use a trie to match paths, but I doubt for the data sizes we're
73
80
  # looking at that a trie is that much faster than using Ruby's built-in native collections.
81
+ #: () -> void
74
82
  def sort_libraries_by_longest_path_first
75
83
  @libraries_by_path = @libraries_by_path.sort.reverse!.to_h
76
84
  end
77
85
 
86
+ #: (::Array[::Gem::BasicSpecification]) -> void
78
87
  def record_loaded_specs(loaded_specs)
79
88
  recorded_library = false
80
89
 
@@ -103,6 +112,7 @@ module Datadog
103
112
  sort_libraries_by_longest_path_first if recorded_library
104
113
  end
105
114
 
115
+ #: (::Array[::String]) -> void
106
116
  def record_loaded_files(loaded_files)
107
117
  loaded_files.each do |file_path|
108
118
  next if seen_files.include?(file_path)
@@ -125,6 +135,7 @@ module Datadog
125
135
  # bundler complaints here impacting the application. (Bundler tends to go "something is wrong, raise!" which
126
136
  # 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
137
  # questions usage" it's not what we want.)
138
+ #: () -> ::String?
128
139
  def bundler_bin_path
129
140
  return unless defined?(Bundler)
130
141
 
@@ -135,7 +146,7 @@ module Datadog
135
146
  rescue Exception => e # rubocop:disable Lint/RescueException
136
147
  Datadog.logger.debug(
137
148
  "CodeProvenance#bundler_bin_path failed. " \
138
- "Cause: #{e.class}: #{e} Location: #{Array(e.backtrace).first}"
149
+ "Cause: #{e.class}: #{e.message} Location: #{Array(e.backtrace).first}"
139
150
  )
140
151
  nil
141
152
  end
@@ -149,8 +160,21 @@ module Datadog
149
160
  # Thus, this class was setup to match the JSON output. Take this into consideration if you are adding new
150
161
  # fields. (Also, we have a spec for this)
151
162
  class Library
152
- attr_reader :kind, :name, :version
153
-
163
+ # @rbs @kind: ::String
164
+ # @rbs @name: ::String
165
+ # @rbs @version: ::String
166
+ # @rbs @paths: ::Array[::String]
167
+
168
+ attr_reader :kind #: ::String
169
+ attr_reader :name #: ::String
170
+ attr_reader :version #: ::String
171
+
172
+ # @rbs kind: ::String
173
+ # @rbs name: ::String
174
+ # @rbs version: ::String | ::Gem::Version
175
+ # @rbs path: ::String
176
+ # @rbs extra_paths: ::Array[::String?]
177
+ # @rbs return: void
154
178
  def initialize(kind:, name:, version:, path:, extra_paths:)
155
179
  extra_paths = Array(extra_paths).compact.reject(&:empty?).map { |p| p.dup.freeze }
156
180
  @kind = kind.freeze
@@ -160,10 +184,12 @@ module Datadog
160
184
  freeze
161
185
  end
162
186
 
187
+ #: (?::JSON::State?) -> ::String
163
188
  def to_json(arg = nil)
164
189
  {kind: @kind, name: @name, version: @version, paths: @paths}.to_json(arg)
165
190
  end
166
191
 
192
+ #: () -> ::String
167
193
  def path
168
194
  @paths.first
169
195
  end
@@ -9,12 +9,32 @@ module Datadog
9
9
  #
10
10
  # Methods prefixed with _native_ are implemented in `collectors_cpu_and_wall_time_worker.c`
11
11
  class CpuAndWallTimeWorker
12
+ # @rbs @worker_thread: untyped
13
+ # @rbs @start_stop_mutex: ::Thread::Mutex
14
+ # @rbs @failure_exception: ::Exception?
15
+ # @rbs @idle_sampling_helper: IdleSamplingHelper
16
+ # @rbs @wait_until_running_mutex: ::Thread::Mutex
17
+ # @rbs @wait_until_running_condition: ::Thread::ConditionVariable
18
+
12
19
  private
13
20
 
14
- attr_accessor :failure_exception
21
+ attr_accessor :failure_exception #: ::Exception?
15
22
 
16
23
  public
17
24
 
25
+ # @rbs gc_profiling_enabled: bool
26
+ # @rbs no_signals_workaround_enabled: bool
27
+ # @rbs thread_context_collector: Datadog::Profiling::Collectors::ThreadContext
28
+ # @rbs dynamic_sampling_rate_overhead_target_percentage: Float
29
+ # @rbs cpu_sampling_interval_ms: ::Integer
30
+ # @rbs idle_sampling_helper: Datadog::Profiling::Collectors::IdleSamplingHelper
31
+ # @rbs dynamic_sampling_rate_enabled: bool
32
+ # @rbs allocation_profiling_enabled: bool
33
+ # @rbs allocation_counting_enabled: bool
34
+ # @rbs gvl_profiling_enabled: bool
35
+ # @rbs sighandler_sampling_enabled: bool
36
+ # @rbs skip_idle_samples_for_testing: false
37
+ # @rbs return: void
18
38
  def initialize(
19
39
  gc_profiling_enabled:,
20
40
  no_signals_workaround_enabled:,
@@ -67,6 +87,8 @@ module Datadog
67
87
  @wait_until_running_condition = ConditionVariable.new
68
88
  end
69
89
 
90
+ # @rbs on_failure_proc: (^(?log_failure: bool) -> void)?
91
+ # @rbs return: bool?
70
92
  def start(on_failure_proc: nil)
71
93
  @start_stop_mutex.synchronize do
72
94
  return if @worker_thread&.alive?
@@ -93,7 +115,7 @@ module Datadog
93
115
  operation_name = self.class._native_failure_exception_during_operation(self).inspect
94
116
  Datadog.logger.warn(
95
117
  "CpuAndWallTimeWorker thread error. " \
96
- "Operation: #{operation_name} Cause: #{e.class}: #{e} Location: #{Array(e.backtrace).first}"
118
+ "Operation: #{operation_name} Cause: #{e.class}: #{e.message} Location: #{Array(e.backtrace).first}"
97
119
  )
98
120
  on_failure_proc&.call
99
121
  Datadog::Core::Telemetry::Logger.report(e, description: "CpuAndWallTimeWorker thread error: #{operation_name}")
@@ -105,6 +127,7 @@ module Datadog
105
127
  true
106
128
  end
107
129
 
130
+ #: () -> void
108
131
  def stop
109
132
  @start_stop_mutex.synchronize do
110
133
  Datadog.logger.debug("Requesting CpuAndWallTimeWorker thread shut down")
@@ -121,14 +144,17 @@ module Datadog
121
144
  end
122
145
  end
123
146
 
147
+ #: () -> true
124
148
  def reset_after_fork
125
149
  self.class._native_reset_after_fork(self)
126
150
  end
127
151
 
152
+ #: () -> ::Hash[::Symbol, untyped]
128
153
  def stats
129
154
  self.class._native_stats(self)
130
155
  end
131
156
 
157
+ #: () -> ::Hash[::Symbol, untyped]
132
158
  def stats_and_reset_not_thread_safe
133
159
  stats = self.stats
134
160
  self.class._native_stats_reset_not_thread_safe(self)
@@ -136,6 +162,8 @@ module Datadog
136
162
  end
137
163
 
138
164
  # Useful for testing, to e.g. make sure the profiler is running before we start running some code we want to observe
165
+ # @rbs timeout_seconds: ::Integer?
166
+ # @rbs return: true
139
167
  def wait_until_running(timeout_seconds: 5)
140
168
  @wait_until_running_mutex.synchronize do
141
169
  return true if self.class._native_is_running?(self)
@@ -152,6 +180,7 @@ module Datadog
152
180
 
153
181
  private
154
182
 
183
+ #: () -> void
155
184
  def signal_running
156
185
  @wait_until_running_mutex.synchronize { @wait_until_running_condition.broadcast }
157
186
  end
@@ -8,17 +8,22 @@ module Datadog
8
8
  #
9
9
  # Methods prefixed with _native_ are implemented in `collectors_idle_sampling_helper.c`
10
10
  class IdleSamplingHelper
11
+ # @rbs @worker_thread: untyped
12
+ # @rbs @start_stop_mutex: ::Thread::Mutex
13
+
11
14
  private
12
15
 
13
- attr_accessor :failure_exception
16
+ attr_accessor :failure_exception #: ::Exception?
14
17
 
15
18
  public
16
19
 
20
+ #: () -> void
17
21
  def initialize
18
22
  @worker_thread = nil
19
23
  @start_stop_mutex = Mutex.new
20
24
  end
21
25
 
26
+ #: () -> (nil | true)
22
27
  def start
23
28
  @start_stop_mutex.synchronize do
24
29
  return if @worker_thread&.alive?
@@ -39,7 +44,7 @@ module Datadog
39
44
  @failure_exception = e
40
45
  Datadog.logger.warn(
41
46
  "IdleSamplingHelper thread error. " \
42
- "Cause: #{e.class}: #{e} Location: #{Array(e.backtrace).first}"
47
+ "Cause: #{e.class}: #{e.message} Location: #{Array(e.backtrace).first}"
43
48
  )
44
49
  Datadog::Core::Telemetry::Logger.report(e, description: "IdleSamplingHelper thread error")
45
50
  end
@@ -50,6 +55,7 @@ module Datadog
50
55
  true
51
56
  end
52
57
 
58
+ #: () -> void
53
59
  def stop
54
60
  @start_stop_mutex.synchronize do
55
61
  Datadog.logger.debug("Requesting IdleSamplingHelper thread shut down")