datadog 2.22.0 → 2.24.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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -1
  3. data/ext/LIBDATADOG_DEVELOPMENT.md +1 -58
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +21 -5
  5. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  6. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  7. data/ext/datadog_profiling_native_extension/extconf.rb +9 -4
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  9. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
  10. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
  11. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  12. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  13. data/ext/libdatadog_api/feature_flags.c +554 -0
  14. data/ext/libdatadog_api/feature_flags.h +5 -0
  15. data/ext/libdatadog_api/init.c +2 -0
  16. data/ext/libdatadog_api/library_config.c +12 -11
  17. data/ext/libdatadog_extconf_helpers.rb +1 -1
  18. data/lib/datadog/appsec/api_security/route_extractor.rb +23 -6
  19. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  20. data/lib/datadog/appsec/assets/blocked.html +8 -0
  21. data/lib/datadog/appsec/assets/blocked.json +1 -1
  22. data/lib/datadog/appsec/assets/blocked.text +3 -1
  23. data/lib/datadog/appsec/assets.rb +1 -1
  24. data/lib/datadog/appsec/context.rb +2 -1
  25. data/lib/datadog/appsec/remote.rb +5 -9
  26. data/lib/datadog/appsec/response.rb +18 -4
  27. data/lib/datadog/appsec/security_engine/result.rb +2 -1
  28. data/lib/datadog/core/configuration/components.rb +30 -3
  29. data/lib/datadog/core/configuration/config_helper.rb +2 -2
  30. data/lib/datadog/core/configuration/deprecations.rb +2 -2
  31. data/lib/datadog/core/configuration/option_definition.rb +4 -2
  32. data/lib/datadog/core/configuration/options.rb +8 -5
  33. data/lib/datadog/core/configuration/settings.rb +28 -3
  34. data/lib/datadog/core/configuration/supported_configurations.rb +332 -302
  35. data/lib/datadog/core/ddsketch.rb +0 -2
  36. data/lib/datadog/core/environment/cgroup.rb +52 -25
  37. data/lib/datadog/core/environment/container.rb +140 -46
  38. data/lib/datadog/core/environment/ext.rb +7 -0
  39. data/lib/datadog/core/environment/process.rb +87 -0
  40. data/lib/datadog/core/feature_flags.rb +61 -0
  41. data/lib/datadog/core/rate_limiter.rb +9 -1
  42. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  43. data/lib/datadog/core/remote/client.rb +14 -6
  44. data/lib/datadog/core/remote/component.rb +6 -4
  45. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  46. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  47. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  48. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  49. data/lib/datadog/core/remote/transport/config.rb +4 -25
  50. data/lib/datadog/core/remote/transport/http/config.rb +10 -50
  51. data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
  52. data/lib/datadog/core/remote/transport/http.rb +15 -24
  53. data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
  54. data/lib/datadog/core/remote/worker.rb +25 -37
  55. data/lib/datadog/core/tag_builder.rb +0 -4
  56. data/lib/datadog/core/tag_normalizer.rb +84 -0
  57. data/lib/datadog/core/telemetry/component.rb +59 -16
  58. data/lib/datadog/core/telemetry/event/app_started.rb +86 -49
  59. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  60. data/lib/datadog/core/telemetry/logger.rb +2 -2
  61. data/lib/datadog/core/telemetry/logging.rb +2 -8
  62. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  63. data/lib/datadog/core/telemetry/request.rb +17 -3
  64. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +3 -34
  65. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  66. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -11
  67. data/lib/datadog/core/telemetry/worker.rb +88 -32
  68. data/lib/datadog/core/transport/ext.rb +2 -0
  69. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  70. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  71. data/lib/datadog/core/transport/http/builder.rb +9 -5
  72. data/lib/datadog/core/transport/http/client.rb +80 -0
  73. data/lib/datadog/core/transport/http.rb +22 -19
  74. data/lib/datadog/core/transport/response.rb +9 -0
  75. data/lib/datadog/core/transport/transport.rb +90 -0
  76. data/lib/datadog/core/utils/array.rb +29 -0
  77. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  78. data/lib/datadog/core/utils/network.rb +3 -1
  79. data/lib/datadog/core/utils/only_once_successful.rb +8 -2
  80. data/lib/datadog/core/utils/time.rb +1 -1
  81. data/lib/datadog/core/utils.rb +2 -0
  82. data/lib/datadog/core/workers/async.rb +10 -1
  83. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  84. data/lib/datadog/core/workers/polling.rb +2 -0
  85. data/lib/datadog/core/workers/queue.rb +100 -1
  86. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  87. data/lib/datadog/data_streams/configuration.rb +11 -0
  88. data/lib/datadog/data_streams/ext.rb +11 -0
  89. data/lib/datadog/data_streams/extensions.rb +16 -0
  90. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  91. data/lib/datadog/data_streams/processor.rb +509 -0
  92. data/lib/datadog/data_streams/transport/http/stats.rb +52 -0
  93. data/lib/datadog/data_streams/transport/http.rb +40 -0
  94. data/lib/datadog/data_streams/transport/stats.rb +46 -0
  95. data/lib/datadog/data_streams.rb +100 -0
  96. data/lib/datadog/di/component.rb +0 -16
  97. data/lib/datadog/di/contrib/active_record.rb +31 -5
  98. data/lib/datadog/di/el/compiler.rb +8 -4
  99. data/lib/datadog/di/el/evaluator.rb +1 -1
  100. data/lib/datadog/di/error.rb +9 -0
  101. data/lib/datadog/di/instrumenter.rb +93 -34
  102. data/lib/datadog/di/probe.rb +20 -0
  103. data/lib/datadog/di/probe_builder.rb +2 -1
  104. data/lib/datadog/di/probe_manager.rb +47 -33
  105. data/lib/datadog/di/probe_notification_builder.rb +77 -25
  106. data/lib/datadog/di/proc_responder.rb +32 -0
  107. data/lib/datadog/di/remote.rb +89 -84
  108. data/lib/datadog/di/transport/diagnostics.rb +8 -36
  109. data/lib/datadog/di/transport/http/diagnostics.rb +1 -33
  110. data/lib/datadog/di/transport/http/input.rb +1 -33
  111. data/lib/datadog/di/transport/http.rb +32 -17
  112. data/lib/datadog/di/transport/input.rb +67 -34
  113. data/lib/datadog/di.rb +61 -5
  114. data/lib/datadog/open_feature/component.rb +60 -0
  115. data/lib/datadog/open_feature/configuration.rb +27 -0
  116. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  117. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  118. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  119. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  120. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  121. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  122. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  123. data/lib/datadog/open_feature/ext.rb +14 -0
  124. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  125. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  126. data/lib/datadog/open_feature/provider.rb +141 -0
  127. data/lib/datadog/open_feature/remote.rb +67 -0
  128. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  129. data/lib/datadog/open_feature/transport.rb +70 -0
  130. data/lib/datadog/open_feature.rb +19 -0
  131. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  132. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  133. data/lib/datadog/opentelemetry/metrics.rb +117 -0
  134. data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
  135. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +35 -0
  136. data/lib/datadog/opentelemetry.rb +3 -0
  137. data/lib/datadog/profiling/collectors/code_provenance.rb +41 -7
  138. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
  139. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  140. data/lib/datadog/profiling/collectors/info.rb +2 -1
  141. data/lib/datadog/profiling/component.rb +12 -11
  142. data/lib/datadog/profiling/http_transport.rb +4 -1
  143. data/lib/datadog/profiling/profiler.rb +4 -0
  144. data/lib/datadog/profiling/tag_builder.rb +36 -3
  145. data/lib/datadog/profiling.rb +1 -2
  146. data/lib/datadog/single_step_instrument.rb +1 -1
  147. data/lib/datadog/tracing/configuration/ext.rb +9 -0
  148. data/lib/datadog/tracing/configuration/settings.rb +74 -0
  149. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  150. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  151. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  152. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  153. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  154. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  155. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  156. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  157. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  158. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  159. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +22 -17
  160. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  161. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  162. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  163. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  164. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  165. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  166. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  167. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  168. data/lib/datadog/tracing/contrib/karafka/patcher.rb +35 -4
  169. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  170. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  171. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  172. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  173. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  174. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  175. data/lib/datadog/tracing/contrib/status_range_matcher.rb +9 -1
  176. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  177. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  178. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  179. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  180. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  181. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  182. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  183. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  184. data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
  185. data/lib/datadog/tracing/contrib.rb +1 -0
  186. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  187. data/lib/datadog/tracing/metadata/ext.rb +1 -1
  188. data/lib/datadog/tracing/remote.rb +1 -9
  189. data/lib/datadog/tracing/span_event.rb +2 -2
  190. data/lib/datadog/tracing/span_operation.rb +9 -4
  191. data/lib/datadog/tracing/trace_operation.rb +44 -6
  192. data/lib/datadog/tracing/tracer.rb +42 -16
  193. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  194. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  195. data/lib/datadog/tracing/transport/http.rb +15 -9
  196. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  197. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  198. data/lib/datadog/tracing/transport/traces.rb +9 -71
  199. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  200. data/lib/datadog/tracing/writer.rb +1 -0
  201. data/lib/datadog/version.rb +2 -2
  202. data/lib/datadog.rb +2 -0
  203. metadata +78 -21
  204. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  205. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  206. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  207. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  208. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  209. data/lib/datadog/di/transport/http/api.rb +0 -42
  210. data/lib/datadog/di/transport/http/client.rb +0 -47
  211. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  212. data/lib/datadog/tracing/transport/http/api.rb +0 -44
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../core/configuration/ext'
4
+
5
+ module Datadog
6
+ module OpenTelemetry
7
+ module Configuration
8
+ module Settings
9
+ def self.extended(base)
10
+ base = base.singleton_class unless base.is_a?(Class)
11
+ add_settings!(base)
12
+ end
13
+
14
+ def self.normalize_temporality_preference(env_var_name)
15
+ proc do |value|
16
+ if value && value.to_s.downcase != 'delta' && value.to_s.downcase != 'cumulative'
17
+ Datadog.logger.warn("#{env_var_name}=#{value} is not supported. Using delta instead.")
18
+ 'delta'
19
+ else
20
+ value
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.normalize_protocol(env_var_name)
26
+ proc do |value|
27
+ if value && value.to_s.downcase != 'http/protobuf'
28
+ Datadog.logger.warn("#{env_var_name}=#{value} is not supported. Using http/protobuf instead.")
29
+ end
30
+ 'http/protobuf'
31
+ end
32
+ end
33
+
34
+ def self.headers_parser(env_var_name)
35
+ lambda do |value|
36
+ return {} if value.nil? || value.empty?
37
+
38
+ headers = {}
39
+ header_items = value.split(',')
40
+ header_items.each do |key_value|
41
+ key, header_value = key_value.split('=', 2)
42
+ # If header is malformed, return an empty hash
43
+ if key.nil? || header_value.nil?
44
+ Datadog.logger.warn("#{env_var_name} has malformed header: #{key_value.inspect}")
45
+ return {}
46
+ end
47
+
48
+ key.strip!
49
+ header_value.strip!
50
+ if key.empty? || header_value.empty?
51
+ Datadog.logger.warn("#{env_var_name} has empty key or value in: #{key_value.inspect}")
52
+ return {}
53
+ end
54
+
55
+ headers[key] = header_value
56
+ end
57
+ headers
58
+ end
59
+ end
60
+
61
+ def self.add_settings!(base)
62
+ base.class_eval do
63
+ settings :opentelemetry do
64
+ settings :exporter do
65
+ option :protocol do |o|
66
+ o.type :string
67
+ o.setter(&Settings.normalize_protocol('OTEL_EXPORTER_OTLP_PROTOCOL'))
68
+ o.env 'OTEL_EXPORTER_OTLP_PROTOCOL'
69
+ o.default 'http/protobuf'
70
+ end
71
+
72
+ option :timeout_millis do |o|
73
+ o.type :int
74
+ o.env 'OTEL_EXPORTER_OTLP_TIMEOUT'
75
+ o.default 10_000
76
+ end
77
+
78
+ option :headers do |o|
79
+ o.type :hash
80
+ o.env 'OTEL_EXPORTER_OTLP_HEADERS'
81
+ o.default { {} }
82
+ o.env_parser(&Settings.headers_parser('OTEL_EXPORTER_OTLP_HEADERS'))
83
+ end
84
+
85
+ option :endpoint do |o|
86
+ o.type :string, nilable: true
87
+ o.env 'OTEL_EXPORTER_OTLP_ENDPOINT'
88
+ o.default nil
89
+ end
90
+ end
91
+
92
+ settings :metrics do
93
+ # Metrics-specific options default to nil to detect unset state.
94
+ # If a metrics-specific env var (e.g., OTEL_EXPORTER_OTLP_METRICS_TIMEOUT) is not set,
95
+ # we fall back to the general OTLP env var (e.g., OTEL_EXPORTER_OTLP_TIMEOUT) per OpenTelemetry spec.
96
+ option :enabled do |o|
97
+ o.type :bool
98
+ o.env 'DD_METRICS_OTEL_ENABLED'
99
+ o.default false
100
+ end
101
+
102
+ option :exporter do |o|
103
+ o.type :string
104
+ o.env 'OTEL_METRICS_EXPORTER'
105
+ o.default 'otlp'
106
+ end
107
+
108
+ option :export_interval_millis do |o|
109
+ o.type :int
110
+ o.env 'OTEL_METRIC_EXPORT_INTERVAL'
111
+ o.default 10_000
112
+ end
113
+
114
+ option :export_timeout_millis do |o|
115
+ o.type :int
116
+ o.env 'OTEL_METRIC_EXPORT_TIMEOUT'
117
+ o.default 7_500
118
+ end
119
+
120
+ option :temporality_preference do |o|
121
+ o.type :string
122
+ o.env 'OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE'
123
+ o.default 'delta'
124
+ o.setter(&Settings.normalize_temporality_preference('OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE'))
125
+ end
126
+
127
+ option :endpoint do |o|
128
+ o.type :string, nilable: true
129
+ o.env 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT'
130
+ o.default nil
131
+ end
132
+
133
+ option :headers do |o|
134
+ o.type :hash, nilable: true
135
+ o.env 'OTEL_EXPORTER_OTLP_METRICS_HEADERS'
136
+ o.default nil
137
+ o.env_parser(&Settings.headers_parser('OTEL_EXPORTER_OTLP_METRICS_HEADERS'))
138
+ end
139
+
140
+ option :timeout_millis do |o|
141
+ o.type :int, nilable: true
142
+ o.env 'OTEL_EXPORTER_OTLP_METRICS_TIMEOUT'
143
+ o.default 10000
144
+ end
145
+
146
+ option :protocol do |o|
147
+ o.type :string, nilable: true
148
+ o.env 'OTEL_EXPORTER_OTLP_METRICS_PROTOCOL'
149
+ o.default "http/protobuf"
150
+ o.setter(&Settings.normalize_protocol('OTEL_EXPORTER_OTLP_METRICS_PROTOCOL'))
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/configuration/ext'
4
+
5
+ module Datadog
6
+ module OpenTelemetry
7
+ class Metrics
8
+ EXPORTER_NONE = 'none'
9
+
10
+ def self.initialize!(components)
11
+ new(components).configure_metrics_sdk
12
+ true
13
+ rescue => exc
14
+ components.logger.error("Failed to initialize OpenTelemetry metrics: #{exc.class}: #{exc}: #{exc.backtrace.join("\n")}")
15
+ false
16
+ end
17
+
18
+ def initialize(components)
19
+ @logger = components.logger
20
+ @settings = components.settings
21
+ @agent_host = components.agent_settings.hostname
22
+ @agent_ssl = components.agent_settings.ssl
23
+ end
24
+
25
+ def configure_metrics_sdk
26
+ provider = ::OpenTelemetry.meter_provider
27
+ provider.shutdown if provider.is_a?(::OpenTelemetry::SDK::Metrics::MeterProvider)
28
+
29
+ # The OpenTelemetry SDK defaults to cumulative temporality, but Datadog prefers delta temporality.
30
+ # Here is an example of how this config is applied: https://github.com/open-telemetry/opentelemetry-ruby/blob/1933d4c18e5f5e45c53fa9e902e58aa91e85cc38/metrics_sdk/lib/opentelemetry/sdk/metrics/aggregation/sum.rb#L14
31
+ if DATADOG_ENV['OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE'].nil?
32
+ ENV['OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE'] = 'delta' # rubocop:disable CustomCops/EnvUsageCop
33
+ end
34
+
35
+ resource = create_resource
36
+ provider = ::OpenTelemetry::SDK::Metrics::MeterProvider.new(resource: resource)
37
+ configure_metric_reader(provider)
38
+ ::OpenTelemetry.meter_provider = provider
39
+ end
40
+
41
+ private
42
+
43
+ def create_resource
44
+ resource_attributes = {}
45
+ resource_attributes['host.name'] = Datadog::Core::Environment::Socket.hostname if @settings.tracing.report_hostname
46
+
47
+ @settings.tags&.each do |key, value|
48
+ otel_key = case key
49
+ when 'service' then 'service.name'
50
+ when 'env' then 'deployment.environment'
51
+ when 'version' then 'service.version'
52
+ else key
53
+ end
54
+ resource_attributes[otel_key] = value
55
+ end
56
+
57
+ resource_attributes['service.name'] = @settings.service_without_fallback || resource_attributes['service.name'] || Datadog::Core::Environment::Ext::FALLBACK_SERVICE_NAME
58
+ resource_attributes['deployment.environment'] = @settings.env if @settings.env
59
+ resource_attributes['service.version'] = @settings.version if @settings.version
60
+
61
+ ::OpenTelemetry::SDK::Resources::Resource.create(resource_attributes)
62
+ end
63
+
64
+ def configure_metric_reader(provider)
65
+ exporter_name = @settings.opentelemetry.metrics.exporter
66
+ return if exporter_name == EXPORTER_NONE
67
+
68
+ configure_otlp_exporter(provider)
69
+ rescue => e
70
+ @logger.warn("Failed to configure OTLP metrics exporter: #{e.class}: #{e}")
71
+ end
72
+
73
+ def default_metrics_endpoint
74
+ "#{@agent_ssl ? "https" : "http"}://#{@agent_host}:4318/v1/metrics"
75
+ end
76
+
77
+ def configure_otlp_exporter(provider)
78
+ require 'opentelemetry/exporter/otlp_metrics'
79
+ require_relative 'sdk/metrics_exporter'
80
+
81
+ metrics_config = @settings.opentelemetry.metrics
82
+ endpoint = get_metrics_config_with_fallback(
83
+ option_name: :endpoint,
84
+ computed_default: default_metrics_endpoint
85
+ )
86
+ timeout = get_metrics_config_with_fallback(option_name: :timeout_millis)
87
+ headers = get_metrics_config_with_fallback(option_name: :headers)
88
+ # OpenTelemetry SDK only supports http/protobuf protocol.
89
+ # TODO: Add support for http/json and grpc.
90
+ # protocol = get_metrics_config_with_fallback(option_name: :protocol)
91
+ exporter = Datadog::OpenTelemetry::SDK::MetricsExporter.new(
92
+ endpoint: endpoint,
93
+ timeout: timeout / 1000.0,
94
+ headers: headers
95
+ )
96
+
97
+ reader = ::OpenTelemetry::SDK::Metrics::Export::PeriodicMetricReader.new(
98
+ exporter: exporter,
99
+ export_interval_millis: metrics_config.export_interval_millis,
100
+ export_timeout_millis: metrics_config.export_timeout_millis
101
+ )
102
+ provider.add_metric_reader(reader)
103
+ rescue LoadError => e
104
+ @logger.warn("Could not load OTLP metrics exporter: #{e.class}: #{e}")
105
+ end
106
+
107
+ # Returns metrics config value if explicitly set, otherwise falls back to exporter config or computed default value.
108
+ def get_metrics_config_with_fallback(option_name:, computed_default: nil)
109
+ if @settings.opentelemetry.metrics.using_default?(option_name)
110
+ @settings.opentelemetry.exporter.public_send(option_name) || computed_default
111
+ else
112
+ @settings.opentelemetry.metrics.public_send(option_name)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -30,7 +30,31 @@ module Datadog
30
30
  [SpanProcessor.new]
