datadog 2.22.0 → 2.23.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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -2
  3. data/ext/LIBDATADOG_DEVELOPMENT.md +1 -58
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +4 -0
  5. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  6. data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
  7. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  8. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  9. data/ext/libdatadog_api/feature_flags.c +554 -0
  10. data/ext/libdatadog_api/feature_flags.h +5 -0
  11. data/ext/libdatadog_api/init.c +2 -0
  12. data/ext/libdatadog_api/library_config.c +12 -11
  13. data/ext/libdatadog_extconf_helpers.rb +1 -1
  14. data/lib/datadog/appsec/api_security/route_extractor.rb +23 -6
  15. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  16. data/lib/datadog/appsec/assets/blocked.html +8 -0
  17. data/lib/datadog/appsec/assets/blocked.json +1 -1
  18. data/lib/datadog/appsec/assets/blocked.text +3 -1
  19. data/lib/datadog/appsec/assets.rb +1 -1
  20. data/lib/datadog/appsec/remote.rb +4 -0
  21. data/lib/datadog/appsec/response.rb +18 -4
  22. data/lib/datadog/core/configuration/components.rb +30 -3
  23. data/lib/datadog/core/configuration/config_helper.rb +1 -1
  24. data/lib/datadog/core/configuration/settings.rb +14 -0
  25. data/lib/datadog/core/configuration/supported_configurations.rb +330 -301
  26. data/lib/datadog/core/ddsketch.rb +0 -2
  27. data/lib/datadog/core/environment/ext.rb +6 -0
  28. data/lib/datadog/core/environment/process.rb +79 -0
  29. data/lib/datadog/core/feature_flags.rb +61 -0
  30. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  31. data/lib/datadog/core/remote/transport/config.rb +2 -10
  32. data/lib/datadog/core/remote/transport/http/config.rb +9 -9
  33. data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
  34. data/lib/datadog/core/remote/transport/http.rb +2 -0
  35. data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
  36. data/lib/datadog/core/remote/worker.rb +25 -37
  37. data/lib/datadog/core/tag_builder.rb +0 -4
  38. data/lib/datadog/core/tag_normalizer.rb +84 -0
  39. data/lib/datadog/core/telemetry/component.rb +7 -3
  40. data/lib/datadog/core/telemetry/event/app_started.rb +52 -49
  41. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
  42. data/lib/datadog/core/telemetry/logger.rb +2 -2
  43. data/lib/datadog/core/telemetry/logging.rb +2 -8
  44. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
  45. data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
  46. data/lib/datadog/core/transport/http/client.rb +69 -0
  47. data/lib/datadog/core/utils/array.rb +29 -0
  48. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  49. data/lib/datadog/core/utils/network.rb +3 -1
  50. data/lib/datadog/core/utils/only_once_successful.rb +6 -2
  51. data/lib/datadog/core/utils.rb +2 -0
  52. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  53. data/lib/datadog/data_streams/configuration.rb +11 -0
  54. data/lib/datadog/data_streams/ext.rb +11 -0
  55. data/lib/datadog/data_streams/extensions.rb +16 -0
  56. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  57. data/lib/datadog/data_streams/processor.rb +509 -0
  58. data/lib/datadog/data_streams/transport/http/api.rb +33 -0
  59. data/lib/datadog/data_streams/transport/http/client.rb +21 -0
  60. data/lib/datadog/data_streams/transport/http/stats.rb +87 -0
  61. data/lib/datadog/data_streams/transport/http.rb +41 -0
  62. data/lib/datadog/data_streams/transport/stats.rb +60 -0
  63. data/lib/datadog/data_streams.rb +100 -0
  64. data/lib/datadog/di/component.rb +0 -16
  65. data/lib/datadog/di/el/evaluator.rb +1 -1
  66. data/lib/datadog/di/error.rb +4 -0
  67. data/lib/datadog/di/instrumenter.rb +76 -30
  68. data/lib/datadog/di/probe.rb +20 -0
  69. data/lib/datadog/di/probe_manager.rb +10 -2
  70. data/lib/datadog/di/probe_notification_builder.rb +62 -23
  71. data/lib/datadog/di/proc_responder.rb +32 -0
  72. data/lib/datadog/di/transport/diagnostics.rb +2 -2
  73. data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
  74. data/lib/datadog/di/transport/http/input.rb +2 -4
  75. data/lib/datadog/di/transport/http.rb +6 -2
  76. data/lib/datadog/di/transport/input.rb +64 -4
  77. data/lib/datadog/open_feature/component.rb +60 -0
  78. data/lib/datadog/open_feature/configuration.rb +27 -0
  79. data/lib/datadog/open_feature/evaluation_engine.rb +69 -0
  80. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  81. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  82. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  83. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  84. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  85. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  86. data/lib/datadog/open_feature/ext.rb +14 -0
  87. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  88. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  89. data/lib/datadog/open_feature/provider.rb +141 -0
  90. data/lib/datadog/open_feature/remote.rb +74 -0
  91. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  92. data/lib/datadog/open_feature/transport.rb +72 -0
  93. data/lib/datadog/open_feature.rb +19 -0
  94. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  95. data/lib/datadog/opentelemetry/metrics.rb +110 -0
  96. data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
  97. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +38 -0
  98. data/lib/datadog/opentelemetry.rb +3 -0
  99. data/lib/datadog/profiling/collectors/code_provenance.rb +15 -6
  100. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
  101. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  102. data/lib/datadog/profiling/profiler.rb +4 -0
  103. data/lib/datadog/profiling/tag_builder.rb +36 -3
  104. data/lib/datadog/profiling.rb +1 -2
  105. data/lib/datadog/single_step_instrument.rb +1 -1
  106. data/lib/datadog/tracing/configuration/ext.rb +9 -0
  107. data/lib/datadog/tracing/configuration/settings.rb +74 -0
  108. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  109. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  110. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  111. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  112. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  113. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  114. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  115. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  116. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  117. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +22 -17
  118. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  119. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  120. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  121. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  122. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  123. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  124. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  125. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  126. data/lib/datadog/tracing/contrib/karafka/patcher.rb +32 -0
  127. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  128. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  129. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  130. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  131. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  132. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  133. data/lib/datadog/tracing/contrib/status_range_matcher.rb +7 -0
  134. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  135. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  136. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  137. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  138. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  139. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
  140. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  141. data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
  142. data/lib/datadog/tracing/contrib.rb +1 -0
  143. data/lib/datadog/tracing/metadata/ext.rb +1 -1
  144. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  145. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  146. data/lib/datadog/tracing/transport/traces.rb +3 -5
  147. data/lib/datadog/version.rb +2 -2
  148. data/lib/datadog.rb +2 -0
  149. metadata +78 -15
  150. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  151. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  152. data/lib/datadog/di/transport/http/client.rb +0 -47
