datadog 2.31.0 → 2.33.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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/ext/datadog_profiling_native_extension/clock_id.h +9 -1
  3. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +73 -0
  4. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -1
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +17 -7
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +16 -5
  7. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +6 -0
  8. data/ext/datadog_profiling_native_extension/extconf.rb +8 -4
  9. data/ext/datadog_profiling_native_extension/http_transport.c +10 -5
  10. data/ext/datadog_profiling_native_extension/stack_recorder.c +3 -9
  11. data/ext/datadog_profiling_native_extension/time_helpers.h +1 -0
  12. data/ext/libdatadog_api/crashtracker.c +2 -0
  13. data/ext/libdatadog_api/di.c +48 -0
  14. data/ext/libdatadog_api/extconf.rb +7 -4
  15. data/ext/libdatadog_extconf_helpers.rb +38 -1
  16. data/lib/datadog/ai_guard/autoload.rb +10 -0
  17. data/lib/datadog/ai_guard/component.rb +1 -1
  18. data/lib/datadog/ai_guard/configuration.rb +105 -2
  19. data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
  20. data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
  21. data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
  22. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +83 -0
  23. data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
  24. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
  25. data/lib/datadog/ai_guard/evaluation.rb +2 -0
  26. data/lib/datadog/ai_guard/ext.rb +2 -0
  27. data/lib/datadog/ai_guard.rb +8 -0
  28. data/lib/datadog/appsec/autoload.rb +1 -1
  29. data/lib/datadog/appsec/component.rb +1 -1
  30. data/lib/datadog/appsec/configuration.rb +414 -1
  31. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
  32. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
  33. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
  34. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +111 -0
  35. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +2 -1
  36. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  37. data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
  38. data/lib/datadog/appsec/metrics/telemetry.rb +13 -1
  39. data/lib/datadog/appsec/security_engine/runner.rb +1 -1
  40. data/lib/datadog/appsec/trace_keeper.rb +18 -6
  41. data/lib/datadog/appsec/utils/http/url_encoded.rb +2 -2
  42. data/lib/datadog/appsec.rb +1 -0
  43. data/lib/datadog/core/configuration/components.rb +1 -1
  44. data/lib/datadog/core/configuration/settings.rb +13 -0
  45. data/lib/datadog/core/configuration/supported_configurations.rb +4 -0
  46. data/lib/datadog/core/configuration.rb +1 -1
  47. data/lib/datadog/core/contrib/rails/utils.rb +1 -1
  48. data/lib/datadog/core/crashtracking/component.rb +3 -3
  49. data/lib/datadog/core/diagnostics/environment_logger.rb +3 -1
  50. data/lib/datadog/core/environment/container.rb +2 -2
  51. data/lib/datadog/core/environment/ext.rb +1 -0
  52. data/lib/datadog/core/environment/socket.rb +13 -0
  53. data/lib/datadog/core/feature_flags.rb +1 -1
  54. data/lib/datadog/core/metrics/client.rb +5 -5
  55. data/lib/datadog/core/remote/client.rb +1 -1
  56. data/lib/datadog/core/remote/component.rb +2 -2
  57. data/lib/datadog/core/runtime/metrics.rb +1 -1
  58. data/lib/datadog/core/telemetry/emitter.rb +1 -1
  59. data/lib/datadog/core/telemetry/event/app_started.rb +2 -2
  60. data/lib/datadog/core/transport/http.rb +2 -0
  61. data/lib/datadog/core/utils.rb +1 -1
  62. data/lib/datadog/core/workers/async.rb +1 -1
  63. data/lib/datadog/core.rb +1 -1
  64. data/lib/datadog/data_streams/configuration.rb +40 -1
  65. data/lib/datadog/data_streams/pathway_context.rb +1 -1
  66. data/lib/datadog/data_streams/processor.rb +1 -1
  67. data/lib/datadog/data_streams.rb +1 -1
  68. data/lib/datadog/di/base.rb +8 -5
  69. data/lib/datadog/di/code_tracker.rb +179 -1
  70. data/lib/datadog/di/component.rb +1 -1
  71. data/lib/datadog/di/configuration.rb +235 -2
  72. data/lib/datadog/di/instrumenter.rb +46 -26
  73. data/lib/datadog/di/probe_builder.rb +1 -1
  74. data/lib/datadog/di/probe_file_loader.rb +2 -2
  75. data/lib/datadog/di/probe_manager.rb +6 -6
  76. data/lib/datadog/di/probe_notification_builder.rb +1 -1
  77. data/lib/datadog/di/probe_notifier_worker.rb +2 -2
  78. data/lib/datadog/di/remote.rb +6 -6
  79. data/lib/datadog/di/serializer.rb +1 -1
  80. data/lib/datadog/di/transport/input.rb +3 -3
  81. data/lib/datadog/error_tracking/configuration.rb +55 -2
  82. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  83. data/lib/datadog/open_feature/component.rb +18 -1
  84. data/lib/datadog/open_feature/evaluation_engine.rb +3 -3
  85. data/lib/datadog/open_feature/exposures/reporter.rb +1 -1
  86. data/lib/datadog/open_feature/exposures/worker.rb +1 -1
  87. data/lib/datadog/open_feature/hooks/flag_eval_hook.rb +49 -0
  88. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  89. data/lib/datadog/open_feature/provider.rb +19 -1
  90. data/lib/datadog/open_feature/remote.rb +1 -1
  91. data/lib/datadog/open_feature/transport.rb +1 -1
  92. data/lib/datadog/opentelemetry/metrics.rb +13 -4
  93. data/lib/datadog/opentelemetry/sdk/configurator.rb +1 -1
  94. data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
  95. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +1 -1
  96. data/lib/datadog/profiling/collectors/code_provenance.rb +35 -9
  97. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +31 -2
  98. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +8 -2
  99. data/lib/datadog/profiling/collectors/info.rb +16 -3
  100. data/lib/datadog/profiling/component.rb +3 -6
  101. data/lib/datadog/profiling/exporter.rb +37 -12
  102. data/lib/datadog/profiling/ext.rb +0 -2
  103. data/lib/datadog/profiling/flush.rb +21 -12
  104. data/lib/datadog/profiling/http_transport.rb +12 -1
  105. data/lib/datadog/profiling/load_native_extension.rb +1 -1
  106. data/lib/datadog/profiling/profiler.rb +13 -1
  107. data/lib/datadog/profiling/scheduler.rb +2 -2
  108. data/lib/datadog/profiling/stack_recorder.rb +0 -4
  109. data/lib/datadog/profiling/tasks/exec.rb +8 -3
  110. data/lib/datadog/profiling/tasks/help.rb +1 -0
  111. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  112. data/lib/datadog/single_step_instrument.rb +1 -1
  113. data/lib/datadog/symbol_database/configuration.rb +65 -0
  114. data/lib/datadog/symbol_database/extractor.rb +906 -0
  115. data/lib/datadog/symbol_database/file_hash.rb +46 -0
  116. data/lib/datadog/symbol_database/logger.rb +43 -0
  117. data/lib/datadog/symbol_database/scope.rb +102 -0
  118. data/lib/datadog/symbol_database/scope_batcher.rb +280 -0
  119. data/lib/datadog/symbol_database/service_version.rb +57 -0
  120. data/lib/datadog/symbol_database/symbol.rb +66 -0
  121. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  122. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  123. data/lib/datadog/symbol_database/transport.rb +54 -0
  124. data/lib/datadog/symbol_database/uploader.rb +169 -0
  125. data/lib/datadog/symbol_database.rb +49 -0
  126. data/lib/datadog/tracing/buffer.rb +3 -3
  127. data/lib/datadog/tracing/configuration/settings.rb +1 -1
  128. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -3
  129. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  130. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  131. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  132. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  133. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  134. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  135. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  136. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  137. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  138. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +2 -2
  139. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  140. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  141. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  142. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
  143. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  144. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
  145. data/lib/datadog/tracing/contrib/component.rb +1 -1
  146. data/lib/datadog/tracing/contrib/configuration/resolver.rb +7 -4
  147. data/lib/datadog/tracing/contrib/dalli/quantize.rb +1 -1
  148. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
  149. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
  150. data/lib/datadog/tracing/contrib/extensions.rb +9 -0
  151. data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
  152. data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -5
  153. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +2 -2
  154. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +2 -2
  155. data/lib/datadog/tracing/contrib/http/instrumentation.rb +2 -2
  156. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -2
  157. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +2 -2
  158. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +2 -2
  159. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +2 -2
  160. data/lib/datadog/tracing/contrib/karafka/patcher.rb +1 -1
  161. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +3 -3
  162. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
  163. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +3 -3
  164. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +6 -0
  165. data/lib/datadog/tracing/contrib/rack/ext.rb +27 -0
  166. data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
  167. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  168. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +117 -1
  169. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  170. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  171. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
  172. data/lib/datadog/tracing/contrib/redis/quantize.rb +1 -1
  173. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
  174. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +1 -1
  175. data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
  176. data/lib/datadog/tracing/contrib.rb +8 -0
  177. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -1
  178. data/lib/datadog/tracing/distributed/baggage.rb +59 -5
  179. data/lib/datadog/tracing/distributed/datadog.rb +11 -11
  180. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +1 -1
  181. data/lib/datadog/tracing/distributed/propagation.rb +2 -2
  182. data/lib/datadog/tracing/distributed/trace_context.rb +74 -32
  183. data/lib/datadog/tracing/event.rb +1 -1
  184. data/lib/datadog/tracing/metadata/tagging.rb +2 -2
  185. data/lib/datadog/tracing/pipeline.rb +1 -1
  186. data/lib/datadog/tracing/remote.rb +1 -1
  187. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  188. data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -2
  189. data/lib/datadog/tracing/sampling/span/rule_parser.rb +2 -2
  190. data/lib/datadog/tracing/span_operation.rb +3 -3
  191. data/lib/datadog/tracing/trace_operation.rb +4 -4
  192. data/lib/datadog/tracing/tracer.rb +6 -8
  193. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  194. data/lib/datadog/tracing/workers.rb +2 -1
  195. data/lib/datadog/version.rb +1 -1
  196. metadata +33 -12
  197. data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
  198. data/lib/datadog/ai_guard/configuration/settings.rb +0 -113
  199. data/lib/datadog/appsec/configuration/settings.rb +0 -423
  200. data/lib/datadog/data_streams/configuration/settings.rb +0 -49
  201. data/lib/datadog/di/configuration/settings.rb +0 -243
  202. data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
