datadog 2.45.0 → 2.46.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 +25 -2
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +4 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +2 -0
- data/ext/datadog_profiling_native_extension/profiling.c +6 -0
- data/ext/libdatadog_api/di.c +30 -5
- data/ext/libdatadog_api/extconf.rb +2 -0
- data/ext/libdatadog_api/otel_thread_context.c +20 -14
- data/ext/libdatadog_api/process_discovery.c +15 -1
- data/ext/libdatadog_api/trace_exporter.c +105 -25
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/core/configuration/components.rb +29 -8
- data/lib/datadog/core/configuration/supported_configurations.rb +5 -2
- data/lib/datadog/core/process_discovery.rb +5 -1
- data/lib/datadog/core/remote/client/capabilities.rb +34 -29
- data/lib/datadog/core/remote/component.rb +28 -12
- data/lib/datadog/core/remote/dispatcher.rb +13 -3
- data/lib/datadog/di/boot.rb +1 -0
- data/lib/datadog/di/capture_expression_evaluator/result.rb +30 -0
- data/lib/datadog/di/capture_expression_evaluator.rb +39 -9
- data/lib/datadog/di/component.rb +38 -3
- data/lib/datadog/di/configuration.rb +53 -2
- data/lib/datadog/di/context.rb +7 -2
- data/lib/datadog/di/contrib/active_record.rb +8 -2
- data/lib/datadog/di/evaluation_failure.rb +28 -0
- data/lib/datadog/di/guardrails_telemetry/reason.rb +87 -0
- data/lib/datadog/di/guardrails_telemetry.rb +203 -0
- data/lib/datadog/di/instrumenter.rb +65 -15
- data/lib/datadog/di/logger.rb +30 -8
- data/lib/datadog/di/probe_manager.rb +48 -5
- data/lib/datadog/di/probe_notification_builder.rb +28 -26
- data/lib/datadog/di/probe_notifier_worker.rb +56 -6
- data/lib/datadog/di/serializer.rb +130 -54
- data/lib/datadog/di/snapshot_event.rb +38 -0
- data/lib/datadog/di/transport/http.rb +13 -3
- data/lib/datadog/di/transport/input.rb +32 -7
- data/lib/datadog/open_feature/activation.rb +268 -0
- data/lib/datadog/open_feature/component.rb +31 -15
- data/lib/datadog/open_feature/provider.rb +282 -5
- data/lib/datadog/open_feature.rb +31 -1
- data/lib/datadog/tracing/component.rb +14 -0
- data/lib/datadog/tracing/configuration/settings.rb +3 -1
- data/lib/datadog/tracing/otel_thread_context.rb +3 -5
- data/lib/datadog/tracing/transport/http/telemetry.rb +94 -0
- data/lib/datadog/tracing/transport/http/traces.rb +2 -0
- data/lib/datadog/tracing/transport/native/telemetry.rb +17 -0
- data/lib/datadog/tracing/transport/native.rb +16 -1
- data/lib/datadog/tracing/transport/telemetry.rb +62 -0
- data/lib/datadog/tracing/transport/traces.rb +82 -15
- data/lib/datadog/version.rb +1 -1
- metadata +16 -7
|
@@ -23,7 +23,11 @@ module Datadog
|
|
|
23
23
|
metadata = get_metadata(settings)
|
|
24
24
|
|
|
25
25
|
shutdown!
|
|
26
|
-
@file_descriptor = _native_store_tracer_metadata(
|
|
26
|
+
@file_descriptor = _native_store_tracer_metadata(
|
|
27
|
+
Datadog.logger,
|
|
28
|
+
settings.tracing.otel_thread_context_enabled,
|
|
29
|
+
**metadata
|
|
30
|
+
)
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def shutdown!
|
|
@@ -6,7 +6,6 @@ require_relative "../../../tracing/remote"
|
|
|
6
6
|
require_relative "../../../di/remote"
|
|
7
7
|
require_relative "../../../symbol_database"
|
|
8
8
|
require_relative "../../../symbol_database/remote"
|
|
9
|
-
require_relative "../../../open_feature/remote"
|
|
10
9
|
|
|
11
10
|
module Datadog
|
|
12
11
|
module Core
|
|
@@ -14,40 +13,57 @@ module Datadog
|
|
|
14
13
|
class Client
|
|
15
14
|
# Capabilities
|
|
16
15
|
class Capabilities
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def initialize(settings, telemetry, open_feature_component_provider: nil)
|
|
20
|
-
open_feature_component_provider ||= -> {}
|
|
16
|
+
def initialize(settings, telemetry:)
|
|
21
17
|
@capabilities = []
|
|
22
18
|
@products = []
|
|
23
|
-
@products_mutex = Mutex.new
|
|
24
19
|
@receivers = []
|
|
25
20
|
@telemetry = telemetry
|
|
21
|
+
@mutex = Mutex.new
|
|
22
|
+
|
|
23
|
+
register(settings)
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
@base64_capabilities = capabilities_to_base64(@capabilities)
|
|
26
|
+
end
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
def capabilities
|
|
29
|
+
@mutex.synchronize { @capabilities.dup }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def products
|
|
33
|
-
@
|
|
33
|
+
@mutex.synchronize { @products.dup }
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def
|
|
37
|
-
@
|
|
38
|
-
|
|
36
|
+
def receivers
|
|
37
|
+
@mutex.synchronize { @receivers.dup }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def base64_capabilities
|
|
41
|
+
@mutex.synchronize { @base64_capabilities }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def register_runtime(capabilities:, products:, receivers:)
|
|
45
|
+
@mutex.synchronize do
|
|
46
|
+
register_capabilities(capabilities)
|
|
47
|
+
register_products(products)
|
|
48
|
+
register_receivers(receivers)
|
|
49
|
+
@base64_capabilities = capabilities_to_base64(@capabilities)
|
|
39
50
|
end
|
|
40
51
|
nil
|
|
41
52
|
end
|
|
42
53
|
|
|
54
|
+
def add_products(*products)
|
|
55
|
+
@mutex.synchronize { register_products(products) }
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
|
|
43
59
|
def remove_products(*products)
|
|
44
|
-
@
|
|
60
|
+
@mutex.synchronize { products.each { |product| @products.delete(product) } }
|
|
45
61
|
nil
|
|
46
62
|
end
|
|
47
63
|
|
|
48
64
|
private
|
|
49
65
|
|
|
50
|
-
def register(settings
|
|
66
|
+
def register(settings)
|
|
51
67
|
if settings.respond_to?(:appsec) && settings.appsec.enabled
|
|
52
68
|
register_capabilities(Datadog::AppSec::Remote.capabilities)
|
|
53
69
|
register_products(Datadog::AppSec::Remote.products)
|
|
@@ -95,32 +111,21 @@ module Datadog
|
|
|
95
111
|
end
|
|
96
112
|
end
|
|
97
113
|
end
|
|
98
|
-
|
|
99
|
-
if settings.respond_to?(:open_feature) && settings.open_feature.enabled
|
|
100
|
-
register_capabilities(Datadog::OpenFeature::Remote.capabilities)
|
|
101
|
-
register_products(Datadog::OpenFeature::Remote.products)
|
|
102
|
-
register_receivers(
|
|
103
|
-
Datadog::OpenFeature::Remote.receivers(
|
|
104
|
-
@telemetry,
|
|
105
|
-
component_provider: open_feature_component_provider,
|
|
106
|
-
),
|
|
107
|
-
)
|
|
108
|
-
end
|
|
109
114
|
end
|
|
110
115
|
|
|
111
116
|
def register_capabilities(capabilities)
|
|
112
|
-
@capabilities.
|
|
117
|
+
capabilities.each { |capability| @capabilities << capability unless @capabilities.include?(capability) }
|
|
113
118
|
end
|
|
114
119
|
|
|
115
120
|
def register_receivers(receivers)
|
|
116
|
-
@receivers.
|
|
121
|
+
receivers.each { |receiver| @receivers << receiver unless @receivers.include?(receiver) }
|
|
117
122
|
end
|
|
118
123
|
|
|
119
124
|
def register_products(products)
|
|
120
|
-
@products.
|
|
125
|
+
products.each { |product| @products << product unless @products.include?(product) }
|
|
121
126
|
end
|
|
122
127
|
|
|
123
|
-
def capabilities_to_base64
|
|
128
|
+
def capabilities_to_base64(capabilities)
|
|
124
129
|
return "" if capabilities.empty?
|
|
125
130
|
|
|
126
131
|
cap_to_hexs = capabilities.reduce(:|).to_s(16).tap { |s| s.size.odd? && s.prepend("0") }.scan(/\h\h/)
|
|
@@ -15,7 +15,7 @@ module Datadog
|
|
|
15
15
|
#
|
|
16
16
|
# @api private
|
|
17
17
|
class Component
|
|
18
|
-
attr_reader :logger, :
|
|
18
|
+
attr_reader :logger, :healthy, :worker
|
|
19
19
|
|
|
20
20
|
def initialize(settings, capabilities, agent_settings, logger:)
|
|
21
21
|
@logger = logger
|
|
@@ -28,6 +28,7 @@ module Datadog
|
|
|
28
28
|
|
|
29
29
|
@barrier = Barrier.new(settings.remote.boot_timeout_seconds)
|
|
30
30
|
|
|
31
|
+
@client_mutex = Mutex.new
|
|
31
32
|
@client = Client.new(@transport, @capabilities, settings: settings, logger: logger)
|
|
32
33
|
@healthy = false
|
|
33
34
|
logger.debug { "new remote configuration client: #{@client.id} products: #{@capabilities.products.sort.join(", ")}" }
|
|
@@ -40,7 +41,7 @@ module Datadog
|
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
begin
|
|
43
|
-
|
|
44
|
+
client.sync
|
|
44
45
|
@healthy ||= true
|
|
45
46
|
rescue Client::SyncError => e
|
|
46
47
|
# Transient errors due to network or agent. Logged the error but not via telemetry
|
|
@@ -60,9 +61,11 @@ module Datadog
|
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
# client state is unknown, state might be corrupted
|
|
63
|
-
|
|
64
|
+
new_client = @client_mutex.synchronize do
|
|
65
|
+
@client = Client.new(@transport, @capabilities, settings: settings, logger: logger)
|
|
66
|
+
end
|
|
64
67
|
@healthy = false
|
|
65
|
-
logger.debug { "new remote configuration client: #{
|
|
68
|
+
logger.debug { "new remote configuration client: #{new_client.id} products: #{@capabilities.products.sort.join(", ")}" }
|
|
66
69
|
|
|
67
70
|
# TODO: bail out if too many errors?
|
|
68
71
|
end
|
|
@@ -81,6 +84,10 @@ module Datadog
|
|
|
81
84
|
@worker.started?
|
|
82
85
|
end
|
|
83
86
|
|
|
87
|
+
def client
|
|
88
|
+
@client_mutex.synchronize { @client }
|
|
89
|
+
end
|
|
90
|
+
|
|
84
91
|
# If the worker is not initialized, initialize it.
|
|
85
92
|
#
|
|
86
93
|
# Then, waits for one client sync to be executed if `kind` is `:once`.
|
|
@@ -98,9 +105,11 @@ module Datadog
|
|
|
98
105
|
def after_fork
|
|
99
106
|
@worker.after_fork
|
|
100
107
|
@barrier = Barrier.new(@settings.remote.boot_timeout_seconds)
|
|
101
|
-
|
|
108
|
+
new_client = @client_mutex.synchronize do
|
|
109
|
+
@client = Client.new(@transport, @capabilities, settings: @settings, logger: @logger)
|
|
110
|
+
end
|
|
102
111
|
@healthy = false
|
|
103
|
-
logger.debug { "remote configuration client recreated after fork: #{
|
|
112
|
+
logger.debug { "remote configuration client recreated after fork: #{new_client.id} products: #{@capabilities.products.sort.join(", ")}" }
|
|
104
113
|
end
|
|
105
114
|
|
|
106
115
|
def add_products(*products)
|
|
@@ -111,6 +120,17 @@ module Datadog
|
|
|
111
120
|
@capabilities.remove_products(*products)
|
|
112
121
|
end
|
|
113
122
|
|
|
123
|
+
def register(capabilities:, products:, receivers:)
|
|
124
|
+
@client_mutex.synchronize do
|
|
125
|
+
@capabilities.register_runtime(
|
|
126
|
+
capabilities: capabilities,
|
|
127
|
+
products: products,
|
|
128
|
+
receivers: receivers,
|
|
129
|
+
)
|
|
130
|
+
@client.dispatcher.add_receivers(*receivers)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
114
134
|
# Barrier provides a mechanism to fence execution until a condition happens
|
|
115
135
|
class Barrier
|
|
116
136
|
def initialize(timeout = nil)
|
|
@@ -189,14 +209,10 @@ module Datadog
|
|
|
189
209
|
#
|
|
190
210
|
# Those checks are instead performed inside the worker loop.
|
|
191
211
|
# This allows users to upgrade their agent while keeping their application running.
|
|
192
|
-
def build(settings, agent_settings, logger:, telemetry
|
|
212
|
+
def build(settings, agent_settings, logger:, telemetry:)
|
|
193
213
|
return unless settings.remote.enabled
|
|
194
214
|
|
|
195
|
-
capabilities = Client::Capabilities.new(
|
|
196
|
-
settings,
|
|
197
|
-
telemetry,
|
|
198
|
-
open_feature_component_provider: open_feature_component_provider,
|
|
199
|
-
)
|
|
215
|
+
capabilities = Client::Capabilities.new(settings, telemetry: telemetry)
|
|
200
216
|
new(settings, capabilities, agent_settings, logger: logger)
|
|
201
217
|
end
|
|
202
218
|
end
|
|
@@ -5,10 +5,20 @@ module Datadog
|
|
|
5
5
|
module Remote
|
|
6
6
|
# Repository update dispatcher
|
|
7
7
|
class Dispatcher
|
|
8
|
-
attr_reader :receivers
|
|
9
|
-
|
|
10
8
|
def initialize(receivers)
|
|
11
|
-
@receivers = receivers
|
|
9
|
+
@receivers = receivers.dup
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def receivers
|
|
14
|
+
@mutex.synchronize { @receivers.dup }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def add_receivers(*receivers)
|
|
18
|
+
@mutex.synchronize do
|
|
19
|
+
receivers.each { |receiver| @receivers << receiver unless @receivers.include?(receiver) }
|
|
20
|
+
end
|
|
21
|
+
nil
|
|
12
22
|
end
|
|
13
23
|
|
|
14
24
|
def dispatch(changes, repository)
|
data/lib/datadog/di/boot.rb
CHANGED
|
@@ -7,6 +7,7 @@ require_relative "code_tracker"
|
|
|
7
7
|
require_relative "component"
|
|
8
8
|
require_relative "context"
|
|
9
9
|
require_relative "capture_limits"
|
|
10
|
+
require_relative "guardrails_telemetry"
|
|
10
11
|
require_relative "capture_expression"
|
|
11
12
|
require_relative "capture_expression_evaluator"
|
|
12
13
|
require_relative "instrumenter"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module DI
|
|
5
|
+
class CaptureExpressionEvaluator
|
|
6
|
+
# The result of evaluating a probe's capture expressions: the
|
|
7
|
+
# serialized values and the evaluation errors.
|
|
8
|
+
class Result
|
|
9
|
+
# Initializes the result.
|
|
10
|
+
#
|
|
11
|
+
# @param serialized_values [Hash{String => untyped}] serialized values keyed by capture expression name
|
|
12
|
+
# @param errors [Array<EvaluationFailure>] evaluation errors of the expressions that failed to evaluate or serialize
|
|
13
|
+
# @return [void]
|
|
14
|
+
def initialize(serialized_values:, errors:)
|
|
15
|
+
@serialized_values = serialized_values
|
|
16
|
+
@errors = errors
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# The serialized values keyed by capture expression name.
|
|
20
|
+
# @return [Hash{String => untyped}]
|
|
21
|
+
attr_reader :serialized_values
|
|
22
|
+
|
|
23
|
+
# The evaluation errors; one entry per expression that failed to
|
|
24
|
+
# evaluate or serialize.
|
|
25
|
+
# @return [Array<EvaluationFailure>]
|
|
26
|
+
attr_reader :errors
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -2,17 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "capture_expression"
|
|
4
4
|
require_relative "capture_limits"
|
|
5
|
+
require_relative "capture_expression_evaluator/result"
|
|
6
|
+
require_relative "evaluation_failure"
|
|
5
7
|
require_relative "fatal_exceptions"
|
|
8
|
+
require_relative "guardrails_telemetry"
|
|
9
|
+
require_relative "../core/utils/time"
|
|
6
10
|
|
|
7
11
|
module Datadog
|
|
8
12
|
module DI
|
|
9
13
|
class CaptureExpressionEvaluator
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
# Initializes the evaluator.
|
|
15
|
+
#
|
|
16
|
+
# @param settings [Datadog::Core::Configuration::Settings] tracer settings
|
|
17
|
+
# @param serializer [Serializer] serializer for captured expression values
|
|
18
|
+
# @param logger [DI::Logger] logger for evaluation failure diagnostics
|
|
19
|
+
# @param guardrails_telemetry [GuardrailsTelemetry] emitter for the canonical capture-timeout skip metric
|
|
20
|
+
# @param telemetry [Datadog::Core::Telemetry::Component, nil] component evaluation failures are reported through
|
|
21
|
+
# @return [void]
|
|
22
|
+
def initialize(settings:, serializer:, logger:, guardrails_telemetry:, telemetry: nil)
|
|
13
23
|
@settings = settings
|
|
14
24
|
@serializer = serializer
|
|
15
25
|
@logger = logger
|
|
26
|
+
@guardrails_telemetry = guardrails_telemetry
|
|
16
27
|
@telemetry = telemetry
|
|
17
28
|
end
|
|
18
29
|
|
|
@@ -22,11 +33,25 @@ module Datadog
|
|
|
22
33
|
|
|
23
34
|
attr_reader :logger
|
|
24
35
|
|
|
36
|
+
# The guardrails metric emitter.
|
|
37
|
+
# @return [GuardrailsTelemetry]
|
|
38
|
+
attr_reader :guardrails_telemetry
|
|
39
|
+
|
|
25
40
|
attr_reader :telemetry
|
|
26
41
|
|
|
42
|
+
# Evaluates each of the probe's capture expressions against the given
|
|
43
|
+
# context, serializing the captured values under one capture deadline
|
|
44
|
+
# resolved through the serializer and shared across all expressions.
|
|
45
|
+
# An expression that fails to evaluate or serialize contributes an entry
|
|
46
|
+
# to the returned errors.
|
|
47
|
+
#
|
|
48
|
+
# @param probe [Probe] the probe whose capture expressions are evaluated
|
|
49
|
+
# @param context [Context] evaluation context the expressions are evaluated against
|
|
50
|
+
# @return [Result] the evaluation result: the serialized values keyed by
|
|
51
|
+
# capture expression name and the errors of the expressions that failed
|
|
52
|
+
# to evaluate or serialize
|
|
27
53
|
def evaluate(probe, context)
|
|
28
|
-
|
|
29
|
-
deadline_ns = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :nanosecond) + budget_ns
|
|
54
|
+
deadline = serializer.serialization_deadline
|
|
30
55
|
|
|
31
56
|
output = {}
|
|
32
57
|
evaluation_errors = []
|
|
@@ -34,9 +59,13 @@ module Datadog
|
|
|
34
59
|
probe.capture_expressions.each do |capture_expression|
|
|
35
60
|
name = capture_expression.name
|
|
36
61
|
|
|
37
|
-
if ::
|
|
62
|
+
if Core::Utils::Time.get_time >= deadline
|
|
38
63
|
output[name] = {notCapturedReason: "timeout"}
|
|
39
|
-
telemetry&.inc(TELEMETRY_NAMESPACE, "capture_expressions_skipped_by_timeout", 1)
|
|
64
|
+
telemetry&.inc(DI::TELEMETRY_NAMESPACE, "capture_expressions_skipped_by_timeout", 1)
|
|
65
|
+
guardrails_telemetry.skipped(
|
|
66
|
+
reason: GuardrailsTelemetry::Reason::EVALUATION_TIMEOUT,
|
|
67
|
+
probe: probe,
|
|
68
|
+
)
|
|
40
69
|
next
|
|
41
70
|
end
|
|
42
71
|
|
|
@@ -53,10 +82,11 @@ module Datadog
|
|
|
53
82
|
attribute_count: limits[:attribute_count],
|
|
54
83
|
length: limits[:length],
|
|
55
84
|
collection_size: limits[:collection_size],
|
|
85
|
+
deadline: deadline,
|
|
56
86
|
)
|
|
57
87
|
rescue Exception => exc # standard:disable Lint/RescueException
|
|
58
88
|
Datadog::DI.reraise_if_fatal(exc)
|
|
59
|
-
evaluation_errors <<
|
|
89
|
+
evaluation_errors << EvaluationFailure.new(expr: name, message: "#{exc.class}: #{exc.message}")
|
|
60
90
|
logger.debug do
|
|
61
91
|
"di: probe #{probe.id}: capture expression #{name}: evaluation failed: #{exc.class}: #{exc.message}"
|
|
62
92
|
end
|
|
@@ -64,7 +94,7 @@ module Datadog
|
|
|
64
94
|
end
|
|
65
95
|
end
|
|
66
96
|
|
|
67
|
-
|
|
97
|
+
Result.new(serialized_values: output, errors: evaluation_errors)
|
|
68
98
|
end
|
|
69
99
|
end
|
|
70
100
|
end
|
data/lib/datadog/di/component.rb
CHANGED
|
@@ -16,6 +16,16 @@ module Datadog
|
|
|
16
16
|
# resources and installed tracepoints upon shutdown.
|
|
17
17
|
class Component
|
|
18
18
|
class << self
|
|
19
|
+
# Builds and registers the DI component, or returns nil without
|
|
20
|
+
# building one when the settings have no dynamic_instrumentation
|
|
21
|
+
# section, DI is explicitly disabled, the runtime is unsupported,
|
|
22
|
+
# or Remote Configuration is disabled.
|
|
23
|
+
#
|
|
24
|
+
# @param settings [Datadog::Core::Configuration::Settings]
|
|
25
|
+
# @param agent_settings [Datadog::Core::Configuration::AgentSettings]
|
|
26
|
+
# @param logger [Core::Logger]
|
|
27
|
+
# @param telemetry [Core::Telemetry::Component, nil]
|
|
28
|
+
# @return [Component, nil] the built component, or nil when no component was built
|
|
19
29
|
def build(settings, agent_settings, logger, telemetry: nil)
|
|
20
30
|
return unless settings.respond_to?(:dynamic_instrumentation)
|
|
21
31
|
|
|
@@ -65,28 +75,47 @@ module Datadog
|
|
|
65
75
|
end
|
|
66
76
|
end
|
|
67
77
|
|
|
78
|
+
# Initializes the DI component.
|
|
79
|
+
#
|
|
80
|
+
# @param settings [Datadog::Core::Configuration::Settings] tracer settings
|
|
81
|
+
# @param agent_settings [Datadog::Core::Configuration::AgentSettings] agent connection settings for the transports
|
|
82
|
+
# @param logger [Datadog::Core::Logger] diagnostics logger, wrapped in the DI logger facade
|
|
83
|
+
# @param code_tracker [CodeTracker, nil] code tracker for line probe installation
|
|
84
|
+
# @param telemetry [Datadog::Core::Telemetry::Component, nil] component DI errors and guardrails metrics are emitted through
|
|
85
|
+
# @return [void]
|
|
68
86
|
def initialize(settings, agent_settings, logger, code_tracker: nil, telemetry: nil)
|
|
69
87
|
@settings = settings
|
|
70
88
|
@agent_settings = agent_settings
|
|
71
89
|
logger = DI::Logger.new(settings, logger)
|
|
72
90
|
@logger = logger
|
|
73
91
|
@telemetry = telemetry
|
|
92
|
+
@guardrails_telemetry = GuardrailsTelemetry.new(settings: settings, logger: logger, telemetry: telemetry)
|
|
74
93
|
@code_tracker = code_tracker
|
|
75
94
|
@redactor = Redactor.new(settings)
|
|
76
|
-
@serializer = Serializer.new(settings, redactor,
|
|
77
|
-
|
|
95
|
+
@serializer = Serializer.new(settings, redactor, logger: logger,
|
|
96
|
+
guardrails_telemetry: guardrails_telemetry, telemetry: telemetry)
|
|
97
|
+
@capture_expression_evaluator = CaptureExpressionEvaluator.new(
|
|
98
|
+
settings: settings, serializer: serializer, logger: logger,
|
|
99
|
+
guardrails_telemetry: guardrails_telemetry, telemetry: telemetry,
|
|
100
|
+
)
|
|
101
|
+
@instrumenter = Instrumenter.new(settings, serializer, logger, code_tracker: code_tracker, telemetry: telemetry,
|
|
102
|
+
guardrails_telemetry: guardrails_telemetry,
|
|
103
|
+
capture_expression_evaluator: capture_expression_evaluator,)
|
|
78
104
|
@probe_repository = ProbeRepository.new
|
|
79
|
-
@probe_notification_builder = ProbeNotificationBuilder.new(settings, serializer, logger,
|
|
105
|
+
@probe_notification_builder = ProbeNotificationBuilder.new(settings, serializer, logger,
|
|
106
|
+
capture_expression_evaluator: capture_expression_evaluator,)
|
|
80
107
|
@probe_notifier_worker = ProbeNotifierWorker.new(
|
|
81
108
|
settings, logger,
|
|
82
109
|
agent_settings: agent_settings,
|
|
83
110
|
probe_repository: probe_repository,
|
|
84
111
|
probe_notification_builder: probe_notification_builder,
|
|
85
112
|
telemetry: telemetry,
|
|
113
|
+
guardrails_telemetry: guardrails_telemetry,
|
|
86
114
|
)
|
|
87
115
|
@probe_manager = ProbeManager.new(
|
|
88
116
|
settings, instrumenter, probe_notification_builder, probe_notifier_worker, logger, probe_repository,
|
|
89
117
|
telemetry: telemetry,
|
|
118
|
+
guardrails_telemetry: guardrails_telemetry,
|
|
90
119
|
)
|
|
91
120
|
# @started transitions are serialized by @lifecycle_mutex so that
|
|
92
121
|
# concurrent RC callbacks (which run on the remote-config thread)
|
|
@@ -99,6 +128,12 @@ module Datadog
|
|
|
99
128
|
attr_reader :agent_settings
|
|
100
129
|
attr_reader :logger
|
|
101
130
|
attr_reader :telemetry
|
|
131
|
+
# The guardrails metric emitter.
|
|
132
|
+
# @return [GuardrailsTelemetry]
|
|
133
|
+
attr_reader :guardrails_telemetry
|
|
134
|
+
# The capture expression evaluator.
|
|
135
|
+
# @return [CaptureExpressionEvaluator]
|
|
136
|
+
attr_reader :capture_expression_evaluator
|
|
102
137
|
attr_reader :code_tracker
|
|
103
138
|
attr_reader :instrumenter
|
|
104
139
|
attr_reader :probe_repository
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../core/configuration/stable_config"
|
|
4
|
+
|
|
3
5
|
module Datadog
|
|
4
6
|
module DI
|
|
5
7
|
# Configuration for DI
|
|
@@ -136,8 +138,35 @@ module Datadog
|
|
|
136
138
|
|
|
137
139
|
option :max_time_to_serialize_ms do |o|
|
|
138
140
|
o.type :int
|
|
139
|
-
o.
|
|
140
|
-
o.
|
|
141
|
+
o.env "DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS"
|
|
142
|
+
o.default do
|
|
143
|
+
legacy = Settings.legacy_capture_timeout_value
|
|
144
|
+
if legacy
|
|
145
|
+
Datadog::Core.log_deprecation(key: :di_legacy_max_time_to_serialize) do
|
|
146
|
+
"The DD_DYNAMIC_INSTRUMENTATION_MAX_TIME_TO_SERIALIZE environment variable is deprecated " \
|
|
147
|
+
"and will be removed in a future release. " \
|
|
148
|
+
"Please use DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS instead."
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
begin
|
|
152
|
+
Integer(legacy, 10)
|
|
153
|
+
rescue ArgumentError
|
|
154
|
+
raise ArgumentError,
|
|
155
|
+
"Expected environment variable DD_DYNAMIC_INSTRUMENTATION_MAX_TIME_TO_SERIALIZE " \
|
|
156
|
+
"to be an int, but '#{legacy}' was provided."
|
|
157
|
+
end
|
|
158
|
+
else
|
|
159
|
+
150
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
o.setter do |value, _old|
|
|
163
|
+
if value < 0
|
|
164
|
+
raise ArgumentError,
|
|
165
|
+
"The setting `dynamic_instrumentation.max_time_to_serialize_ms` must not be negative, " \
|
|
166
|
+
"but `#{value.inspect}` was provided."
|
|
167
|
+
end
|
|
168
|
+
value
|
|
169
|
+
end
|
|
141
170
|
end
|
|
142
171
|
|
|
143
172
|
# Settings in the 'internal' group are for internal Datadog
|
|
@@ -244,6 +273,28 @@ module Datadog
|
|
|
244
273
|
end
|
|
245
274
|
end
|
|
246
275
|
end
|
|
276
|
+
|
|
277
|
+
# Resolves the deprecated capture-timeout name for the setting's
|
|
278
|
+
# default. The legacy value applies only when the canonical variable
|
|
279
|
+
# is configured nowhere (environment with its registry alias, local or
|
|
280
|
+
# fleet stable configuration), and the legacy itself resolves in the
|
|
281
|
+
# option machinery's precedence order: fleet stable configuration,
|
|
282
|
+
# environment, local stable configuration.
|
|
283
|
+
def self.legacy_capture_timeout_value
|
|
284
|
+
canonical_name = "DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS"
|
|
285
|
+
legacy_name = "DD_DYNAMIC_INSTRUMENTATION_MAX_TIME_TO_SERIALIZE"
|
|
286
|
+
|
|
287
|
+
stable = Datadog::Core::Configuration::StableConfig.configuration
|
|
288
|
+
local = stable&.dig(:local, :config)
|
|
289
|
+
fleet = stable&.dig(:fleet, :config)
|
|
290
|
+
|
|
291
|
+
canonical = (fleet && DATADOG_ENV.get_environment_variable(canonical_name, source_env: fleet)) ||
|
|
292
|
+
DATADOG_ENV[canonical_name] ||
|
|
293
|
+
(local && DATADOG_ENV.get_environment_variable(canonical_name, source_env: local))
|
|
294
|
+
return nil if canonical
|
|
295
|
+
|
|
296
|
+
(fleet && fleet[legacy_name]) || DATADOG_ENV[legacy_name] || (local && local[legacy_name])
|
|
297
|
+
end
|
|
247
298
|
end
|
|
248
299
|
end
|
|
249
300
|
end
|
data/lib/datadog/di/context.rb
CHANGED
|
@@ -61,9 +61,14 @@ module Datadog
|
|
|
61
61
|
# Exception raised by the method, if any, for a method probe
|
|
62
62
|
attr_reader :exception
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
# Returns the locals captured at the probe hit in serialized form,
|
|
65
|
+
# or nil when no locals were captured.
|
|
66
|
+
#
|
|
67
|
+
# @param serialization_deadline [Float] absolute capture deadline in monotonic clock seconds
|
|
68
|
+
# @return [Hash{Symbol => Hash}, nil] serialized locals keyed by variable name
|
|
69
|
+
def serialized_locals(serialization_deadline:)
|
|
65
70
|
# TODO cache?
|
|
66
|
-
locals && serializer.serialize_vars(locals, **probe.snapshot_serializer_limits(settings))
|
|
71
|
+
locals && serializer.serialize_vars(locals, deadline: serialization_deadline, **probe.snapshot_serializer_limits(settings))
|
|
67
72
|
end
|
|
68
73
|
|
|
69
74
|
def fetch(var_name)
|
|
@@ -10,7 +10,7 @@ Datadog::DI::Serializer.register(
|
|
|
10
10
|
# logic for "instances of classes derived from X", but a condition Proc
|
|
11
11
|
# is more universal.
|
|
12
12
|
condition: lambda { |value| ActiveRecord::Base === value }
|
|
13
|
-
) do |serializer, value, name:, depth:|
|
|
13
|
+
) do |serializer, value, name:, depth:, deadline:|
|
|
14
14
|
# +serializer+ is an instance of DI::Serializer.
|
|
15
15
|
# Use it to perform the serialization to primitive values.
|
|
16
16
|
#
|
|
@@ -30,12 +30,18 @@ Datadog::DI::Serializer.register(
|
|
|
30
30
|
# nesting levels in the output.
|
|
31
31
|
# This serializer could also potentially do its own depth limiting.
|
|
32
32
|
#
|
|
33
|
+
# +deadline+ is the absolute monotonic-clock deadline (in seconds) of the
|
|
34
|
+
# capture point that is being serialized. Pass it through to
|
|
35
|
+
# +serialize_value+ so the custom-serialized subtree draws down the same
|
|
36
|
+
# capture budget as the rest of the snapshot.
|
|
37
|
+
#
|
|
33
38
|
# Steep: steep thinks all of the arguments are nil here
|
|
34
39
|
# Looks like it cannot handle kwargs in lambdas
|
|
35
40
|
# @type var depth: Integer
|
|
41
|
+
# @type var deadline: Float
|
|
36
42
|
value_to_serialize = {
|
|
37
43
|
attributes: value.attributes,
|
|
38
44
|
new_record: value.new_record?,
|
|
39
45
|
}
|
|
40
|
-
serializer.serialize_value(value_to_serialize, depth: depth, type: value.class)
|
|
46
|
+
serializer.serialize_value(value_to_serialize, depth: depth, type: value.class, deadline: deadline)
|
|
41
47
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module DI
|
|
5
|
+
# An expression evaluation failure reported in a snapshot's evaluation
|
|
6
|
+
# errors: the expression that failed and the error description.
|
|
7
|
+
# @api private
|
|
8
|
+
class EvaluationFailure
|
|
9
|
+
# Initializes the failure.
|
|
10
|
+
#
|
|
11
|
+
# @param expr [String] name of the capture expression, or source of the condition or template expression, that failed to evaluate or serialize
|
|
12
|
+
# @param message [String] error description of the failed evaluation
|
|
13
|
+
# @return [void]
|
|
14
|
+
def initialize(expr:, message:)
|
|
15
|
+
@expr = expr
|
|
16
|
+
@message = message
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# The name of the capture expression, or the source of the condition or
|
|
20
|
+
# template expression, that failed to evaluate or serialize.
|
|
21
|
+
# @return [String]
|
|
22
|
+
attr_reader :expr
|
|
23
|
+
|
|
24
|
+
# @return [String] the error description of the failed evaluation
|
|
25
|
+
attr_reader :message
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|