@@ -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 nil
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 nil
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,110 @@
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 resolve_metrics_endpoint
74
+ metrics_config = @settings.opentelemetry.metrics
75
+ exporter_config = @settings.opentelemetry.exporter
76
+
77
+ return metrics_config.endpoint if metrics_config.endpoint
78
+ return exporter_config.endpoint if exporter_config.endpoint
79
+ "#{@agent_ssl ? "https" : "http"}://#{@agent_host}:4318/v1/metrics"
80
+ end
81
+
82
+ def configure_otlp_exporter(provider)
83
+ require 'opentelemetry/exporter/otlp_metrics'
84
+ require_relative 'sdk/metrics_exporter'
85
+
86
+ metrics_config = @settings.opentelemetry.metrics
87
+ exporter_config = @settings.opentelemetry.exporter
88
+ timeout = metrics_config.timeout_millis || exporter_config.timeout_millis
89
+ headers = metrics_config.headers || exporter_config.headers || {}
90
+
91
+ protocol = metrics_config.protocol || exporter_config.protocol
92
+ exporter = Datadog::OpenTelemetry::SDK::MetricsExporter.new(
93
+ endpoint: resolve_metrics_endpoint,
94
+ timeout: timeout / 1000.0,
95
+ headers: headers,
96
+ protocol: protocol
97
+ )
98
+
99
+ reader = ::OpenTelemetry::SDK::Metrics::Export::PeriodicMetricReader.new(
100
+ exporter: exporter,
101
+ export_interval_millis: metrics_config.export_interval_millis,
102
+ export_timeout_millis: metrics_config.export_timeout_millis
103
+ )
104
+ provider.add_metric_reader(reader)
105
+ rescue LoadError => e
106
+ @logger.warn("Could not load OTLP metrics exporter: #{e.class}: #{e}")
107
+ end
108
+ end
109
+ end
110
+ 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,38 @@
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
+
13
+ def initialize(endpoint:, timeout:, headers:, protocol:)
14
+ super(endpoint: endpoint, timeout: timeout, headers: headers)
15
+ @telemetry_tags = {'protocol' => protocol, 'encoding' => 'protobuf'}
16
+ end
17
+
18
+ def export(metrics, timeout: nil)
19
+ telemetry&.inc('tracers', METRIC_EXPORT_ATTEMPTS, 1, tags: @telemetry_tags)
20
+ result = super
21
+ metric_name = (result == 0) ? METRIC_EXPORT_SUCCESSES : METRIC_EXPORT_FAILURES
22
+ telemetry&.inc('tracers', metric_name, 1, tags: @telemetry_tags)
23
+ result
24
+ rescue => e
25
+ Datadog.logger.error("Failed to export OpenTelemetry Metrics: #{e.class}: #{e}")
26
+ telemetry&.inc('tracers', METRIC_EXPORT_FAILURES, 1, tags: @telemetry_tags)
27
+ raise
28
+ end
29
+
30
+ private
31
+
32
+ def telemetry
33
+ Datadog.send(:components).telemetry
34
+ end
35
+ end
36
+ end
37
+ end
38
+ 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.to_s if defined?(Bundler))].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
@@ -118,12 +127,12 @@ module Datadog
118
127
  class Library