@@ -30,7 +30,7 @@ module Datadog
30
30
  install_pending_method_probes(tp.self)
31
31
  rescue => exc
32
32
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
33
- logger.debug { "di: unhandled exception in definition trace point: #{exc.class}: #{exc}" }
33
+ logger.debug { "di: unhandled exception in definition trace point: #{exc.class}: #{exc.message}" }
34
34
  telemetry&.report(exc, description: "Unhandled exception in definition trace point")
35
35
  # TODO test this path
36
36
  end
@@ -133,13 +133,13 @@ module Datadog
133
133
  # In "propagate all exceptions" mode we will try to instrument again.
134
134
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
135
135
 
136
- logger.debug { "di: error processing probe configuration: #{exc.class}: #{exc}" }
136
+ logger.debug { "di: error processing probe configuration: #{exc.class}: #{exc.message}" }
137
137
  telemetry&.report(exc, description: "Error processing probe configuration")
138
138
 
139
139
  payload = probe_notification_builder.build_errored(probe, exc)
140
140
  probe_notifier_worker.add_status(payload, probe: probe)
141
141
 
142
- probe_repository.add_failed(probe.id, "#{exc.class}: #{exc}")
142
+ probe_repository.add_failed(probe.id, "#{exc.class}: #{exc.message}")
143
143
 