31
31
  end
32
32
 
33
- ::OpenTelemetry::SDK::Configurator.prepend(self)
33
+ def metrics_configuration_hook
34
+ components = Datadog.send(:components)
35
+ return super unless components.settings.opentelemetry.metrics.enabled
36
+
37
+ begin
38
+ require 'opentelemetry-metrics-sdk'
39
+ rescue LoadError => exc
40
+ components.logger.warn("Failed to load OpenTelemetry metrics gems: #{exc.class}: #{exc}")
41
+ return super
42
+ end
43
+
44
+ success = Datadog::OpenTelemetry::Metrics.initialize!(components)
45
+ super unless success
46
+ end
47
+
48
+ # Prepend to ConfiguratorPatch (not Configurator) so our hook runs first.
49
+ begin
50
+ require 'opentelemetry-metrics-sdk' if defined?(OpenTelemetry::SDK) && !defined?(OpenTelemetry::SDK::Metrics::ConfiguratorPatch)
51
+ rescue LoadError
52
+ end
53
+
54
+ if defined?(::OpenTelemetry::SDK::Metrics::ConfiguratorPatch)
55
+ ::OpenTelemetry::SDK::Metrics::ConfiguratorPatch.prepend(self) unless ::OpenTelemetry::SDK::Metrics::ConfiguratorPatch.ancestors.include?(self)
56
+ end
57
+ ::OpenTelemetry::SDK::Configurator.prepend(self) unless ::OpenTelemetry::SDK::Configurator.ancestors.include?(self)
34
58
  end
