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
|
@@ -46,6 +46,38 @@ module Datadog
|
|
|
46
46
|
Core::Diagnostics::Health::Metrics.new(telemetry: telemetry, logger: logger, **options)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
# Decides whether to build the SymbolDatabase component, for the
|
|
50
|
+
# tri-state symbol_database.enabled setting: true/false are explicit
|
|
51
|
+
# overrides; nil (the default) mirrors Dynamic Instrumentation by
|
|
52
|
+
# building whenever DI's component was built. dynamic_instrumentation
|
|
53
|
+
# is DI's build result — nil only when DI is explicitly disabled or the
|
|
54
|
+
# runtime can't support it (Rails development mode, missing C
|
|
55
|
+
# extension, remote config off, non-MRI). Under the always-build model
|
|
56
|
+
# it is non-nil even when the DI setting defaults to off, so a built
|
|
57
|
+
# SymbolDatabase component is inert until an upload is actually
|
|
58
|
+
# permitted: the component's own gate (see Component#upload_allowed?)
|
|
59
|
+
# only extracts and uploads when DI is truly active or the customer
|
|
60
|
+
# opted in explicitly. This mirrors DI advertising/building a component
|
|
61
|
+
# by default while installing no probes until it is enabled.
|
|
62
|
+
# force_upload (internal/testing) always builds, since it uploads
|
|
63
|
+
# regardless of DI or the enabled setting.
|
|
64
|
+
# @param settings [Configuration::Settings]
|
|
65
|
+
# @param dynamic_instrumentation [DI::Component, nil]
|
|
66
|
+
# @return [Boolean]
|
|
67
|
+
def enable_symbol_database?(settings, dynamic_instrumentation)
|
|
68
|
+
# The symbol_database settings group is only registered on the full
|
|
69
|
+
# library load path; a partial load (e.g. require 'datadog/di') leaves
|
|
70
|
+
# it absent, in which case the feature cannot be enabled.
|
|
71
|
+
return false unless settings.respond_to?(:symbol_database)
|
|
72
|
+
|
|
73
|
+
# force_upload uploads unconditionally, so the component must be built
|
|
74
|
+
# even when the setting is nil and DI's component was not built.
|
|
75
|
+
return true if settings.symbol_database.internal.force_upload
|
|
76
|
+
|
|
77
|
+
# nil (the default) follows whether DI's component was built.
|
|
78
|
+
Datadog::SymbolDatabase.resolve_enabled(settings.symbol_database.enabled, !dynamic_instrumentation.nil?)
|
|
79
|
+
end
|
|
80
|
+
|
|
49
81
|
def build_logger(settings)
|
|
50
82
|
logger = settings.logger.instance || Core::Logger.new($stderr)
|
|
51
83
|
logger.level = settings.diagnostics.debug ? ::Logger::DEBUG : settings.logger.level
|
|
@@ -174,10 +206,31 @@ module Datadog
|
|
|
174
206
|
@ai_guard = Datadog::AIGuard::Component.build(settings, logger: @logger, telemetry: telemetry)
|
|
175
207
|
@open_feature = OpenFeature::Component.build(settings, agent_settings, logger: @logger, telemetry: telemetry)
|
|
176
208
|
@dynamic_instrumentation = Datadog::DI::Component.build(settings, agent_settings, @logger, telemetry: telemetry)
|
|
177
|
-
|
|
209
|
+
# Only build symbol database when enabled, so a disabled component is
|
|
210
|
+
# never constructed.
|
|
211
|
+
@symbol_database =
|
|
212
|
+
if self.class.enable_symbol_database?(settings, @dynamic_instrumentation)
|
|
213
|
+
# di_active is a live predicate, not a snapshot: DI may start after
|
|
214
|
+
# this component is built (implicit enablement via remote config).
|
|
215
|
+
# Symbol Database holds only this opaque proc, never a DI reference,
|
|
216
|
+
# so the two modules stay independent — the orchestration layer
|
|
217
|
+
# owns the cross-feature knowledge.
|
|
218
|
+
Datadog::SymbolDatabase::Component.build(
|
|
219
|
+
settings, agent_settings, @logger,
|
|
220
|
+
telemetry: telemetry,
|
|
221
|
+
di_active: -> { dynamic_instrumentation&.started? || false },
|
|
222
|
+
)
|
|
223
|
+
end
|
|
178
224
|
@error_tracking = Datadog::ErrorTracking::Component.build(settings, @tracer, @logger)
|
|
179
225
|
@data_streams = self.class.build_data_streams(settings, agent_settings, @logger, @agent_info)
|
|
180
|
-
|
|
226
|
+
# Reflects "the customer configured DI to be on" — true iff the
|
|
227
|
+
# component was built AND the env-var-driven enabled flag is set.
|
|
228
|
+
# This is the post-initialize / pre-startup value visible to tests
|
|
229
|
+
# that don't call startup!. Production overwrites this in startup!
|
|
230
|
+
# below with `dynamic_instrumentation&.started?`, which also accounts
|
|
231
|
+
# for RC-driven enablement carried over via ComponentsState.
|
|
232
|
+
@environment_logger_extra[:dynamic_instrumentation_enabled] =
|
|
233
|
+
!!(@dynamic_instrumentation && settings.dynamic_instrumentation.enabled)
|
|
181
234
|
|
|
182
235
|
# Configure non-privileged components.
|
|
183
236
|
Datadog::Tracing::Contrib::Component.configure(settings)
|
|
@@ -228,11 +281,25 @@ module Datadog
|
|
|
228
281
|
remote.start
|
|
229
282
|
end
|
|
230
283
|
|
|
284
|
+
# Start DI component if enabled by env var or if it was implicitly enabled
|
|
285
|
+
# (via RC) in the previous Components instance. dynamic_instrumentation is
|
|
286
|
+
# non-nil only when Component.build's preconditions passed (Ruby 2.6+ MRI,
|
|
287
|
+
# etc.), which is exactly the condition under which di/base.rb is loaded
|
|
288
|
+
# and DI.activate_tracking is defined.
|
|
289
|
+
if dynamic_instrumentation
|
|
290
|
+
if settings.dynamic_instrumentation.enabled || old_state&.di_implicitly_enabled?
|
|
291
|
+
DI.activate_tracking
|
|
292
|
+
dynamic_instrumentation.start!
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
231
296
|
# This should stay here, not in initialize. During reconfiguration, the order of the calls is:
|
|
232
297
|
# initialize new components, shutdown old components, startup new components.
|
|
233
298
|
# Because this is a singleton, if we call it in initialize, it will be shutdown right away.
|
|
234
299
|
Core::ProcessDiscovery.publish(settings)
|
|
235
300
|
|
|
301
|
+
@environment_logger_extra[:dynamic_instrumentation_enabled] = dynamic_instrumentation&.started? || false
|
|
302
|
+
|
|
236
303
|
Core::Diagnostics::EnvironmentLogger.collect_and_log!(@environment_logger_extra)
|
|
237
304
|
end
|
|
238
305
|
|
|
@@ -309,9 +376,22 @@ module Datadog
|
|
|
309
376
|
|
|
310
377
|
# Returns the current state of various components.
|
|
311
378
|
def state
|
|
379
|
+
# di_implicitly_enabled distinguishes RC-driven start from explicit
|
|
380
|
+
# start (env var or programmatic) so that an explicit
|
|
381
|
+
# `enabled = false` on reconfiguration can take effect.
|
|
382
|
+
#
|
|
383
|
+
# using_default? — not `!enabled` — because Datadog.configure has
|
|
384
|
+
# already mutated the singleton settings by the time #state runs,
|
|
385
|
+
# so `!enabled` would treat a customer's explicit
|
|
386
|
+
# `enabled = false` as "implicitly enabled" and restart DI.
|
|
387
|
+
# using_default? asks "did the customer ever touch this setting",
|
|
388
|
+
# which is the right question for RC carry-over.
|
|
389
|
+
di_implicit = dynamic_instrumentation&.started? &&
|
|
390
|
+
@settings.dynamic_instrumentation.using_default?(:enabled)
|
|
312
391
|
ComponentsState.new(
|
|
313
392
|
telemetry_enabled: telemetry.enabled,
|
|
314
393
|
remote_started: remote&.started?,
|
|
394
|
+
di_implicitly_enabled: di_implicit || false,
|
|
315
395
|
)
|
|
316
396
|
end
|
|
317
397
|
end
|
|
@@ -5,9 +5,10 @@ module Datadog
|
|
|
5
5
|
module Configuration
|
|
6
6
|
# Stores the state of component tree when replacing the tree.
|
|
7
7
|
class ComponentsState
|
|
8
|
-
def initialize(telemetry_enabled:, remote_started:)
|
|
8
|
+
def initialize(telemetry_enabled:, remote_started:, di_implicitly_enabled: false)
|
|
9
9
|
@telemetry_enabled = !!telemetry_enabled
|
|
10
10
|
@remote_started = !!remote_started
|
|
11
|
+
@di_implicitly_enabled = !!di_implicitly_enabled
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def telemetry_enabled?
|
|
@@ -17,6 +18,10 @@ module Datadog
|
|
|
17
18
|
def remote_started?
|
|
18
19
|
@remote_started
|
|
19
20
|
end
|
|
21
|
+
|
|
22
|
+
def di_implicitly_enabled?
|
|
23
|
+
@di_implicitly_enabled
|
|
24
|
+
end
|
|
20
25
|
end
|
|
21
26
|
end
|
|
22
27
|
end
|
|
@@ -14,7 +14,6 @@ require_relative '../../profiling/ext'
|
|
|
14
14
|
|
|
15
15
|
require_relative '../../tracing/configuration/settings'
|
|
16
16
|
require_relative '../../opentelemetry/configuration/settings'
|
|
17
|
-
require_relative '../../symbol_database/configuration'
|
|
18
17
|
|
|
19
18
|
module Datadog
|
|
20
19
|
module Core
|
|
@@ -825,14 +824,14 @@ module Datadog
|
|
|
825
824
|
# It must respect the interface of [Datadog::Core::Utils::Time#get_time] method.
|
|
826
825
|
#
|
|
827
826
|
# For [Timecop](https://rubygems.org/gems/timecop), for example,
|
|
828
|
-
# `->(unit = :float_second) { ::Process.clock_gettime_without_mock(::
|
|
827
|
+
# `->(unit = :float_second) { ::Process.clock_gettime_without_mock(Datadog::Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }`
|
|
829
828
|
# allows Datadog features to use the real monotonic time when time is frozen with
|
|
830
829
|
# `Timecop.mock_process_clock = true`.
|
|
831
830
|
#
|
|
832
|
-
# @default `->(unit = :float_second) { ::Process.clock_gettime(::
|
|
831
|
+
# @default `->(unit = :float_second) { ::Process.clock_gettime(Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }`
|
|
833
832
|
# @return [Proc<Numeric>]
|
|
834
833
|
option :get_time_provider do |o|
|
|
835
|
-
o.default_proc { |unit = :float_second| ::Process.clock_gettime(::
|
|
834
|
+
o.default_proc { |unit = :float_second| ::Process.clock_gettime(Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }
|
|
836
835
|
o.type :proc
|
|
837
836
|
|
|
838
837
|
o.after_set do |get_time_provider|
|
|
@@ -840,7 +839,7 @@ module Datadog
|
|
|
840
839
|
end
|
|
841
840
|
|
|
842
841
|
o.resetter do |_value|
|
|
843
|
-
->(unit = :float_second) { ::Process.clock_gettime(::
|
|
842
|
+
->(unit = :float_second) { ::Process.clock_gettime(Core::Utils::Time::MONOTONIC_CLOCK_ID, unit) }.tap do |default|
|
|
844
843
|
Core::Utils::Time.get_time_provider = default
|
|
845
844
|
end
|
|
846
845
|
end
|
|
@@ -1130,8 +1129,6 @@ module Datadog
|
|
|
1130
1129
|
extend Datadog::Tracing::Configuration::Settings
|
|
1131
1130
|
|
|
1132
1131
|
extend Datadog::OpenTelemetry::Configuration::Settings
|
|
1133
|
-
|
|
1134
|
-
extend Datadog::SymbolDatabase::Configuration::Settings
|
|
1135
1132
|
end
|
|
1136
1133
|
# standard:enable Metrics/BlockLength
|
|
1137
1134
|
end
|
|
@@ -19,6 +19,7 @@ module Datadog
|
|
|
19
19
|
"DD_API_SECURITY_DOWNSTREAM_BODY_ANALYSIS_SAMPLE_RATE",
|
|
20
20
|
"DD_API_SECURITY_ENABLED",
|
|
21
21
|
"DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED",
|
|
22
|
+
"DD_API_SECURITY_MAX_DOWNSTREAM_BODY_BYTES",
|
|
22
23
|
"DD_API_SECURITY_MAX_DOWNSTREAM_REQUEST_BODY_ANALYSIS",
|
|
23
24
|
"DD_API_SECURITY_REQUEST_SAMPLE_RATE",
|
|
24
25
|
"DD_API_SECURITY_SAMPLE_DELAY",
|
|
@@ -49,6 +50,7 @@ module Datadog
|
|
|
49
50
|
"DD_DBM_PROPAGATION_MODE",
|
|
50
51
|
"DD_DISABLE_DATADOG_RAILS",
|
|
51
52
|
"DD_DYNAMIC_INSTRUMENTATION_ENABLED",
|
|
53
|
+
"DD_DYNAMIC_INSTRUMENTATION_MAX_TIME_TO_SERIALIZE",
|
|
52
54
|
"DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE",
|
|
53
55
|
"DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS",
|
|
54
56
|
"DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES",
|
|
@@ -59,6 +61,7 @@ module Datadog
|
|
|
59
61
|
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED",
|
|
60
62
|
"DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED",
|
|
61
63
|
"DD_EXTERNAL_ENV",
|
|
64
|
+
"DD_FLAGGING_EVALUATION_COUNTS_ENABLED",
|
|
62
65
|
"DD_GIT_COMMIT_SHA",
|
|
63
66
|
"DD_GIT_REPOSITORY_URL",
|
|
64
67
|
"DD_HEALTH_METRICS_ENABLED",
|
|
@@ -32,7 +32,9 @@ module Datadog
|
|
|
32
32
|
@knuth_factor = knuth_factor
|
|
33
33
|
|
|
34
34
|
rate = rate.to_f
|
|
35
|
-
|
|
35
|
+
# Not using `between?` here: it raises on NaN instead of falling back to 1.0 below.
|
|
36
|
+
# No current caller passes NaN, but this guards against it defensively for future ones.
|
|
37
|
+
unless rate >= 0.0 && rate <= 1.0 # rubocop:disable Style/ComparableBetween
|
|
36
38
|
Datadog.logger.warn("Sample rate #{rate} is not between 0.0 and 1.0, falling back to 1.0")
|
|
37
39
|
rate = 1.0
|
|
38
40
|
end
|
|
@@ -157,7 +157,7 @@ module Datadog
|
|
|
157
157
|
begin
|
|
158
158
|
if send_stats? && !start.nil?
|
|
159
159
|
finished = Utils::Time.get_time
|
|
160
|
-
distribution(stat, (
|
|
160
|
+
distribution(stat, (finished - start) * 1000, options)
|
|
161
161
|
end
|
|
162
162
|
rescue => e
|
|
163
163
|
# TODO: Likely to be redundant, since `distribution` handles its own errors.
|
|
@@ -4,6 +4,7 @@ require_relative '../../utils/base64_codec'
|
|
|
4
4
|
require_relative '../../../appsec/remote'
|
|
5
5
|
require_relative '../../../tracing/remote'
|
|
6
6
|
require_relative '../../../di/remote'
|
|
7
|
+
require_relative '../../../symbol_database'
|
|
7
8
|
require_relative '../../../symbol_database/remote'
|
|
8
9
|
require_relative '../../../open_feature/remote'
|
|
9
10
|
|
|
@@ -35,13 +36,43 @@ module Datadog
|
|
|
35
36
|
register_receivers(Datadog::AppSec::Remote.receivers(@telemetry))
|
|
36
37
|
end
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
# Tracing must register before DI: on a combined RC dispatch,
|
|
40
|
+
# the APM_TRACING handler must run first to call
|
|
41
|
+
# Datadog::DI::Remote.handle_rc_enablement and start the
|
|
42
|
+
# component before the DI receiver processes LIVE_DEBUGGING
|
|
43
|
+
# changes against `component.started?`. Reversing the order
|
|
44
|
+
# silently drops the probe — the remote client only
|
|
45
|
+
# redispatches on content hash changes.
|
|
46
|
+
register_capabilities(Datadog::Tracing::Remote.capabilities)
|
|
47
|
+
register_products(Datadog::Tracing::Remote.products)
|
|
48
|
+
register_receivers(Datadog::Tracing::Remote.receivers(@telemetry))
|
|
49
|
+
|
|
50
|
+
# Skip DI registration entirely when DI is explicitly disabled
|
|
51
|
+
# (DD_DYNAMIC_INSTRUMENTATION_ENABLED=false) or when the runtime
|
|
52
|
+
# cannot run DI (JRuby, Ruby 2.5): in either case no component will
|
|
53
|
+
# run, so advertising bit 38 or the LIVE_DEBUGGING product would
|
|
54
|
+
# invite probe configs and an enable signal the tracer must refuse.
|
|
55
|
+
# When the env var is unset (default) on a supported runtime, DI is
|
|
56
|
+
# registered so RC can enable it.
|
|
57
|
+
if settings.respond_to?(:dynamic_instrumentation) &&
|
|
58
|
+
!Datadog::DI::Remote.explicitly_disabled?(settings) &&
|
|
59
|
+
Datadog::DI.supported_runtime?
|
|
39
60
|
register_capabilities(Datadog::DI::Remote.capabilities)
|
|
40
61
|
register_products(Datadog::DI::Remote.products)
|
|
41
62
|
register_receivers(Datadog::DI::Remote.receivers(@telemetry))
|
|
63
|
+
end
|
|
42
64
|
|
|
43
|
-
|
|
44
|
-
|
|
65
|
+
# Skip symbol database registration on runtimes that cannot run it
|
|
66
|
+
# (JRuby, Ruby < 2.7): DI supports Ruby 2.6 but Symbol Database does
|
|
67
|
+
# not, so advertising LIVE_DEBUGGING_SYMBOL_DB there would invite
|
|
68
|
+
# symbol-upload configs the component can never serve.
|
|
69
|
+
if settings.respond_to?(:symbol_database) && Datadog::SymbolDatabase.supported_runtime?
|
|
70
|
+
# Symbol database follows DI: when unset it advertises whenever DI
|
|
71
|
+
# advertises (mirror the DI branch above, including the unset/default
|
|
72
|
+
# case that RC may enable). An explicit symbol_database.enabled wins.
|
|
73
|
+
di_enabled = settings.respond_to?(:dynamic_instrumentation) &&
|
|
74
|
+
!Datadog::DI::Remote.explicitly_disabled?(settings)
|
|
75
|
+
if Datadog::SymbolDatabase.resolve_enabled(settings.symbol_database.enabled, di_enabled)
|
|
45
76
|
register_capabilities(Datadog::SymbolDatabase::Remote.capabilities)
|
|
46
77
|
register_products(Datadog::SymbolDatabase::Remote.products)
|
|
47
78
|
register_receivers(Datadog::SymbolDatabase::Remote.receivers(@telemetry))
|
|
@@ -53,10 +84,6 @@ module Datadog
|
|
|
53
84
|
register_products(Datadog::OpenFeature::Remote.products)
|
|
54
85
|
register_receivers(Datadog::OpenFeature::Remote.receivers(@telemetry))
|
|
55
86
|
end
|
|
56
|
-
|
|
57
|
-
register_capabilities(Datadog::Tracing::Remote.capabilities)
|
|
58
|
-
register_products(Datadog::Tracing::Remote.products)
|
|
59
|
-
register_receivers(Datadog::Tracing::Remote.receivers(@telemetry))
|
|
60
87
|
end
|
|
61
88
|
|
|
62
89
|
def register_capabilities(capabilities)
|
|
@@ -57,7 +57,14 @@ module Datadog
|
|
|
57
57
|
enabled: !!components.profiler,
|
|
58
58
|
},
|
|
59
59
|
dynamic_instrumentation: {
|
|
60
|
-
|
|
60
|
+
# With always-build, the DI component is constructed by
|
|
61
|
+
# default (when the runtime supports DI, RC is available, and
|
|
62
|
+
# DI is not explicitly disabled) regardless of whether the
|
|
63
|
+
# customer turned DI on. Its presence therefore no longer
|
|
64
|
+
# indicates the enabled state. started? reflects whether DI was
|
|
65
|
+
# actually enabled by env var or by implicit enablement via
|
|
66
|
+
# remote config.
|
|
67
|
+
enabled: components.dynamic_instrumentation&.started? || false,
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -108,7 +108,7 @@ module Datadog
|
|
|
108
108
|
|
|
109
109
|
# Resolve fallback and merge headers
|
|
110
110
|
fallback = api_options.delete(:fallback)
|
|
111
|
-
api_options[:headers] = @default_headers.merge(
|
|
111
|
+
api_options[:headers] = @default_headers.merge(api_options[:headers] || {})
|
|
112
112
|
|
|
113
113
|
# Add API::Instance with all settings
|
|
114
114
|
instances[key] = API::Instance.new(
|
|
@@ -6,7 +6,7 @@ module Datadog
|
|
|
6
6
|
# Monkey patches `Kernel#fork` and similar functions, adding an `at_fork` callback mechanism which
|
|
7
7
|
# is used to restart observability after the VM forks (e.g. in multiprocess Ruby apps).
|
|
8
8
|
module AtForkMonkeyPatch
|
|
9
|
-
AT_FORK_CHILD_BLOCKS = [] # rubocop:disable Style/MutableConstant Used to store blocks to run, mutable by design.
|
|
9
|
+
AT_FORK_CHILD_BLOCKS = [] # rubocop:disable Style/MutableConstant -- Used to store blocks to run, mutable by design.
|
|
10
10
|
private_constant :AT_FORK_CHILD_BLOCKS
|
|
11
11
|
|
|
12
12
|
def self.supported?
|
|
@@ -8,11 +8,15 @@ module Datadog
|
|
|
8
8
|
module_function
|
|
9
9
|
|
|
10
10
|
# Current monotonic time
|
|
11
|
+
# On macOS, CLOCK_MONOTONIC only has microsecond precision,
|
|
12
|
+
# so we use CLOCK_MONOTONIC_RAW which has nanosecond precision instead.
|
|
11
13
|
#
|
|
12
14
|
# @param unit [Symbol] unit for the resulting value, same as ::Process#clock_gettime, defaults to :float_second
|
|
13
15
|
# @return [Float|Integer] timestamp in the requested unit, since some unspecified starting point
|
|
16
|
+
MONOTONIC_CLOCK_ID = RUBY_PLATFORM.include?("darwin") ? Process::CLOCK_MONOTONIC_RAW : Process::CLOCK_MONOTONIC
|
|
17
|
+
|
|
14
18
|
def get_time(unit = :float_second)
|
|
15
|
-
Process.clock_gettime(
|
|
19
|
+
Process.clock_gettime(MONOTONIC_CLOCK_ID, unit)
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
# Current wall time.
|
data/lib/datadog/di/base.rb
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
# are loaded, and also none of the rest of datadog library which also
|
|
9
9
|
# has contrib code in other products.
|
|
10
10
|
|
|
11
|
+
require_relative 'fatal_exceptions'
|
|
11
12
|
require_relative 'code_tracker'
|
|
12
13
|
|
|
13
14
|
# Needed since this file can be loaded without core
|
|
@@ -53,7 +54,8 @@ module Datadog
|
|
|
53
54
|
# Activate code tracking by default because line trace points will not work
|
|
54
55
|
# without it.
|
|
55
56
|
Datadog::DI.activate_tracking!
|
|
56
|
-
rescue => exc
|
|
57
|
+
rescue Exception => exc # standard:disable Lint/RescueException
|
|
58
|
+
Datadog::DI.reraise_if_fatal(exc)
|
|
57
59
|
if defined?(Datadog.logger)
|
|
58
60
|
Datadog.logger.warn { "di: Failed to activate code tracking for DI: #{exc.class}: #{exc.message}" }
|
|
59
61
|
else
|
data/lib/datadog/di/boot.rb
CHANGED
|
@@ -6,6 +6,9 @@ require_relative 'error'
|
|
|
6
6
|
require_relative 'code_tracker'
|
|
7
7
|
require_relative 'component'
|
|
8
8
|
require_relative 'context'
|
|
9
|
+
require_relative 'capture_limits'
|
|
10
|
+
require_relative 'capture_expression'
|
|
11
|
+
require_relative 'capture_expression_evaluator'
|
|
9
12
|
require_relative 'instrumenter'
|
|
10
13
|
require_relative 'probe'
|
|
11
14
|
require_relative 'probe_builder'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "capture_limits"
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module DI
|
|
7
|
+
class CaptureExpression
|
|
8
|
+
def initialize(name:, expr:, limits: nil)
|
|
9
|
+
@name = name
|
|
10
|
+
@expr = expr
|
|
11
|
+
@limits = limits
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :name
|
|
15
|
+
|
|
16
|
+
attr_reader :expr
|
|
17
|
+
|
|
18
|
+
attr_reader :limits
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "capture_expression"
|
|
4
|
+
require_relative "capture_limits"
|
|
5
|
+
require_relative "fatal_exceptions"
|
|
6
|
+
|
|
7
|
+
module Datadog
|
|
8
|
+
module DI
|
|
9
|
+
class CaptureExpressionEvaluator
|
|
10
|
+
TELEMETRY_NAMESPACE = "dynamic_instrumentation"
|
|
11
|
+
|
|
12
|
+
def initialize(settings:, serializer:, logger:, telemetry: nil)
|
|
13
|
+
@settings = settings
|
|
14
|
+
@serializer = serializer
|
|
15
|
+
@logger = logger
|
|
16
|
+
@telemetry = telemetry
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
attr_reader :settings
|
|
20
|
+
|
|
21
|
+
attr_reader :serializer
|
|
22
|
+
|
|
23
|
+
attr_reader :logger
|
|
24
|
+
|
|
25
|
+
attr_reader :telemetry
|
|
26
|
+
|
|
27
|
+
def evaluate(probe, context)
|
|
28
|
+
budget_ns = settings.dynamic_instrumentation.max_time_to_serialize_ms * 1_000_000
|
|
29
|
+
deadline_ns = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :nanosecond) + budget_ns
|
|
30
|
+
|
|
31
|
+
output = {}
|
|
32
|
+
evaluation_errors = []
|
|
33
|
+
|
|
34
|
+
probe.capture_expressions.each do |capture_expression|
|
|
35
|
+
name = capture_expression.name
|
|
36
|
+
|
|
37
|
+
if ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :nanosecond) >= deadline_ns
|
|
38
|
+
output[name] = {notCapturedReason: "timeout"}
|
|
39
|
+
telemetry&.inc(TELEMETRY_NAMESPACE, "capture_expressions_skipped_by_timeout", 1)
|
|
40
|
+
next
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
begin
|
|
44
|
+
value = capture_expression.expr.evaluate(context)
|
|
45
|
+
limits = CaptureLimits.resolve(
|
|
46
|
+
expr_limits: capture_expression.limits,
|
|
47
|
+
probe: probe,
|
|
48
|
+
settings: settings,
|
|
49
|
+
)
|
|
50
|
+
output[name] = serializer.serialize_value(
|
|
51
|
+
value, name: name,
|
|
52
|
+
depth: limits[:depth],
|
|
53
|
+
attribute_count: limits[:attribute_count],
|
|
54
|
+
length: limits[:length],
|
|
55
|
+
collection_size: limits[:collection_size],
|
|
56
|
+
)
|
|
57
|
+
rescue Exception => exc # standard:disable Lint/RescueException
|
|
58
|
+
Datadog::DI.reraise_if_fatal(exc)
|
|
59
|
+
evaluation_errors << {expr: name, message: "#{exc.class}: #{exc.message}"}
|
|
60
|
+
logger.debug do
|
|
61
|
+
"di: probe #{probe.id}: capture expression #{name}: evaluation failed: #{exc.class}: #{exc.message}"
|
|
62
|
+
end
|
|
63
|
+
telemetry&.report(exc, description: "DI capture-expression evaluation failed")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
[output, evaluation_errors]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module DI
|
|
5
|
+
class CaptureLimits
|
|
6
|
+
def initialize(max_reference_depth: nil, max_collection_size: nil,
|
|
7
|
+
max_length: nil, max_field_count: nil)
|
|
8
|
+
@max_reference_depth = max_reference_depth
|
|
9
|
+
@max_collection_size = max_collection_size
|
|
10
|
+
@max_length = max_length
|
|
11
|
+
@max_field_count = max_field_count
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :max_reference_depth
|
|
15
|
+
|
|
16
|
+
attr_reader :max_collection_size
|
|
17
|
+
|
|
18
|
+
attr_reader :max_length
|
|
19
|
+
|
|
20
|
+
attr_reader :max_field_count
|
|
21
|
+
|
|
22
|
+
def self.resolve(expr_limits:, probe:, settings:)
|
|
23
|
+
di = settings.dynamic_instrumentation
|
|
24
|
+
{
|
|
25
|
+
depth: expr_limits&.max_reference_depth ||
|
|
26
|
+
probe.max_capture_depth ||
|
|
27
|
+
di.max_capture_depth,
|
|
28
|
+
collection_size: expr_limits&.max_collection_size ||
|
|
29
|
+
probe.max_capture_collection_size ||
|
|
30
|
+
di.max_capture_collection_size,
|
|
31
|
+
length: expr_limits&.max_length ||
|
|
32
|
+
probe.max_capture_string_length ||
|
|
33
|
+
di.max_capture_string_length,
|
|
34
|
+
attribute_count: expr_limits&.max_field_count ||
|
|
35
|
+
probe.max_capture_attribute_count ||
|
|
36
|
+
di.max_capture_attribute_count,
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# rubocop:disable Lint/AssignmentInCondition
|
|
4
4
|
|
|
5
5
|
require_relative 'error'
|
|
6
|
+
require_relative 'fatal_exceptions'
|
|
6
7
|
|
|
7
8
|
module Datadog
|
|
8
9
|
module DI
|
|
@@ -120,7 +121,8 @@ module Datadog
|
|
|
120
121
|
end
|
|
121
122
|
end
|
|
122
123
|
nil
|
|
123
|
-
rescue => exc
|
|
124
|
+
rescue Exception => exc # standard:disable Lint/RescueException
|
|
125
|
+
Datadog::DI.reraise_if_fatal(exc)
|
|
124
126
|
# Backfill is best-effort — if it fails, line probes on
|
|
125
127
|
# pre-loaded code won't work but everything else is unaffected.
|
|
126
128
|
if component = DI.current_component
|
|
@@ -191,7 +193,8 @@ module Datadog
|
|
|
191
193
|
# Since this method normally is called from customer applications,
|
|
192
194
|
# rescue any exceptions that might not be handled to not break said
|
|
193
195
|
# customer applications.
|
|
194
|
-
rescue => exc
|
|
196
|
+
rescue Exception => exc # standard:disable Lint/RescueException
|
|
197
|
+
Datadog::DI.reraise_if_fatal(exc)
|
|
195
198
|
# Code tracker may be loaded without the rest of DI,
|
|
196
199
|
# in which case DI.component will not yet be defined,
|
|
197
200
|
# but we will have DI.current_component (set to nil).
|