144
144
  raise
145
145
  end
@@ -164,7 +164,7 @@ module Datadog
164
164
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
165
165
  # Silence all exceptions?
166
166
  # TODO should we propagate here and rescue upstream?
167
- logger.debug { "di: error removing #{probe.type} probe at #{probe.location} (#{probe.id}): #{exc.class}: #{exc}" }
167
+ logger.debug { "di: error removing #{probe.type} probe at #{probe.location} (#{probe.id}): #{exc.class}: #{exc.message}" }
168
168
  telemetry&.report(exc, description: "Error removing probe")
169
169
  end
170
170
  end
@@ -193,13 +193,13 @@ module Datadog
193
193
  rescue => exc
194
194
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
195
195
 
196
- logger.debug { "di: error installing #{probe.type} probe at #{probe.location} (#{probe.id}) after class is defined: #{exc.class}: #{exc}" }
196
+ logger.debug { "di: error installing #{probe.type} probe at #{probe.location} (#{probe.id}) after class is defined: #{exc.class}: #{exc.message}" }
197
197
  telemetry&.report(exc, description: "Error installing probe after class is defined")
198
198
 
199
199
  payload = probe_notification_builder.build_errored(probe, exc)
200
200
  probe_notifier_worker.add_status(payload, probe: probe)