35
59
  end
36
60
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'opentelemetry/exporter/otlp_metrics'
4
+
5
+ module Datadog
6
+ module OpenTelemetry
7
+ module SDK
8
+ class MetricsExporter < ::OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter
9
+ METRIC_EXPORT_ATTEMPTS = 'otel.metrics_export_attempts'
10
+ METRIC_EXPORT_SUCCESSES = 'otel.metrics_export_successes'
11
+ METRIC_EXPORT_FAILURES = 'otel.metrics_export_failures'
12
+ TELEMETRY_NAMESPACE = 'tracers'
13
+ TELEMETRY_TAGS = {'protocol' => "http", 'encoding' => 'protobuf'}
14
+
15
+ def export(metrics, timeout: nil)
16
+ telemetry&.inc(TELEMETRY_NAMESPACE, METRIC_EXPORT_ATTEMPTS, 1, tags: TELEMETRY_TAGS)
17
+ result = super
18
+ metric_name = (result == 0) ? METRIC_EXPORT_SUCCESSES : METRIC_EXPORT_FAILURES
19
+ telemetry&.inc(TELEMETRY_NAMESPACE, metric_name, 1, tags: TELEMETRY_TAGS)
20
+ result
21
+ rescue => e
22
+ Datadog.logger.error("Failed to export OpenTelemetry Metrics: #{e.class}: #{e}")
23
+ telemetry&.inc(TELEMETRY_NAMESPACE, METRIC_EXPORT_FAILURES, 1, tags: TELEMETRY_TAGS)
24
+ raise
25
+ end
26
+
27
+ private
28
+
29
+ def telemetry
30
+ Datadog.send(:components).telemetry
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -22,6 +22,8 @@ require_relative 'opentelemetry/api/baggage'
22
22
  require_relative 'opentelemetry/sdk/configurator' if defined?(OpenTelemetry::SDK)