119
128
  attr_reader :kind, :name, :version
120
129
 
121
- def initialize(kind:, name:, version:, path:, extra_path: nil)
122
- extra_path = nil if extra_path&.empty?
130
+ def initialize(kind:, name:, version:, path:, extra_paths:)
131
+ extra_paths = Array(extra_paths).compact.reject(&:empty?).map { |p| p.dup.freeze }
123
132
  @kind = kind.freeze
124
133
  @name = name.dup.freeze
125
134
  @version = version.to_s.dup.freeze
126
- @paths = [path.dup.freeze, extra_path.dup.freeze].compact.freeze
135
+ @paths = [path.dup.freeze, *extra_paths].freeze
127
136
  freeze
128
137
  end
129
138
 
@@ -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)
@@ -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
@@ -13,6 +13,9 @@ module Datadog
13
13
  ENV_BAGGAGE_TAG_KEYS = 'DD_TRACE_BAGGAGE_TAG_KEYS'
14
14
  ENV_TRACE_ID_128_BIT_GENERATION_ENABLED = 'DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED'
15
15
  ENV_NATIVE_SPAN_EVENTS = 'DD_TRACE_NATIVE_SPAN_EVENTS'
16
+ ENV_RESOURCE_RENAMING_ENABLED = 'DD_TRACE_RESOURCE_RENAMING_ENABLED'
17
+ ENV_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT = 'DD_TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT'
18
+ ENV_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED = 'DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED'
16
19
 
17
20
  # @public_api
18
21
  module SpanAttributeSchema
@@ -104,6 +107,12 @@ module Datadog
104
107
  ENV_ENABLED = 'DD_TRACE_CLIENT_IP_ENABLED'
105
108
  ENV_HEADER_NAME = 'DD_TRACE_CLIENT_IP_HEADER'
106
109
  end
110
+
111
+ # @public_api
112
+ module HTTPErrorStatuses
113
+ ENV_SERVER_ERROR_STATUSES = 'DD_TRACE_HTTP_SERVER_ERROR_STATUSES'
114
+ ENV_CLIENT_ERROR_STATUSES = 'DD_TRACE_HTTP_CLIENT_ERROR_STATUSES'
115
+ end
107
116
  end
108
117
  end
109
118
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative '../../tracing/configuration/ext'
4
4
  require_relative '../../core/environment/variable_helpers'
5
+ require_relative '../contrib/status_range_matcher'
6
+ require_relative '../contrib/status_range_env_parser'
5
7
  require_relative 'http'
6
8
 
7
9
  module Datadog
@@ -272,6 +274,38 @@ module Datadog
272
274
  o.type :bool
273
275
  end
274
276
 
