datadog 2.36.0 → 2.38.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +74 -1
- data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +5 -12
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +29 -18
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +17 -3
- data/ext/datadog_profiling_native_extension/collectors_stack.c +19 -24
- data/ext/datadog_profiling_native_extension/collectors_stack.h +0 -1
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +348 -213
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +3 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +11 -3
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +5 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +2 -0
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +7 -0
- data/ext/datadog_profiling_native_extension/time_helpers.h +12 -7
- data/ext/libdatadog_api/di.c +76 -0
- data/ext/libdatadog_api/extconf.rb +6 -0
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/ai_guard/api_client.rb +6 -4
- data/lib/datadog/appsec/api_security/route_extractor.rb +7 -4
- data/lib/datadog/appsec/configuration.rb +6 -0
- data/lib/datadog/appsec/context.rb +4 -2
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +59 -4
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +59 -5
- data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +1 -1
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +5 -0
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +4 -0
- data/lib/datadog/appsec/contrib/rack/input_peeker.rb +4 -17
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +21 -0
- data/lib/datadog/appsec/contrib/rails/gateway/request.rb +7 -1
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +60 -7
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +4 -0
- data/lib/datadog/appsec/ext.rb +1 -0
- data/lib/datadog/appsec/metrics/collector.rb +18 -1
- data/lib/datadog/appsec/metrics/exporter.rb +9 -3
- data/lib/datadog/appsec/remote.rb +9 -3
- data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
- data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
- data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
- data/lib/datadog/appsec/route_normalizer.rb +80 -0
- data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
- data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
- data/lib/datadog/core/configuration/components.rb +82 -2
- data/lib/datadog/core/configuration/components_state.rb +6 -1
- data/lib/datadog/core/configuration/settings.rb +4 -7
- data/lib/datadog/core/configuration/supported_configurations.rb +3 -0
- data/lib/datadog/core/evp.rb +11 -0
- data/lib/datadog/core/knuth_sampler.rb +3 -1
- data/lib/datadog/core/metrics/client.rb +1 -1
- data/lib/datadog/core/remote/client/capabilities.rb +34 -7
- data/lib/datadog/core/telemetry/event/app_started.rb +8 -1
- data/lib/datadog/core/transport/http/builder.rb +1 -1
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +1 -1
- data/lib/datadog/core/utils/time.rb +5 -1
- data/lib/datadog/di/base.rb +3 -1
- data/lib/datadog/di/boot.rb +3 -0
- data/lib/datadog/di/capture_expression.rb +21 -0
- data/lib/datadog/di/capture_expression_evaluator.rb +71 -0
- data/lib/datadog/di/capture_limits.rb +41 -0
- data/lib/datadog/di/code_tracker.rb +5 -2
- data/lib/datadog/di/component.rb +112 -38
- data/lib/datadog/di/configuration.rb +6 -0
- data/lib/datadog/di/context.rb +9 -3
- data/lib/datadog/di/el/compiler.rb +1 -1
- data/lib/datadog/di/error.rb +10 -0
- data/lib/datadog/di/fatal_exceptions.rb +26 -0
- data/lib/datadog/di/instrumenter.rb +412 -165
- data/lib/datadog/di/probe.rb +61 -2
- data/lib/datadog/di/probe_builder.rb +83 -1
- data/lib/datadog/di/probe_file_loader.rb +8 -3
- data/lib/datadog/di/probe_manager.rb +67 -9
- data/lib/datadog/di/probe_notification_builder.rb +52 -8
- data/lib/datadog/di/probe_notifier_worker.rb +52 -32
- data/lib/datadog/di/redactor.rb +16 -1
- data/lib/datadog/di/remote.rb +176 -15
- data/lib/datadog/di/serializer.rb +34 -15
- data/lib/datadog/di/transport/input.rb +8 -4
- data/lib/datadog/di.rb +63 -0
- data/lib/datadog/kit/appsec/events/v2.rb +60 -2
- data/lib/datadog/open_feature/component.rb +29 -6
- data/lib/datadog/open_feature/configuration.rb +8 -0
- data/lib/datadog/open_feature/ext.rb +2 -0
- data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
- data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
- data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +101 -0
- data/lib/datadog/open_feature/hooks/{flag_eval_hook.rb → flag_eval_metrics_hook.rb} +1 -1
- data/lib/datadog/open_feature/native_evaluator.rb +22 -0
- data/lib/datadog/open_feature/provider.rb +59 -27
- data/lib/datadog/open_feature/transport.rb +55 -1
- data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +5 -12
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +4 -11
- data/lib/datadog/opentelemetry/sdk.rb +19 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +6 -1
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -3
- data/lib/datadog/profiling/collectors/thread_context.rb +9 -1
- data/lib/datadog/profiling/component.rb +17 -0
- data/lib/datadog/symbol_database/component.rb +164 -47
- data/lib/datadog/symbol_database/configuration.rb +12 -4
- data/lib/datadog/symbol_database/extensions.rb +19 -0
- data/lib/datadog/symbol_database/extractor.rb +124 -87
- data/lib/datadog/symbol_database/file_hash.rb +3 -1
- data/lib/datadog/symbol_database/remote.rb +8 -3
- data/lib/datadog/symbol_database/scope_batcher.rb +7 -3
- data/lib/datadog/symbol_database/uploader.rb +9 -2
- data/lib/datadog/symbol_database.rb +22 -0
- data/lib/datadog/tracing/contrib/active_job/data_streams.rb +62 -0
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +12 -0
- data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +2 -3
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +12 -12
- data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/sequel/utils.rb +2 -2
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +1 -1
- data/lib/datadog/tracing/contrib/sinatra/framework.rb +1 -1
- data/lib/datadog/tracing/distributed/baggage.rb +2 -1
- data/lib/datadog/tracing/ext.rb +9 -0
- data/lib/datadog/tracing/remote.rb +34 -3
- data/lib/datadog/tracing/transport/trace_formatter.rb +1 -1
- data/lib/datadog/version.rb +1 -1
- data/lib/datadog.rb +1 -0
- metadata +25 -8
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'ext'
|
|
4
|
+
require_relative '../core/utils/time'
|
|
4
5
|
require 'open_feature/sdk'
|
|
5
6
|
|
|
6
7
|
module Datadog
|
|
7
8
|
module OpenFeature
|
|
8
9
|
# OpenFeature feature flagging provider backed by Datadog Remote Configuration.
|
|
9
10
|
#
|
|
10
|
-
# Requires openfeature-sdk >= 0.5.1 for flag evaluation metrics support.
|
|
11
|
+
# Requires openfeature-sdk >= 0.5.1 for flag evaluation metrics and EVP hook support.
|
|
12
|
+
#
|
|
13
|
+
# Hook lifecycle note: FlagEvalEVPHook is returned from #hooks so EVP uses the SDK-final
|
|
14
|
+
# EvaluationDetails. This matches FlagEvalMetricsHook and records defaults/errors produced
|
|
15
|
+
# by OpenFeature hook failures or post-provider type validation.
|
|
11
16
|
#
|
|
12
17
|
# Implementation follows the OpenFeature contract of Provider SDK.
|
|
13
18
|
# For details see:
|
|
@@ -70,8 +75,10 @@ module Datadog
|
|
|
70
75
|
end
|
|
71
76
|
|
|
72
77
|
def hooks
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
component = Datadog.send(:components).open_feature
|
|
79
|
+
otel_hook = component&.flag_eval_metrics_hook
|
|
80
|
+
evp_hook = component&.flag_eval_evp_hook
|
|
81
|
+
[otel_hook, evp_hook].compact
|
|
75
82
|
end
|
|
76
83
|
|
|
77
84
|
def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil)
|
|
@@ -101,57 +108,82 @@ module Datadog
|
|
|
101
108
|
private
|
|
102
109
|
|
|
103
110
|
def evaluate(flag_key, default_value:, expected_type:, evaluation_context:)
|
|
111
|
+
# Stamp evaluation entry time once, here on the eval thread. The EVP path uses this for
|
|
112
|
+
# accurate first/last_evaluation bounds instead of a later hook-fire clock read.
|
|
113
|
+
eval_time_ms = (Core::Utils::Time.now.to_f * 1000).to_i
|
|
114
|
+
|
|
104
115
|
engine = OpenFeature.engine
|
|
105
|
-
return component_not_configured_default(default_value) if engine.nil?
|
|
116
|
+
return component_not_configured_default(default_value, eval_time_ms) if engine.nil?
|
|
117
|
+
|
|
118
|
+
result = fetch_engine_value(engine, flag_key, default_value, expected_type, evaluation_context)
|
|
119
|
+
|
|
120
|
+
# Build metadata before branching so provider-returned details carry eval-entry time.
|
|
121
|
+
flag_meta = build_flag_metadata(result, eval_time_ms)
|
|
122
|
+
|
|
123
|
+
if result.error?
|
|
124
|
+
return sdk_error_details(default_value, result.error_code, result.error_message, result.reason, flag_meta)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
sdk_success_details(result, flag_meta)
|
|
128
|
+
rescue => e
|
|
129
|
+
error_message = "#{e.class}: #{e.message}"
|
|
130
|
+
error_result = Datadog::OpenFeature::ResolutionDetails.build_error(
|
|
131
|
+
value: default_value,
|
|
132
|
+
error_code: Ext::GENERAL,
|
|
133
|
+
error_message: error_message
|
|
134
|
+
)
|
|
135
|
+
error_flag_meta = build_flag_metadata(error_result, eval_time_ms || (Core::Utils::Time.now.to_f * 1000).to_i)
|
|
106
136
|
|
|
107
|
-
|
|
137
|
+
sdk_error_details(default_value, Ext::GENERAL, error_message, Ext::ERROR, error_flag_meta)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def fetch_engine_value(engine, flag_key, default_value, expected_type, evaluation_context)
|
|
141
|
+
engine.fetch_value(
|
|
108
142
|
flag_key,
|
|
109
143
|
default_value: default_value,
|
|
110
144
|
expected_type: expected_type,
|
|
111
145
|
evaluation_context: evaluation_context
|
|
112
146
|
)
|
|
147
|
+
end
|
|
113
148
|
|
|
114
|
-
|
|
115
|
-
return ::OpenFeature::SDK::Provider::ResolutionDetails.new(
|
|
116
|
-
value: default_value,
|
|
117
|
-
error_code: result.error_code,
|
|
118
|
-
error_message: result.error_message,
|
|
119
|
-
reason: result.reason
|
|
120
|
-
)
|
|
121
|
-
end
|
|
122
|
-
|
|
149
|
+
def sdk_success_details(result, flag_meta)
|
|
123
150
|
::OpenFeature::SDK::Provider::ResolutionDetails.new(
|
|
124
151
|
value: result.value,
|
|
125
152
|
variant: result.variant,
|
|
126
153
|
reason: result.reason,
|
|
127
|
-
flag_metadata:
|
|
154
|
+
flag_metadata: flag_meta,
|
|
128
155
|
)
|
|
129
|
-
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def sdk_error_details(default_value, error_code, error_message, reason, flag_meta = {})
|
|
130
159
|
::OpenFeature::SDK::Provider::ResolutionDetails.new(
|
|
131
160
|
value: default_value,
|
|
132
|
-
error_code:
|
|
133
|
-
error_message:
|
|
134
|
-
reason:
|
|
161
|
+
error_code: error_code,
|
|
162
|
+
error_message: error_message,
|
|
163
|
+
reason: reason,
|
|
164
|
+
flag_metadata: flag_meta
|
|
135
165
|
)
|
|
136
166
|
end
|
|
137
167
|
|
|
138
|
-
def build_flag_metadata(result)
|
|
139
|
-
metadata = result.flag_metadata || {}
|
|
168
|
+
def build_flag_metadata(result, eval_time_ms)
|
|
169
|
+
metadata = result.flag_metadata&.dup || {}
|
|
140
170
|
allocation_key = result.allocation_key
|
|
141
|
-
if allocation_key && !allocation_key.empty?
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
171
|
+
metadata['__dd_allocation_key'] = allocation_key if allocation_key && !allocation_key.empty?
|
|
172
|
+
|
|
173
|
+
# Eval-time stamped at provider entry; the EVP hook reads 'dd.eval.timestamp_ms' for
|
|
174
|
+
# accurate first/last_evaluation bounds (it falls back to hook-fire time when absent).
|
|
175
|
+
metadata['dd.eval.timestamp_ms'] = eval_time_ms
|
|
145
176
|
|
|
146
177
|
metadata
|
|
147
178
|
end
|
|
148
179
|
|
|
149
|
-
def component_not_configured_default(value)
|
|
180
|
+
def component_not_configured_default(value, eval_time_ms)
|
|
150
181
|
::OpenFeature::SDK::Provider::ResolutionDetails.new(
|
|
151
182
|
value: value,
|
|
152
183
|
error_code: Ext::PROVIDER_FATAL,
|
|
153
184
|
error_message: "Datadog's OpenFeature component must be configured",
|
|
154
|
-
reason: Ext::ERROR
|
|
185
|
+
reason: Ext::ERROR,
|
|
186
|
+
flag_metadata: {'dd.eval.timestamp_ms' => eval_time_ms}
|
|
155
187
|
)
|
|
156
188
|
end
|
|
157
189
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../core/encoding'
|
|
4
|
+
require_relative '../core/evp'
|
|
4
5
|
require_relative '../core/transport/http'
|
|
5
6
|
require_relative '../core/transport/http/env'
|
|
6
7
|
require_relative '../core/transport/http/api/endpoint'
|
|
@@ -12,6 +13,7 @@ module Datadog
|
|
|
12
13
|
module OpenFeature
|
|
13
14
|
module Transport
|
|
14
15
|
class HTTP
|
|
16
|
+
# API spec for the EVP exposures endpoint.
|
|
15
17
|
class Spec
|
|
16
18
|
def initialize
|
|
17
19
|
@endpoint = Core::Transport::HTTP::API::Endpoint.new(
|
|
@@ -23,7 +25,30 @@ module Datadog
|
|
|
23
25
|
def call(env, &block)
|
|
24
26
|
@endpoint.call(env) do |request_env|
|
|
25
27
|
request_env.headers['Content-Type'] = env.request.parcel.content_type
|
|
26
|
-
request_env.headers[
|
|
28
|
+
request_env.headers[Core::EVP::SUBDOMAIN_HEADER_NAME] =
|
|
29
|
+
Core::EVP::EVENT_PLATFORM_INTAKE_SUBDOMAIN
|
|
30
|
+
request_env.body = env.request.parcel.data
|
|
31
|
+
|
|
32
|
+
block.call(request_env)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# API spec for the EVP flagevaluation endpoint.
|
|
38
|
+
# Path: /evp_proxy/v2/api/v2/flagevaluation
|
|
39
|
+
# Header: X-Datadog-EVP-Subdomain: event-platform-intake (same as exposures)
|
|
40
|
+
class FlagevaluationsSpec
|
|
41
|
+
def initialize
|
|
42
|
+
@endpoint = Core::Transport::HTTP::API::Endpoint.new(
|
|
43
|
+
:post, '/evp_proxy/v2/api/v2/flagevaluation'
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def call(env, &block)
|
|
48
|
+
@endpoint.call(env) do |request_env|
|
|
49
|
+
request_env.headers['Content-Type'] = env.request.parcel.content_type
|
|
50
|
+
request_env.headers[Core::EVP::SUBDOMAIN_HEADER_NAME] =
|
|
51
|
+
Core::EVP::EVENT_PLATFORM_INTAKE_SUBDOMAIN
|
|
27
52
|
request_env.body = env.request.parcel.data
|
|
28
53
|
|
|
29
54
|
block.call(request_env)
|
|
@@ -38,6 +63,14 @@ module Datadog
|
|
|
38
63
|
) { |t| t.api('exposures', HTTP::Spec.new) }.to_transport(self)
|
|
39
64
|
end
|
|
40
65
|
|
|
66
|
+
# Build a transport instance for the flagevaluation EVP endpoint.
|
|
67
|
+
def self.build_flagevaluations(agent_settings:, logger:)
|
|
68
|
+
Core::Transport::HTTP.build(
|
|
69
|
+
agent_settings: agent_settings,
|
|
70
|
+
logger: logger
|
|
71
|
+
) { |t| t.api('flagevaluations', HTTP::FlagevaluationsSpec.new) }.to_transport(self)
|
|
72
|
+
end
|
|
73
|
+
|
|
41
74
|
def initialize(apis, default_api, logger:)
|
|
42
75
|
@api = apis[default_api]
|
|
43
76
|
@logger = logger
|
|
@@ -61,6 +94,27 @@ module Datadog
|
|
|
61
94
|
|
|
62
95
|
Core::Transport::InternalErrorResponse.new(e)
|
|
63
96
|
end
|
|
97
|
+
|
|
98
|
+
# POST a flag evaluations batch to /evp_proxy/v2/api/v2/flagevaluation.
|
|
99
|
+
# Mirrors send_exposures; uses the FlagevaluationsSpec endpoint.
|
|
100
|
+
def send_flag_evaluations(payload)
|
|
101
|
+
encoder = Core::Encoding::JSONEncoder
|
|
102
|
+
parcel = Core::Transport::Parcel.new(
|
|
103
|
+
encoder.encode(payload),
|
|
104
|
+
content_type: encoder.content_type
|
|
105
|
+
)
|
|
106
|
+
request = Core::Transport::Request.new(parcel)
|
|
107
|
+
|
|
108
|
+
@api.endpoint.call(Core::Transport::HTTP::Env.new(request)) do |env|
|
|
109
|
+
@api.call(env)
|
|
110
|
+
end
|
|
111
|
+
rescue => e
|
|
112
|
+
message = "Internal error during request. Cause: #{e.class}: #{e.message} " \
|
|
113
|
+
"Location: #{Array(e.backtrace).first}"
|
|
114
|
+
@logger.debug(message)
|
|
115
|
+
|
|
116
|
+
Core::Transport::InternalErrorResponse.new(e)
|
|
117
|
+
end
|
|
64
118
|
end
|
|
65
119
|
end
|
|
66
120
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'opentelemetry/exporter/otlp_logs'
|
|
4
|
+
require_relative '../sdk'
|
|
4
5
|
|
|
5
6
|
module Datadog
|
|
6
7
|
module OpenTelemetry
|
|
@@ -10,27 +11,19 @@ module Datadog
|
|
|
10
11
|
METRIC_EXPORT_SUCCESSES = 'otel.logs_export_successes'
|
|
11
12
|
METRIC_EXPORT_FAILURES = 'otel.logs_export_failures'
|
|
12
13
|
METRIC_LOG_RECORDS = 'otel.log_records'
|
|
13
|
-
TELEMETRY_NAMESPACE = 'tracers'
|
|
14
|
-
TELEMETRY_TAGS = {'protocol' => 'http', 'encoding' => 'protobuf'}.freeze
|
|
15
14
|
|
|
16
15
|
def export(log_records, timeout: nil)
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
SDK.telemetry_inc(METRIC_EXPORT_ATTEMPTS, 1)
|
|
17
|
+
SDK.telemetry_inc(METRIC_LOG_RECORDS, log_records.size)
|
|
19
18
|
result = super
|
|
20
19
|
metric_name = (result == 0) ? METRIC_EXPORT_SUCCESSES : METRIC_EXPORT_FAILURES
|
|
21
|
-
|
|
20
|
+
SDK.telemetry_inc(metric_name, 1)
|
|
22
21
|
result
|
|
23
22
|
rescue => e
|
|
24
23
|
Datadog.logger.warn("Failed to export OpenTelemetry Logs: #{e.class}: #{e.message}")
|
|
25
|
-
|
|
24
|
+
SDK.telemetry_inc(METRIC_EXPORT_FAILURES, 1)
|
|
26
25
|
raise
|
|
27
26
|
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
|
|
31
|
-
def telemetry
|
|
32
|
-
Datadog.send(:components).telemetry
|
|
33
|
-
end
|
|
34
27
|
end
|
|
35
28
|
end
|
|
36
29
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'opentelemetry/exporter/otlp_metrics'
|
|
4
|
+
require_relative '../sdk'
|
|
4
5
|
|
|
5
6
|
module Datadog
|
|
6
7
|
module OpenTelemetry
|
|
@@ -9,26 +10,18 @@ module Datadog
|
|
|
9
10
|
METRIC_EXPORT_ATTEMPTS = 'otel.metrics_export_attempts'
|
|
10
11
|
METRIC_EXPORT_SUCCESSES = 'otel.metrics_export_successes'
|
|
11
12
|
METRIC_EXPORT_FAILURES = 'otel.metrics_export_failures'
|
|
12
|
-
TELEMETRY_NAMESPACE = 'tracers'
|
|
13
|
-
TELEMETRY_TAGS = {'protocol' => "http", 'encoding' => 'protobuf'}
|
|
14
13
|
|
|
15
14
|
def export(metrics, timeout: nil)
|
|
16
|
-
|
|
15
|
+
SDK.telemetry_inc(METRIC_EXPORT_ATTEMPTS, 1)
|
|
17
16
|
result = super
|
|
18
17
|
metric_name = (result == 0) ? METRIC_EXPORT_SUCCESSES : METRIC_EXPORT_FAILURES
|
|
19
|
-
|
|
18
|
+
SDK.telemetry_inc(metric_name, 1)
|
|
20
19
|
result
|
|
21
20
|
rescue => e
|
|
22
21
|
Datadog.logger.error("Failed to export OpenTelemetry Metrics: #{e.class}: #{e.message}")
|
|
23
|
-
|
|
22
|
+
SDK.telemetry_inc(METRIC_EXPORT_FAILURES, 1)
|
|
24
23
|
raise
|
|
25
24
|
end
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
def telemetry
|
|
30
|
-
Datadog.send(:components).telemetry
|
|
31
|
-
end
|
|
32
25
|
end
|
|
33
26
|
end
|
|
34
27
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../tracing/ext'
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module OpenTelemetry
|
|
7
|
+
module SDK
|
|
8
|
+
TELEMETRY_TAGS = {'protocol' => 'http', 'encoding' => 'protobuf'}.freeze
|
|
9
|
+
|
|
10
|
+
def self.telemetry_inc(metric_name, value)
|
|
11
|
+
telemetry&.inc(Datadog::Tracing::Ext::TELEMETRY_METRICS_NAMESPACE, metric_name, value, tags: TELEMETRY_TAGS)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.telemetry
|
|
15
|
+
Datadog.send(:components).telemetry
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -49,7 +49,7 @@ module Datadog
|
|
|
49
49
|
# profiler overhead!
|
|
50
50
|
dynamic_sampling_rate_enabled: true,
|
|
51
51
|
skip_idle_samples_for_testing: false,
|
|
52
|
-
idle_sampling_helper: IdleSamplingHelper.new
|
|
52
|
+
idle_sampling_helper: IdleSamplingHelper.new(thread_context_collector: thread_context_collector)
|
|
53
53
|
)
|
|
54
54
|
unless dynamic_sampling_rate_enabled
|
|
55
55
|
Datadog.logger.warn(
|
|
@@ -170,11 +170,16 @@ module Datadog
|
|
|
170
170
|
|
|
171
171
|
@wait_until_running_condition.wait(@wait_until_running_mutex, timeout_seconds)
|
|
172
172
|
|
|
173
|
+
# Not using `_native_is_running?(self) || raise(...)` here: `_native_is_running?` is declared
|
|
174
|
+
# to return `bool`, so steep infers the `||` expression as `bool` too and rejects it against
|
|
175
|
+
# this method's declared `true` return type. The explicit `if`/`else` type-checks correctly.
|
|
176
|
+
# rubocop:disable Style/RedundantCondition
|
|
173
177
|
if self.class._native_is_running?(self)
|
|
174
178
|
true
|
|
175
179
|
else
|
|
176
180
|
raise "Timeout waiting for #{self.class.name} to start (waited for #{timeout_seconds} seconds)"
|
|
177
181
|
end
|
|
182
|
+
# rubocop:enable Style/RedundantCondition
|
|
178
183
|
end
|
|
179
184
|
end
|
|
180
185
|
|
|
@@ -10,6 +10,7 @@ module Datadog
|
|
|
10
10
|
class IdleSamplingHelper
|
|
11
11
|
# @rbs @worker_thread: untyped
|
|
12
12
|
# @rbs @start_stop_mutex: ::Thread::Mutex
|
|
13
|
+
# @rbs @thread_context_collector: Datadog::Profiling::Collectors::ThreadContext?
|
|
13
14
|
|
|
14
15
|
private
|
|
15
16
|
|
|
@@ -17,10 +18,11 @@ module Datadog
|
|
|
17
18
|
|
|
18
19
|
public
|
|
19
20
|
|
|
20
|
-
#: () -> void
|
|
21
|
-
def initialize
|
|
21
|
+
#: (thread_context_collector: Datadog::Profiling::Collectors::ThreadContext) -> void
|
|
22
|
+
def initialize(thread_context_collector:)
|
|
22
23
|
@worker_thread = nil
|
|
23
24
|
@start_stop_mutex = Mutex.new
|
|
25
|
+
@thread_context_collector = thread_context_collector
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
#: () -> (nil | true)
|
|
@@ -37,7 +39,7 @@ module Datadog
|
|
|
37
39
|
@worker_thread = Thread.new do
|
|
38
40
|
Thread.current.name = self.class.name
|
|
39
41
|
|
|
40
|
-
self.class._native_idle_sampling_loop(self)
|
|
42
|
+
self.class._native_idle_sampling_loop(self, @thread_context_collector)
|
|
41
43
|
|
|
42
44
|
Datadog.logger.debug("IdleSamplingHelper thread stopping cleanly")
|
|
43
45
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
@@ -45,9 +45,10 @@ module Datadog
|
|
|
45
45
|
waiting_for_gvl_threshold_ns: 10_000_000,
|
|
46
46
|
otel_context_enabled: false,
|
|
47
47
|
native_filenames_enabled: true,
|
|
48
|
+
trigger_global_reset: true,
|
|
48
49
|
**options
|
|
49
50
|
)
|
|
50
|
-
new(
|
|
51
|
+
collector = new(
|
|
51
52
|
recorder: recorder,
|
|
52
53
|
max_frames: max_frames,
|
|
53
54
|
tracer: tracer,
|
|
@@ -57,6 +58,13 @@ module Datadog
|
|
|
57
58
|
native_filenames_enabled: native_filenames_enabled,
|
|
58
59
|
**options,
|
|
59
60
|
)
|
|
61
|
+
|
|
62
|
+
# By default, mirror what the CpuAndWallTimeWorker does when profiling starts: reset the global per-thread
|
|
63
|
+
# context state, which (among other things) resizes the per-thread sampling buffers to this collector's
|
|
64
|
+
# max_frames. Tests that need to control this explicitly can pass `trigger_global_reset: false`.
|
|
65
|
+
Testing._native_global_reset_per_thread_context(collector) if trigger_global_reset
|
|
66
|
+
|
|
67
|
+
collector
|
|
60
68
|
end
|
|
61
69
|
|
|
62
70
|
def inspect
|
|
@@ -226,6 +226,15 @@ module Datadog
|
|
|
226
226
|
return false
|
|
227
227
|
end
|
|
228
228
|
|
|
229
|
+
# Heap profiling relies on `ObjectSpace._id2ref`, which was removed on Ruby 4.1
|
|
230
|
+
# (https://bugs.ruby-lang.org/issues/22135).
|
|
231
|
+
if RubyVersion.is?(">= 4.1")
|
|
232
|
+
logger.warn(
|
|
233
|
+
"Heap profiling is currently incompatible with Ruby 4.1+ and has been disabled."
|
|
234
|
+
)
|
|
235
|
+
return false
|
|
236
|
+
end
|
|
237
|
+
|
|
229
238
|
unless allocation_profiling_enabled
|
|
230
239
|
logger.warn(
|
|
231
240
|
"Heap profiling was requested but allocation profiling is not enabled. " \
|
|
@@ -244,6 +253,14 @@ module Datadog
|
|
|
244
253
|
|
|
245
254
|
return false unless heap_profiling_enabled && heap_size_profiling_enabled
|
|
246
255
|
|
|
256
|
+
if RubyVersion.is?(">= 4")
|
|
257
|
+
logger.info(
|
|
258
|
+
"Heap live size profiling is currently incompatible with Ruby 4 and has been disabled. " \
|
|
259
|
+
"Heap live objects is not affected and remains enabled."
|
|
260
|
+
)
|
|
261
|
+
return false
|
|
262
|
+
end
|
|
263
|
+
|
|
247
264
|
true
|
|
248
265
|
end
|
|
249
266
|
|