23
23
  require_relative 'opentelemetry/sdk/trace/span' if defined?(OpenTelemetry::SDK)
24
24
 
25
+ require_relative 'opentelemetry/metrics' if defined?(OpenTelemetry::SDK::Metrics)
26
+
25
27
  module Datadog
26
28
  # Datadog OpenTelemetry integration.
27
29
  module OpenTelemetry
@@ -47,6 +49,7 @@ end
47
49
  # Currently, this closely translates to Datadog's partial flushing.
48
50
  #
49
51
  # @see OpenTelemetry::SDK::Trace::SpanProcessor#on_finish
52
+
50
53
  Datadog.configure do |c|
51
54
  c.tracing.partial_flush.enabled = true
52
55
  end
@@ -22,6 +22,7 @@ module Datadog
22
22
  @libraries_by_path = {}
23
23
  @seen_files = Set.new
24
24
  @seen_libraries = Set.new
25
+ @executable_paths = [Gem.bindir, bundler_bin_path].uniq.compact.freeze
25
26
 
26
27
  record_library(
27
28
  Library.new(
@@ -29,7 +30,7 @@ module Datadog
29
30
  name: "stdlib",
30
31
  version: RUBY_VERSION,
31
32
  path: standard_library_path,
32
- extra_path: ruby_native_filename,
33
+ extra_paths: [ruby_native_filename],
33
34
  )
34
35
  )