277
+ settings :resource_renaming do
278
+ # Whether resource renaming is enabled. When enabled, http.endpoint tag
279
+ # containing a route will be reported in traces. If AppSec is enabled,
280
+ # this feature will be enabled by default.
281
+ #
282
+ # For web applications built with instrumented frameworks, http.endpoint tag
283
+ # will contain the route as it is defined in the application.
284
+ # For basic Rack applications, or applications that are mounted and are not instrumented,
285
+ # the route will be inferred from the request path.
286
+ #
287
+ # @default `DD_TRACE_RESOURCE_RENAMING_ENABLED` environment variable, otherwise `false`.
288
+ # @return [Boolean]
289
+ option :enabled do |o|
290
+ o.type :bool, nilable: false
291
+ o.env Configuration::Ext::ENV_RESOURCE_RENAMING_ENABLED
292
+ o.default false
293
+ end
294
+
295
+ # When set to true, http.endoint is always inferred from path,
296
+ # instead of using http.route value when it is set.
297
+ #
298
+ # This is useful for testing purposes.
299
+ #
300
+ # @default false
301
+ # @return [Boolean]
302
+ option :always_simplified_endpoint do |o|
303
+ o.type :bool, nilable: false
304
+ o.env Configuration::Ext::ENV_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT
305
+ o.default false
306
+ end
307
+ end
308
+
275
309
  # Forces the tracer to always send span events with the native span events format
276
310
  # regardless of the agent support. This is useful in agent-less setups.
277
311
  #
@@ -490,6 +524,46 @@ module Datadog
490
524
  o.env Tracing::Configuration::Ext::Distributed::ENV_X_DATADOG_TAGS_MAX_LENGTH
491
525
  o.default 512
492
526
  end
527
+
528
+ # HTTP error statuses configuration
529
+ # @public_api
530
+ settings :http_error_statuses do
531
+ # Defines the range of status codes to be considered errors on http.server span kinds.
532
+ # Once set, only the values within the specified range are considered errors.
533
+ #
534
+ # Format of env var: comma-separated list of values like 500,501,502 or ranges like 500-599 (e.g. `500,502,504-510`)
535
+ #
536
+ # @default `DD_TRACE_HTTP_SERVER_ERROR_STATUSES` environment variable, otherwise `500..599`.
537
+ # @return [Tracing::Contrib::StatusRangeMatcher]
538
+ option :server do |o|
539
+ o.env Tracing::Configuration::Ext::HTTPErrorStatuses::ENV_SERVER_ERROR_STATUSES
540
+ o.default 500..599
541
+ o.setter do |v|
542
+ Tracing::Contrib::StatusRangeMatcher.new(v) if v
543
+ end
544
+ o.env_parser do |values|
545
+ Tracing::Contrib::StatusRangeEnvParser.call(values)
546
+ end
547
+ end
548
+
549
+ # Defines the range of status codes to be considered errors on http.client span kinds.
550
+ # Once set, only the values within the specified range are considered errors.
551
+ #
552
+ # Format of env var: comma-separated list of values like 400,401,402 or ranges like 400-499 (e.g. `400,402,404-410`)
553
+ #
554
+ # @default `DD_TRACE_HTTP_CLIENT_ERROR_STATUSES` environment variable, otherwise `400..499`.
555
+ # @return [Tracing::Contrib::StatusRangeMatcher]
556
+ option :client do |o|
557
+ o.env Tracing::Configuration::Ext::HTTPErrorStatuses::ENV_CLIENT_ERROR_STATUSES
558
+ o.default 400..499
559
+ o.setter do |v|
560
+ Tracing::Contrib::StatusRangeMatcher.new(v) if v
561
+ end
562
+ o.env_parser do |values|
563
+ Tracing::Contrib::StatusRangeEnvParser.call(values)
564
+ end
565
+ end
566
+ end
493
567
  end
494
568
  end
495
569
  end
@@ -77,10 +77,10 @@ module Datadog
77
77
 
78
78
  exception = payload[:exception_object]
79
79
  if exception.nil?
80
- # [christian] in some cases :status is not defined,
81
- # rather than firing an error, simply acknowledge we don't know it.
82
- status = payload.fetch(:status, '?').to_s
83
- span.status = 1 if status.start_with?('5')
80
+ status = payload[:status]
81
+ if status && Datadog.configuration.tracing.http_error_statuses.server.include?(status)
82
+ span.status = Tracing::Metadata::Ext::Errors::STATUS
83
+ end
84
84
  elsif Utils.exception_is_error?(exception)
85
85
  span.set_error(exception)
86
86
  end