datadog 2.35.0 → 2.37.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 +85 -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 +71 -31
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +18 -4
- data/ext/datadog_profiling_native_extension/collectors_stack.c +37 -18
- data/ext/datadog_profiling_native_extension/collectors_stack.h +8 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +596 -341
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +11 -7
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +7 -8
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -12
- data/ext/datadog_profiling_native_extension/extconf.rb +13 -2
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +4 -43
- data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -47
- data/ext/datadog_profiling_native_extension/heap_recorder.c +44 -26
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +14 -35
- data/ext/datadog_profiling_native_extension/profiling.c +41 -4
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +40 -34
- data/ext/datadog_profiling_native_extension/stack_recorder.c +24 -3
- data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +4 -2
- data/ext/libdatadog_api/datadog_ruby_common.c +7 -8
- data/ext/libdatadog_api/datadog_ruby_common.h +0 -12
- 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 +13 -4
- data/lib/datadog/appsec/component.rb +1 -1
- data/lib/datadog/appsec/configuration.rb +13 -0
- data/lib/datadog/appsec/context.rb +4 -2
- data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +37 -4
- 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 +64 -19
- data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -0
- data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
- data/lib/datadog/appsec/contrib/rack/gateway/request.rb +41 -3
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +24 -7
- data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +21 -0
- data/lib/datadog/appsec/contrib/rails/gateway/request.rb +35 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +17 -1
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +60 -7
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +24 -3
- data/lib/datadog/appsec/default_header_tags.rb +10 -6
- 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/components.rb +83 -2
- data/lib/datadog/core/configuration/components_state.rb +6 -1
- data/lib/datadog/core/configuration/settings.rb +1 -5
- data/lib/datadog/core/configuration/supported_configurations.rb +4 -0
- data/lib/datadog/core/evp.rb +11 -0
- data/lib/datadog/core/remote/client/capabilities.rb +34 -7
- data/lib/datadog/core/remote/component.rb +1 -1
- data/lib/datadog/core/telemetry/event/app_started.rb +8 -22
- data/lib/datadog/core/utils/at_fork_monkey_patch.rb +1 -1
- data/lib/datadog/core/utils/forking.rb +3 -1
- data/lib/datadog/core/utils/spawn_monkey_patch.rb +3 -1
- data/lib/datadog/core.rb +3 -0
- data/lib/datadog/di/base.rb +7 -2
- data/lib/datadog/di/code_tracker.rb +5 -2
- data/lib/datadog/di/component.rb +110 -36
- data/lib/datadog/di/error.rb +10 -0
- data/lib/datadog/di/fatal_exceptions.rb +26 -0
- data/lib/datadog/di/instrumenter.rb +381 -165
- data/lib/datadog/di/probe.rb +14 -0
- 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 +4 -1
- data/lib/datadog/di/probe_notifier_worker.rb +52 -32
- data/lib/datadog/di/redactor.rb +16 -1
- data/lib/datadog/di/remote.rb +175 -14
- data/lib/datadog/di/serializer.rb +12 -5
- data/lib/datadog/di/transport/input.rb +8 -4
- data/lib/datadog/di.rb +63 -0
- data/lib/datadog/error_tracking/collector.rb +2 -1
- data/lib/datadog/error_tracking/component.rb +2 -2
- data/lib/datadog/kit/appsec/events/v2.rb +60 -2
- data/lib/datadog/kit/tracing/method_tracer.rb +4 -1
- 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/propagator.rb +9 -3
- data/lib/datadog/opentelemetry/sdk/span_processor.rb +4 -1
- data/lib/datadog/opentelemetry/sdk.rb +19 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -3
- data/lib/datadog/profiling/collectors/thread_context.rb +1 -0
- data/lib/datadog/profiling/component.rb +13 -15
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
- data/lib/datadog/ruby_version.rb +25 -0
- data/lib/datadog/symbol_database/component.rb +453 -128
- 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 +326 -150
- 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/configuration/ext.rb +13 -0
- data/lib/datadog/tracing/configuration/settings.rb +17 -0
- data/lib/datadog/tracing/contrib/configuration/resolver.rb +7 -0
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/grpc.rb +1 -0
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/http.rb +1 -0
- data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/karafka.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -1
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +3 -1
- data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq.rb +1 -0
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +2 -0
- data/lib/datadog/tracing/contrib/waterdrop.rb +1 -0
- data/lib/datadog/tracing/distributed/baggage.rb +2 -1
- data/lib/datadog/tracing/distributed/propagation.rb +33 -1
- data/lib/datadog/tracing/distributed/trace_context.rb +11 -2
- data/lib/datadog/tracing/ext.rb +9 -0
- data/lib/datadog/tracing/remote.rb +34 -3
- data/lib/datadog/tracing/trace_digest.rb +7 -0
- data/lib/datadog/tracing/trace_operation.rb +4 -1
- data/lib/datadog/tracing/tracer.rb +1 -0
- data/lib/datadog/version.rb +1 -1
- data/lib/datadog.rb +5 -1
- metadata +22 -6
|
@@ -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
|
|
@@ -43,6 +43,15 @@ module Datadog
|
|
|
43
43
|
digest = @datadog_propagator.extract(carrier)
|
|
44
44
|
return context unless digest
|
|
45
45
|
|
|
46
|
+
# Always call continue_trace! so span links are created in restart mode
|
|
47
|
+
# (when digest.trace_id is nil) before we attempt to build the OTel SpanContext.
|
|
48
|
+
trace = Tracing.continue_trace!(digest)
|
|
49
|
+
|
|
50
|
+
# In restart mode DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT=restart, the extracted
|
|
51
|
+
# digest carries no trace_id (fresh start). continue_trace! has already recorded
|
|
52
|
+
# the span link; return the current context so the caller's span starts a new root.
|
|
53
|
+
return context unless digest.trace_id
|
|
54
|
+
|
|
46
55
|
# Converts the {Numeric} Datadog id object to OpenTelemetry's byte array format.
|
|
47
56
|
# 128-bit unsigned, big-endian integer
|
|
48
57
|
trace_id = [digest.trace_id >> 64, digest.trace_id & 0xFFFFFFFFFFFFFFFF].pack('Q>Q>')
|
|
@@ -70,9 +79,6 @@ module Datadog
|
|
|
70
79
|
)
|
|
71
80
|
|
|
72
81
|
span = ::OpenTelemetry::Trace.non_recording_span(span_context)
|
|
73
|
-
|
|
74
|
-
trace = Tracing.continue_trace!(digest)
|
|
75
|
-
|
|
76
82
|
span.datadog_trace = trace
|
|
77
83
|
|
|
78
84
|
::OpenTelemetry::Trace.context_with_span(span, parent_context: context)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'trace/span'
|
|
4
|
+
require_relative '../../core/utils'
|
|
4
5
|
require_relative '../../tracing/span_link'
|
|
5
6
|
require_relative '../../tracing/span_event'
|
|
6
7
|
require_relative '../../tracing/trace_digest'
|
|
@@ -101,12 +102,14 @@ module Datadog
|
|
|
101
102
|
|
|
102
103
|
unless span.links.nil?
|
|
103
104
|
datadog_span.links = span.links.map do |link|
|
|
105
|
+
tracestate = link.span_context.tracestate&.to_s
|
|
106
|
+
|
|
104
107
|
Datadog::Tracing::SpanLink.new(
|
|
105
108
|
Datadog::Tracing::TraceDigest.new(
|
|
106
109
|
trace_id: link.span_context.hex_trace_id.to_i(16),
|
|
107
110
|
span_id: link.span_context.hex_span_id.to_i(16),
|
|
108
111
|
trace_sampling_priority: (link.span_context.trace_flags&.sampled? ? 1 : 0),
|
|
109
|
-
trace_state:
|
|
112
|
+
trace_state: tracestate && ::Datadog::Core::Utils.utf8_encode(tracestate, placeholder: nil),
|
|
110
113
|
span_remote: link.span_context.remote?,
|
|
111
114
|
),
|
|
112
115
|
attributes: link.attributes
|
|
@@ -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(
|
|
@@ -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
|
|
@@ -141,15 +141,14 @@ module Datadog
|
|
|
141
141
|
# that causes a segmentation fault during garbage collection of Ractors
|
|
142
142
|
# (https://bugs.ruby-lang.org/issues/18464). We don't allow enabling gc profiling on such Rubies.
|
|
143
143
|
# This bug is fixed on Ruby versions 3.1.4, 3.2.3 and 3.3.0.
|
|
144
|
-
if
|
|
145
|
-
|
|
146
|
-
(RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3")
|
|
144
|
+
if RubyVersion.is?(">= 3", "< 3.1.4") ||
|
|
145
|
+
RubyVersion.is?(">= 3.2", "< 3.2.3")
|
|
147
146
|
logger.warn(
|
|
148
147
|
"Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling GC profiling would cause " \
|
|
149
148
|
"crashes (https://bugs.ruby-lang.org/issues/18464). GC profiling has been disabled."
|
|
150
149
|
)
|
|
151
150
|
return false
|
|
152
|
-
elsif
|
|
151
|
+
elsif RubyVersion.is?(">= 3", "< 4")
|
|
153
152
|
logger.debug(
|
|
154
153
|
"Using Ractors may result in GC profiling unexpectedly " \
|
|
155
154
|
"stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
|
|
@@ -177,7 +176,7 @@ module Datadog
|
|
|
177
176
|
# Ruby 3.2.0 to 3.2.2 have a bug in the newobj tracepoint (https://bugs.ruby-lang.org/issues/19482,
|
|
178
177
|
# https://github.com/ruby/ruby/pull/7464) that makes this crash in any configuration. This bug is
|
|
179
178
|
# fixed on Ruby versions 3.2.3 and 3.3.0.
|
|
180
|
-
if
|
|
179
|
+
if RubyVersion.is?(">= 3.2", "< 3.2.3")
|
|
181
180
|
logger.warn(
|
|
182
181
|
"Allocation profiling is not supported in Ruby versions 3.2.0, 3.2.1 and 3.2.2 and will be forcibly " \
|
|
183
182
|
"disabled. This is due to a VM bug that can lead to crashes (https://bugs.ruby-lang.org/issues/19482). " \
|
|
@@ -191,9 +190,8 @@ module Datadog
|
|
|
191
190
|
# that causes a segmentation fault during garbage collection of Ractors
|
|
192
191
|
# (https://bugs.ruby-lang.org/issues/18464). We don't recommend using this feature on such Rubies.
|
|
193
192
|
# This bug is fixed on Ruby versions 3.1.4, 3.2.3 and 3.3.0.
|
|
194
|
-
if
|
|
195
|
-
|
|
196
|
-
(RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3")
|
|
193
|
+
if RubyVersion.is?(">= 3", "< 3.1.4") ||
|
|
194
|
+
RubyVersion.is?(">= 3.2", "< 3.2.3")
|
|
197
195
|
logger.warn(
|
|
198
196
|
"Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling allocation profiling while using " \
|
|
199
197
|
"Ractors may cause unexpected issues, including crashes (https://bugs.ruby-lang.org/issues/18464). " \
|
|
@@ -202,7 +200,7 @@ module Datadog
|
|
|
202
200
|
# ANNOYANCE - Only with Ractors
|
|
203
201
|
# On all known versions of Ruby 3.x, due to https://bugs.ruby-lang.org/issues/19112, when a ractor gets
|
|
204
202
|
# garbage collected, Ruby will disable all active tracepoints, which this feature internally relies on.
|
|
205
|
-
elsif
|
|
203
|
+
elsif RubyVersion.is?(">= 3", "< 4")
|
|
206
204
|
logger.debug(
|
|
207
205
|
"Using Ractors may result in allocation profiling " \
|
|
208
206
|
"stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
|
|
@@ -220,7 +218,7 @@ module Datadog
|
|
|
220
218
|
|
|
221
219
|
return false unless heap_profiling_enabled
|
|
222
220
|
|
|
223
|
-
if
|
|
221
|
+
if RubyVersion.is?("< 3.1")
|
|
224
222
|
logger.warn(
|
|
225
223
|
"Current Ruby version (#{RUBY_VERSION}) cannot support heap profiling due to VM limitations. " \
|
|
226
224
|
"Please upgrade to Ruby >= 3.1 in order to use this feature. Heap profiling has been disabled."
|
|
@@ -262,7 +260,7 @@ module Datadog
|
|
|
262
260
|
end
|
|
263
261
|
|
|
264
262
|
if setting_value == false
|
|
265
|
-
if
|
|
263
|
+
if RubyVersion.is?("< 2.6")
|
|
266
264
|
logger.warn(
|
|
267
265
|
'The profiling "no signals" workaround has been disabled via configuration on Ruby 2.5. ' \
|
|
268
266
|
"This is not recommended " \
|
|
@@ -288,7 +286,7 @@ module Datadog
|
|
|
288
286
|
# Setting is in auto mode. Let's probe to see if we should enable it:
|
|
289
287
|
|
|
290
288
|
# We don't warn users in this situation because "upgrade your Ruby" is not a great warning
|
|
291
|
-
return true if
|
|
289
|
+
return true if RubyVersion.is?("< 2.6")
|
|
292
290
|
|
|
293
291
|
if Gem.loaded_specs["mysql2"] && incompatible_libmysqlclient_version?(settings, logger)
|
|
294
292
|
logger.warn(
|
|
@@ -450,13 +448,13 @@ module Datadog
|
|
|
450
448
|
end
|
|
451
449
|
|
|
452
450
|
private_class_method def self.dir_interruption_workaround_enabled?(settings, no_signals_workaround_enabled)
|
|
453
|
-
return false if no_signals_workaround_enabled ||
|
|
451
|
+
return false if no_signals_workaround_enabled || RubyVersion.is?(">= 3.4")
|
|
454
452
|
|
|
455
453
|
settings.profiling.advanced.dir_interruption_workaround_enabled
|
|
456
454
|
end
|
|
457
455
|
|
|
458
456
|
private_class_method def self.can_apply_exec_monkey_patch?(settings)
|
|
459
|
-
return false if
|
|
457
|
+
return false if RubyVersion.is?("< 2.7")
|
|
460
458
|
|
|
461
459
|
# This file is 2.7+ only so we only require it here once we've checked the Ruby version
|
|
462
460
|
require "datadog/profiling/ext/exec_monkey_patch"
|
|
@@ -465,7 +463,7 @@ module Datadog
|
|
|
465
463
|
end
|
|
466
464
|
|
|
467
465
|
private_class_method def self.enable_gvl_profiling?(settings, logger)
|
|
468
|
-
|
|
466
|
+
RubyVersion.is?(">= 3.2") && settings.profiling.advanced.gvl_enabled
|
|
469
467
|
end
|
|
470
468
|
end
|
|
471
469
|
end
|
|
@@ -28,7 +28,7 @@ module Datadog
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
if
|
|
31
|
+
if RubyVersion.is?("< 3")
|
|
32
32
|
# Monkey patches for Dir.singleton_class (Ruby 2 version). See DirMonkeyPatches above for more details.
|
|
33
33
|
module DirClassMonkeyPatches
|
|
34
34
|
# Steep: Workaround that defines args and block only for Ruby 2.x.
|
|
@@ -270,7 +270,7 @@ module Datadog
|
|
|
270
270
|
end
|
|
271
271
|
end
|
|
272
272
|
|
|
273
|
-
if
|
|
273
|
+
if RubyVersion.is?("< 3")
|
|
274
274
|
# Monkey patches for Dir (Ruby 2 version). See DirMonkeyPatches above for more details.
|
|
275
275
|
module DirInstanceMonkeyPatches
|
|
276
276
|
# Steep: Workaround that defines args and block only for Ruby 2.x.
|
|
@@ -300,7 +300,7 @@ module Datadog
|
|
|
300
300
|
end
|
|
301
301
|
end
|
|
302
302
|
|
|
303
|
-
|
|
303
|
+
if RubyVersion.is?(">= 2.6.0") # This method is Ruby 2.6+
|
|
304
304
|
# See note on methods that yield above.
|
|
305
305
|
def each_child(*args, &block)
|
|
306
306
|
if block
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
# Compares the running Ruby against version requirements.
|
|
5
|
+
#
|
|
6
|
+
# Lexical comparisons against `RUBY_VERSION` are subtly wrong: `RUBY_VERSION < "3.2.3"` is also
|
|
7
|
+
# `true` on 3.2.10 and 3.2.11, since those sort *before* "3.2.3" as strings.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# RubyVersion.is?(">= 3.2", "< 3.2.3") # => true on Ruby 3.2.0, 3.2.1, 3.2.2 (NOT 3.2.3+, NOT 3.2.10+)
|
|
11
|
+
module RubyVersion
|
|
12
|
+
extend RubyVersion # steep currently needs this (instead of extend self or def self.is?) for the inline rbs to work
|
|
13
|
+
|
|
14
|
+
CURRENT_RUBY_VERSION = Gem::Version.new(RUBY_VERSION) #: ::Gem::Version
|
|
15
|
+
private_constant :CURRENT_RUBY_VERSION
|
|
16
|
+
|
|
17
|
+
# Returns `true` when the running Ruby satisfies ALL of the given requirements. Each uses the
|
|
18
|
+
# same syntax as a gem dependency (e.g. `">= 3.1"`, `">= 3.2"`, `"< 3.2.3"`).
|
|
19
|
+
#
|
|
20
|
+
# @rbs (*String requirements, ?ruby_version: ::Gem::Version) -> bool
|
|
21
|
+
def is?(*requirements, ruby_version: CURRENT_RUBY_VERSION)
|
|
22
|
+
Gem::Requirement.new(*requirements).satisfied_by?(ruby_version)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|