35
36
  end
@@ -51,7 +52,8 @@ module Datadog
51
52
  :libraries_by_name,
52
53
  :libraries_by_path,
53
54
  :seen_files,
54
- :seen_libraries
55
+ :seen_libraries,
56
+ :executable_paths
55
57
 
56
58
  def record_library(library)
57
59
  libraries_by_name[library.name] = library
@@ -79,13 +81,20 @@ module Datadog
79
81
  loaded_specs.each do |spec|
80
82
  next if libraries_by_name.key?(spec.name)
81
83
 
84
+ extra_paths = [(spec.extension_dir if spec.extensions.any?)]
85
+ spec.executables&.each do |executable|
86
+ executable_paths.each do |path|
87
+ extra_paths << File.join(path, executable)
88
+ end
89
+ end
90
+
82
91
  record_library(
83
92
  Library.new(
84
93
  kind: "library",
85
94
  name: spec.name,
86
95
  version: spec.version,
87
96
  path: spec.gem_dir,
88
- extra_path: (spec.extension_dir if spec.extensions.any?),
97
+ extra_paths: extra_paths,
89
98
  )
90
99
  )
91
100
  recorded_library = true
@@ -107,6 +116,30 @@ module Datadog
107
116
  end
108
117
  end
109
118
 
119
+ # This is intended to mirror bundler's `Bundler.bin_path` with one key difference: the bundler version of the
120
+ # method **tries to create the path** if it doesn't exist, whereas our version doesn't.
121
+ #
122
+ # This "try to create the path" is annoying because creating the path can fail if e.g. the app doesn't have
123
+ # permissions to do that, see https://github.com/DataDog/dd-trace-rb/issues/5137.
124
+ # Thus we have our own version that a) Avoids creating the path, and b) Rescues exceptions to avoid any
125
+ # bundler complaints here impacting the application. (Bundler tends to go "something is wrong, raise!" which
126
+ # I think makes a lot of sense given how bundler is intended to be used, but for this our kind of "ask a few
127
+ # questions usage" it's not what we want.)
128
+ def bundler_bin_path
129
+ return unless defined?(Bundler)
130
+
131
+ path = Bundler.settings[:bin] || "bin"
132
+ root = Bundler.root
133
+ result = Pathname.new(path).expand_path(root).expand_path
134
+ result.to_s
135
+ rescue Exception => e # rubocop:disable Lint/RescueException
136
+ Datadog.logger.debug(
137
+ "CodeProvenance#bundler_bin_path failed. " \
138
+ "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
139
+ )
140
+ nil
141
+ end
142
+
110
143
  # Represents metadata we have for a ruby gem