201
201
 
202
- probe_repository.add_failed(probe.id, "#{exc.class}: #{exc}")
202
+ probe_repository.add_failed(probe.id, "#{exc.class}: #{exc.message}")
203
203
  end
204
204
  end
205
205
  end
@@ -368,7 +368,7 @@ module Datadog
368
368
  end
369
369
  rescue => exc
370
370
  evaluation_errors << {
371
- message: "#{exc.class}: #{exc}",
371
+ message: "#{exc.class}: #{exc.message}",
372
372
  expr: segment.dsl_expr,
373
373
  }
374
374
  '[evaluation error]'
@@ -95,7 +95,7 @@ module Datadog
95
95
  rescue => exc
96
96
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
97
97
 
98
- logger.debug { "di: error in probe notifier worker: #{exc.class}: #{exc} (at #{exc.backtrace.first})" }
98
+ logger.debug { "di: error in probe notifier worker: #{exc.class}: #{exc.message} (at #{exc.backtrace.first})" }
99
99
  telemetry&.report(exc, description: "Error in probe notifier worker")
100
100
  end
101
101
  @lock.synchronize do
@@ -320,7 +320,7 @@ module Datadog
320
320
  end
321
321
  rescue => exc
322
322
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
323
- logger.debug { "di: failed to send #{event_name}: #{exc.class}: #{exc} (at #{exc.backtrace.first})" }
323
+ logger.debug { "di: failed to send #{event_name}: #{exc.class}: #{exc.message} (at #{exc.backtrace.first})" }
324
324
  telemetry&.report(exc, description: "Error sending #{event_type}")
325
325
  end
326
326
  end
@@ -88,11 +88,11 @@ module Datadog
88
88
  # content as errored but with a somewhat different exception
89
89
  # message.
90
90
  # TODO assert content state (errored for this example)
91
- content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
91
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class}: #{exc.message}")
92
92
  rescue => exc
93
93
  raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
94
94
 
95
- component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc}" }
95
+ component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc.message}" }
96
96
  component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
97
97
 
98
98
  # TODO test this path
@@ -106,7 +106,7 @@ module Datadog
106
106
  # content as errored but with a somewhat different exception
107
107
  # message.
108
108
  # TODO assert content state (errored for this example)
109
- content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
109
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class}: #{exc.message}")
110
110
  end
111
111
 
112
112
  # Important: even if processing fails for this probe config,
@@ -117,11 +117,11 @@ module Datadog
117
117
  rescue => exc
118
118
  raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