111
144
  #
112
145
  # Important note: This class gets encoded to JSON with the built-in JSON gem. But, we've found that in some
@@ -118,17 +151,18 @@ module Datadog
118
151
  class Library
119
152
  attr_reader :kind, :name, :version
120
153
 
121
- def initialize(kind:, name:, version:, path:, extra_path: nil)
122
- extra_path = nil if extra_path&.empty?
154
+ def initialize(kind:, name:, version:, path:, extra_paths:)
155
+ extra_paths = Array(extra_paths).compact.reject(&:empty?).map { |p| p.dup.freeze }
123
156
  @kind = kind.freeze
124
157
  @name = name.dup.freeze
125
158
  @version = version.to_s.dup.freeze
126
- @paths = [path.dup.freeze, extra_path.dup.freeze].compact.freeze
159
+ @paths = [path.dup.freeze, *extra_paths].freeze
127
160
  freeze
128
161
  end
129
162
 
130
163
  def to_json(arg = nil)
131
- {kind: @kind, name: @name, version: @version, paths: @paths}.to_json(arg)
164
+ # Steep: https://github.com/ruby/rbs/pull/2691
165
+ {kind: @kind, name: @name, version: @version, paths: @paths}.to_json(arg) # steep:ignore ArgumentTypeMismatch
132
166
  end