119
119
 
120
- component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc}" }
120
+ component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc.message}" }
121
121
  component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
122
122
 
123
123
  # TODO assert content state (errored for this example)
124
- content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
124
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class}: #{exc.message}")
125
125
  end
126
126
 
127
127
  # This method does not mark +previous_content+ as succeeded or errored,
@@ -136,7 +136,7 @@ module Datadog
136
136
  rescue => exc
137
137
  raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
138
138
 
139
- component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
139
+ component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc.message}" }
140
140
  component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
141
141
  end
142
142
 
@@ -186,7 +186,7 @@ module Datadog
186
186
  # surface errors so they can fix their serializers) or they may be defined
187
187
  # internally by dd-trace-rb (in which case we need to fix them). We use
188
188
  # WARN level to surface these errors in either case.
189
- Datadog.logger.warn("DI: Custom serializer condition failed: #{e.class}: #{e}")
189
+ Datadog.logger.warn("DI: Custom serializer condition failed: #{e.class}: #{e.message}")
190
190
  telemetry&.report(e, description: "Custom serializer condition failed")
191
191
  next
192
192
  end
@@ -81,14 +81,14 @@ module Datadog
81
81
  # Serialization failed for this snapshot - report via callback
82
82
  # This catches JSON::GeneratorError, Encoding errors, TypeError, etc.
83
83
  probe_id = snapshot.dig(:debugger, :snapshot, :probe, :id)
84
- logger.debug { "di: JSON encoding failed for snapshot (probe #{probe_id}): #{exc.class}: #{exc}" }
84
+ logger.debug { "di: JSON encoding failed for snapshot (probe #{probe_id}): #{exc.class}: #{exc.message}" }
85
85
  telemetry&.report(exc, description: "JSON encoding failed for snapshot")
86
86
 
87
87
  if probe_id
88
88
  begin
89
89
  on_serialization_error.call(probe_id, exc)
90
90
  rescue => callback_exc
91
- logger.debug { "di: error in serialization error callback for probe #{probe_id}: #{callback_exc.class}: #{callback_exc}" }
91
+ logger.debug { "di: error in serialization error callback for probe #{probe_id}: #{callback_exc.class}: #{callback_exc.message}" }
92
92
  telemetry&.report(callback_exc, description: "Error in serialization error callback")
93
93
  end
94
94
  end
@@ -110,7 +110,7 @@ module Datadog
110
110
  begin
111
111
  send_input_chunk(chunked_payload, serialized_tags)
112
112
  rescue => exc
113
- logger.debug { "di: failed to send snapshot chunk: #{exc.class}: #{exc} (at #{exc.backtrace.first})" }
113
+ logger.debug { "di: failed to send snapshot chunk: #{exc.class}: #{exc.message} (at #{exc.backtrace.first})" }
114
114
  telemetry&.report(exc, description: "Error sending snapshot chunk")
115
115
  end
116
116
  end
@@ -1,11 +1,64 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'ext'
4
+
3
5
  module Datadog
4
6
  module ErrorTracking
5
7
  # Configuration for ErrorTracking
6
8
  module Configuration