133
167
 
134
168
  def path
@@ -82,7 +82,7 @@ module Datadog
82
82
  "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
83
83
  )
84
84
  on_failure_proc&.call
85
- Datadog::Core::Telemetry::Logger.report(e, description: "CpuAndWallTimeWorker thread error", pii_safe: true)
85
+ Datadog::Core::Telemetry::Logger.report(e, description: "CpuAndWallTimeWorker thread error")
86
86
  end
87
87
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
88
88
  @worker_thread.thread_variable_set(:fork_safe, true)
@@ -41,7 +41,7 @@ module Datadog
41
41
  "IdleSamplingHelper thread error. " \
42
42
  "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
43
43
  )
44
- Datadog::Core::Telemetry::Logger.report(e, description: "IdleSamplingHelper thread error", pii_safe: true)
44
+ Datadog::Core::Telemetry::Logger.report(e, description: "IdleSamplingHelper thread error")
45
45
  end
46
46
  @worker_thread.name = self.class.name # Repeated from above to make sure thread gets named asap
47
47
  @worker_thread.thread_variable_set(:fork_safe, true)
@@ -16,7 +16,8 @@ module Datadog
16
16
  class Info
17
17
  def initialize(settings)
18
18
  @profiler_info = nil
19
- @info = {
19
+ # Steep: https://github.com/soutaro/steep/issues/363
20
+ @info = { # steep:ignore IncompatibleAssignment
20
21
  platform: collect_platform_info,
21
22
  runtime: collect_runtime_info,
22
23
  application: collect_application_info(settings),
@@ -4,9 +4,6 @@ module Datadog
4
4
  module Profiling
5
5
  # Responsible for wiring up the Profiler for execution
6
6
  module Component
7
- ALLOCATION_WITH_RACTORS_ONLY_ONCE = Datadog::Core::Utils::OnlyOnce.new
8
- private_constant :ALLOCATION_WITH_RACTORS_ONLY_ONCE
9
-
10
7
  # Passing in a `nil` tracer is supported and will disable the following profiling features:
11
8
  # * Profiling in the trace viewer, as well as scoping a profile down to a span
12
9
  # * Endpoint aggregation in the profiler UX, including normalization (resource per endpoint call)
@@ -145,7 +142,7 @@ module Datadog
145
142
  logger.debug(
146
143
  "Using Ractors may result in GC profiling unexpectedly " \
147
144
  "stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
148
- "application stability or performance. This does not happen if Ractors are not used."
145
+ "application stability or performance. This issue is fixed on Ruby 4."
149
146
  )
150
147
  end
151
148
 
@@ -195,13 +192,11 @@ module Datadog
195
192
  # On all known versions of Ruby 3.x, due to https://bugs.ruby-lang.org/issues/19112, when a ractor gets
196
193
  # garbage collected, Ruby will disable all active tracepoints, which this feature internally relies on.
197
194
  elsif RUBY_VERSION.start_with?("3.")
198
- ALLOCATION_WITH_RACTORS_ONLY_ONCE.run do
199
- logger.info(
200
- "Using Ractors may result in allocation profiling " \
201
- "stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
202
- "application stability or performance. This does not happen if Ractors are not used."
203
- )
204
- end
195
+ logger.debug(
196
+ "Using Ractors may result in allocation profiling " \
197
+ "stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
198
+ "application stability or performance. This issue is fixed on Ruby 4."
199
+ )
205
200
  end
206
201
 
207
202
  logger.debug("Enabled allocation profiling")
@@ -220,6 +215,12 @@ module Datadog
220
215
  "Please upgrade to Ruby >= 3.1 in order to use this feature. Heap profiling has been disabled."
221
216
  )
222
217
  return false
218
+ elsif RUBY_VERSION.start_with?("4.")
219
+ logger.warn(
220
+ "Datadog Ruby heap profiler is currently incompatible with Ruby 4. " \
221
+ "Heap profiling has been disabled."
222
+ )
223
+ return false
223
224
  end
224
225
 
225
226
  unless allocation_profiling_enabled
@@ -13,7 +13,10 @@ module Datadog
13
13
  def initialize(agent_settings:, site:, api_key:, upload_timeout_seconds:)
14
14
  @upload_timeout_milliseconds = (upload_timeout_seconds * 1_000).to_i
15
15
 
16
- @exporter_configuration =
16
+ # Steep: multiple issues here
17
+ # first https://github.com/soutaro/steep/issues/363
18
+ # then https://github.com/soutaro/steep/issues/1603 (remove the .freeze to see it)
19
+ @exporter_configuration = # steep:ignore IncompatibleAssignment
17
20
  if agentless?(site, api_key)
18
21
  [:agentless, site, api_key].freeze
19
22
  else
@@ -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
@@ -1,17 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../core/tag_builder"
4
- require_relative "../core/utils"
3
+ require_relative '../core/tag_builder'
4
+ require_relative '../core/utils'
5
+
6
+ require 'set'
5
7
 
6
8
  module Datadog
7
9
  module Profiling
8
10
  # Builds a hash of default plus user tags to be included in a profile
11
+ #
12
+ # @api private
9
13
  module TagBuilder
10
14
  include Datadog::Profiling::Ext::Transport::HTTP # Tag name constants
11
15
 
16
+ # When changing or adding profiling-related tags, make sure they are
17
+ # kept in sync with
18
+ # https://docs.google.com/spreadsheets/d/1LOGMf4c4Avbtn36uZ2SWvhIGKRPLM1BoWkUP4JYj7hA/
19
+ # (Datadog internal link).
20
+ #
21
+ # For consistency between the different profilers, every tag should be
22
+ # vetted before it gets reported with a profile, as otherwise it's too
23
+ # easy to end up with different tags in different languages.
24
+ ALLOWED_TAGS = Set.new(
25
+ [
26
+ 'env',
27
+ 'service',
28
+ 'version',
29
+ 'git.commit.sha',
30
+ 'git.repository_url',
31
+ 'host',
32
+ 'language',
33
+ 'runtime',
34
+ 'runtime_engine',
35
+ 'runtime_platform',
36
+ 'runtime_version',
37
+ 'runtime-id',
38
+ 'process_id',
39
+ 'profiler_version',
40
+ 'profile_seq',
41
+ ]
42
+ ).freeze
43
+
12
44
  def self.call(
13
45
  settings:,
14
- # Other metadata
15
46
  profile_seq:,
16
47
  profiler_version: Core::Environment::Identity.gem_datadog_version
17
48
  )
@@ -19,6 +50,8 @@ module Datadog
19
50
  FORM_FIELD_TAG_PROFILER_VERSION => profiler_version,
20
51
  'profile_seq' => profile_seq.to_s,
21
52
  )
53
+ user_tag_keys = settings.tags.keys
54
+ hash.keep_if { |tag| user_tag_keys.include?(tag) || ALLOWED_TAGS.include?(tag) }
22
55
  Core::Utils.encode_tags(hash)
23
56
  end
24
57
  end
@@ -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