9
+ # Settings
10
+ module Settings
11
+ def self.extended(base)
12
+ base = base.singleton_class unless base.is_a?(Class)
13
+ add_settings!(base)
14
+ end
15
+
16
+ def self.add_settings!(base)
17
+ base.class_eval do
18
+ # Error Tracking specific configurations.
19
+ # @public_api
20
+ settings :error_tracking do
21
+ # Enable automatic reporting of handled errors and defines the scope
22
+ # for which to report errors: user code, gems, or both. Possible
23
+ # values are: all | user | third_party.
24
+ #
25
+ # @default 'DD_ERROR_TRACKING_HANDLED_ERRORS' environment variable, otherwise `nil`
26
+ # @return [String, nil]
27
+ option :handled_errors do |o|
28
+ o.type :string, nilable: true
29
+ o.default Ext::DEFAULT_HANDLED_ERRORS
30
+ o.env Ext::ENV_HANDLED_ERRORS
31
+ o.setter do |value|
32
+ next value if Ext::VALID_HANDLED_ERRORS.include?(value)
33
+
34
+ unless value.nil?
35
+ Datadog.logger.warn(
36
+ "Invalid handled errors scope: #{value}. " \
37
+ "Supported values are: #{Ext::VALID_HANDLED_ERRORS.join(" | ")}. " \
38
+ 'Deactivating the feature.'
39
+ )
40
+ end
41
+
42
+ Ext::DEFAULT_HANDLED_ERRORS
43
+ end
44
+ end
45
+
46
+ # Enable automatic reporting of handled errors and specify what files should be
47
+ # instrumented. The value is a list of comma separated paths, filenames or gem names.
48
+ # The paths can be absolute, starting with '/' or relative to directory in which the program
49
+ # is launched, starting with './'.
50
+ #
51
+ # @default 'DD_ERROR_TRACKING_HANDLED_ERRORS_MODULES' environment variable, otherwise `nil`
52
+ # @return [String, nil]
53
+ option :handled_errors_include do |o|
54
+ o.type :array
55
+ o.default []
56
+ o.env Ext::ENV_HANDLED_ERRORS_INCLUDE
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
7
62
  end
8
63
  end
9
64
  end
10
-
11
- require_relative 'configuration/settings'
@@ -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
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../core/configuration/ext'
4
+ require_relative '../core/environment/socket'
4
5
 
5
6
  module Datadog
6
7
  module OpenTelemetry
@@ -11,7 +12,7 @@ module Datadog
11
12
  new(components).configure_metrics_sdk
12
13
  true
13
14
  rescue => exc
14
- components.logger.error("Failed to initialize OpenTelemetry metrics: #{exc.class}: #{exc}: #{exc.backtrace.join("\n")}")
15
+ components.logger.error("Failed to initialize OpenTelemetry metrics: #{exc.class}: #{exc.message}: #{exc.backtrace.join("\n")}")
15
16
  false
16
17
  end
17
18
 
@@ -42,7 +43,6 @@ module Datadog
42
43
 
43
44
  def create_resource
44
45
  resource_attributes = {}
45
- resource_attributes['host.name'] = Datadog::Core::Environment::Socket.hostname if @settings.tracing.report_hostname
46
46
 
47
47
  @settings.tags&.each do |key, value|
48
48
  otel_key = case key
@@ -58,6 +58,15 @@ module Datadog
58
58
  resource_attributes['deployment.environment'] = @settings.env if @settings.env
59
59
  resource_attributes['service.version'] = @settings.version if @settings.version
60
60
 
61
+ hostname = Datadog::Core::Environment::Socket.resolved_hostname(@settings)
62
+ if hostname
63
+ if hostname == @settings.hostname
64
+ resource_attributes['host.name'] = hostname
65
+ elsif !resource_attributes.key?('host.name')
66
+ resource_attributes['host.name'] = hostname
67
+ end
68
+ end
69
+
61
70
  ::OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
62
71
  end
63
72
 
@@ -67,7 +76,7 @@ module Datadog
67
76
 
68
77
  configure_otlp_exporter(provider)
69
78
  rescue => e
70
- @logger.warn("Failed to configure OTLP metrics exporter: #{e.class}: #{e}")
79
+ @logger.warn("Failed to configure OTLP metrics exporter: #{e.class}: #{e.message}")
71
80
  end
72
81
 
73
82
  def default_metrics_endpoint
@@ -101,7 +110,7 @@ module Datadog
101
110
  )
102
111
  provider.add_metric_reader(reader)
103
112
  rescue LoadError => e
104
- @logger.warn("Could not load OTLP metrics exporter: #{e.class}: #{e}")
113
+ @logger.warn("Could not load OTLP metrics exporter: #{e.class}: #{e.message}")
105
114
  end
106
115
 
107
116
  # 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