datadog 2.23.0 → 2.25.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 +67 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +17 -5
- data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
- data/ext/datadog_profiling_native_extension/extconf.rb +4 -1
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
- data/ext/datadog_profiling_native_extension/profiling.c +2 -0
- data/lib/datadog/ai_guard/api_client.rb +82 -0
- data/lib/datadog/ai_guard/component.rb +42 -0
- data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
- data/lib/datadog/ai_guard/configuration/settings.rb +98 -0
- data/lib/datadog/ai_guard/configuration.rb +11 -0
- data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
- data/lib/datadog/ai_guard/evaluation/no_op_result.rb +34 -0
- data/lib/datadog/ai_guard/evaluation/request.rb +81 -0
- data/lib/datadog/ai_guard/evaluation/result.rb +43 -0
- data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
- data/lib/datadog/ai_guard/evaluation.rb +72 -0
- data/lib/datadog/ai_guard/ext.rb +16 -0
- data/lib/datadog/ai_guard.rb +153 -0
- data/lib/datadog/appsec/context.rb +2 -1
- data/lib/datadog/appsec/remote.rb +5 -12
- data/lib/datadog/appsec/security_engine/engine.rb +3 -3
- data/lib/datadog/appsec/security_engine/result.rb +2 -1
- data/lib/datadog/appsec/security_engine/runner.rb +2 -2
- data/lib/datadog/core/configuration/components.rb +6 -0
- data/lib/datadog/core/configuration/config_helper.rb +1 -1
- data/lib/datadog/core/configuration/deprecations.rb +2 -2
- data/lib/datadog/core/configuration/option_definition.rb +4 -2
- data/lib/datadog/core/configuration/options.rb +8 -5
- data/lib/datadog/core/configuration/settings.rb +14 -3
- data/lib/datadog/core/configuration/supported_configurations.rb +8 -1
- data/lib/datadog/core/environment/cgroup.rb +52 -25
- data/lib/datadog/core/environment/container.rb +140 -46
- data/lib/datadog/core/environment/ext.rb +1 -0
- data/lib/datadog/core/environment/process.rb +9 -1
- data/lib/datadog/core/error.rb +6 -6
- data/lib/datadog/core/pin.rb +4 -0
- data/lib/datadog/core/rate_limiter.rb +9 -1
- data/lib/datadog/core/remote/client.rb +14 -6
- data/lib/datadog/core/remote/component.rb +6 -4
- data/lib/datadog/core/remote/configuration/content.rb +15 -2
- data/lib/datadog/core/remote/configuration/digest.rb +14 -7
- data/lib/datadog/core/remote/configuration/repository.rb +1 -1
- data/lib/datadog/core/remote/configuration/target.rb +13 -6
- data/lib/datadog/core/remote/transport/config.rb +3 -16
- data/lib/datadog/core/remote/transport/http/config.rb +4 -44
- data/lib/datadog/core/remote/transport/http/negotiation.rb +0 -39
- data/lib/datadog/core/remote/transport/http.rb +13 -24
- data/lib/datadog/core/remote/transport/negotiation.rb +7 -16
- data/lib/datadog/core/semaphore.rb +1 -4
- data/lib/datadog/core/telemetry/component.rb +52 -13
- data/lib/datadog/core/telemetry/event/app_started.rb +36 -1
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
- data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
- data/lib/datadog/core/telemetry/request.rb +17 -3
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +2 -32
- data/lib/datadog/core/telemetry/transport/http.rb +21 -16
- data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -10
- data/lib/datadog/core/telemetry/worker.rb +88 -32
- data/lib/datadog/core/transport/ext.rb +2 -0
- data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
- data/lib/datadog/core/transport/http/api/instance.rb +4 -21
- data/lib/datadog/core/transport/http/builder.rb +9 -5
- data/lib/datadog/core/transport/http/client.rb +19 -8
- data/lib/datadog/core/transport/http.rb +22 -19
- data/lib/datadog/core/transport/response.rb +12 -1
- data/lib/datadog/core/transport/transport.rb +90 -0
- data/lib/datadog/core/utils/only_once_successful.rb +2 -0
- data/lib/datadog/core/utils/safe_dup.rb +2 -2
- data/lib/datadog/core/utils/sequence.rb +2 -0
- data/lib/datadog/core/utils/time.rb +1 -1
- data/lib/datadog/core/workers/async.rb +10 -1
- data/lib/datadog/core/workers/interval_loop.rb +44 -3
- data/lib/datadog/core/workers/polling.rb +2 -0
- data/lib/datadog/core/workers/queue.rb +100 -1
- data/lib/datadog/data_streams/processor.rb +1 -1
- data/lib/datadog/data_streams/transport/http/stats.rb +1 -36
- data/lib/datadog/data_streams/transport/http.rb +5 -6
- data/lib/datadog/data_streams/transport/stats.rb +3 -17
- data/lib/datadog/di/boot.rb +4 -2
- data/lib/datadog/di/contrib/active_record.rb +30 -5
- data/lib/datadog/di/el/compiler.rb +8 -4
- data/lib/datadog/di/error.rb +5 -0
- data/lib/datadog/di/instrumenter.rb +26 -7
- data/lib/datadog/di/logger.rb +2 -2
- data/lib/datadog/di/probe_builder.rb +2 -1
- data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
- data/lib/datadog/di/probe_manager.rb +37 -31
- data/lib/datadog/di/probe_notification_builder.rb +15 -2
- data/lib/datadog/di/probe_notifier_worker.rb +5 -5
- data/lib/datadog/di/remote.rb +89 -84
- data/lib/datadog/di/transport/diagnostics.rb +7 -35
- data/lib/datadog/di/transport/http/diagnostics.rb +1 -31
- data/lib/datadog/di/transport/http/input.rb +1 -31
- data/lib/datadog/di/transport/http.rb +28 -17
- data/lib/datadog/di/transport/input.rb +7 -34
- data/lib/datadog/di.rb +61 -5
- data/lib/datadog/error_tracking/filters.rb +2 -2
- data/lib/datadog/kit/appsec/events/v2.rb +2 -3
- data/lib/datadog/open_feature/evaluation_engine.rb +2 -1
- data/lib/datadog/open_feature/remote.rb +3 -10
- data/lib/datadog/open_feature/transport.rb +9 -11
- data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
- data/lib/datadog/opentelemetry/configuration/settings.rb +2 -2
- data/lib/datadog/opentelemetry/metrics.rb +21 -14
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +5 -8
- data/lib/datadog/profiling/collectors/code_provenance.rb +27 -2
- data/lib/datadog/profiling/collectors/info.rb +5 -4
- data/lib/datadog/profiling/component.rb +12 -11
- data/lib/datadog/profiling/ext/dir_monkey_patches.rb +18 -0
- data/lib/datadog/profiling/http_transport.rb +4 -1
- data/lib/datadog/tracing/contrib/extensions.rb +10 -2
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +31 -32
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +2 -1
- data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +6 -3
- data/lib/datadog/tracing/contrib/waterdrop.rb +4 -0
- data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
- data/lib/datadog/tracing/distributed/baggage.rb +3 -2
- data/lib/datadog/tracing/remote.rb +1 -9
- data/lib/datadog/tracing/sampling/priority_sampler.rb +3 -1
- data/lib/datadog/tracing/span.rb +1 -1
- data/lib/datadog/tracing/span_event.rb +2 -2
- data/lib/datadog/tracing/span_operation.rb +20 -9
- data/lib/datadog/tracing/trace_operation.rb +44 -6
- data/lib/datadog/tracing/tracer.rb +42 -16
- data/lib/datadog/tracing/transport/http/traces.rb +2 -50
- data/lib/datadog/tracing/transport/http.rb +15 -9
- data/lib/datadog/tracing/transport/io/client.rb +1 -1
- data/lib/datadog/tracing/transport/traces.rb +6 -66
- data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
- data/lib/datadog/tracing/writer.rb +1 -0
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +1 -0
- metadata +24 -17
- data/lib/datadog/core/remote/transport/http/api.rb +0 -53
- data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
- data/lib/datadog/core/transport/http/api/spec.rb +0 -36
- data/lib/datadog/data_streams/transport/http/api.rb +0 -33
- data/lib/datadog/data_streams/transport/http/client.rb +0 -21
- data/lib/datadog/di/transport/http/api.rb +0 -42
- data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
- data/lib/datadog/tracing/transport/http/api.rb +0 -44
|
@@ -1,12 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
Datadog::DI::Serializer.register(
|
|
4
|
-
#
|
|
5
|
-
#
|
|
3
|
+
Datadog::DI::Serializer.register(
|
|
4
|
+
# This serializer uses a dynamic condition to determine its applicability
|
|
5
|
+
# to a particular value. A simpler case could have been a serializer for
|
|
6
|
+
# a particular class, but in this case any ActiveRecord model is covered
|
|
7
|
+
# and they all have different classes.
|
|
8
|
+
#
|
|
9
|
+
# An alternative could have been to make DI specifically provide lookup
|
|
10
|
+
# logic for "instances of classes derived from X", but a condition Proc
|
|
11
|
+
# is more universal.
|
|
12
|
+
condition: lambda { |value| ActiveRecord::Base === value }
|
|
13
|
+
) do |serializer, value, name:, depth:|
|
|
14
|
+
# +serializer+ is an instance of DI::Serializer.
|
|
15
|
+
# Use it to perform the serialization to primitive values.
|
|
16
|
+
#
|
|
17
|
+
# +value+ is the value to serialize. It should match the condition
|
|
18
|
+
# provided above, meaning it would be an ActiveRecord::Base instance.
|
|
19
|
+
#
|
|
20
|
+
# +name+ is the name of the (local/instance) variable being serialized.
|
|
21
|
+
# The name is used by DI for redaction (upstream of serialization logic),
|
|
22
|
+
# and could potentially be used for redaction here also.
|
|
23
|
+
#
|
|
24
|
+
# +depth+ is the remaining depth for serializing collections and objects.
|
|
25
|
+
# It should always be an integer.
|
|
26
|
+
# Reduce it by 1 when invoking +serialize_value+ on the contents of +value+.
|
|
27
|
+
# This serializer could also potentially do its own depth limiting.
|
|
28
|
+
#
|
|
29
|
+
# Steep: steep thinks all of the arguments are nil here
|
|
30
|
+
# Looks like it cannot handle kwargs in lambdas
|
|
31
|
+
# @type var depth: Integer
|
|
6
32
|
value_to_serialize = {
|
|
7
33
|
attributes: value.attributes,
|
|
8
34
|
new_record: value.new_record?,
|
|
9
35
|
}
|
|
10
|
-
serializer.serialize_value(value_to_serialize, depth: depth
|
|
11
|
-
# steep:ignore:end
|
|
36
|
+
serializer.serialize_value(value_to_serialize, depth: depth - 1, type: value.class)
|
|
12
37
|
end
|
|
@@ -22,7 +22,8 @@ module Datadog
|
|
|
22
22
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
# Steep: https://github.com/soutaro/steep/issues/363
|
|
26
|
+
OPERATORS = { # steep:ignore IncompatibleAssignment
|
|
26
27
|
'eq' => '==',
|
|
27
28
|
'ne' => '!=',
|
|
28
29
|
'ge' => '>=',
|
|
@@ -31,16 +32,19 @@ module Datadog
|
|
|
31
32
|
'lt' => '<',
|
|
32
33
|
}.freeze
|
|
33
34
|
|
|
35
|
+
# Steep: https://github.com/soutaro/steep/issues/363
|
|
34
36
|
SINGLE_ARG_METHODS = %w[
|
|
35
37
|
len isEmpty isUndefined
|
|
36
|
-
].freeze
|
|
38
|
+
].freeze # steep:ignore IncompatibleAssignment
|
|
37
39
|
|
|
40
|
+
# Steep: https://github.com/soutaro/steep/issues/363
|
|
38
41
|
TWO_ARG_METHODS = %w[
|
|
39
42
|
startsWith endsWith contains matches
|
|
40
43
|
getmember index instanceof
|
|
41
|
-
].freeze
|
|
44
|
+
].freeze # steep:ignore IncompatibleAssignment
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
# Steep: https://github.com/soutaro/steep/issues/363
|
|
47
|
+
MULTI_ARG_METHODS = { # steep:ignore IncompatibleAssignment
|
|
44
48
|
'and' => '&&',
|
|
45
49
|
'or' => '||',
|
|
46
50
|
}.freeze
|
data/lib/datadog/di/error.rb
CHANGED
|
@@ -42,6 +42,11 @@ module Datadog
|
|
|
42
42
|
class ProbePreviouslyFailed < Error
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
# Raised when trying to instrument a probe when there is existing
|
|
46
|
+
# instrumentation for the same probe id.
|
|
47
|
+
class AlreadyInstrumented < Error
|
|
48
|
+
end
|
|
49
|
+
|
|
45
50
|
# Raised when installing a line probe and multiple files match the
|
|
46
51
|
# specified path suffix.
|
|
47
52
|
# A probe must be installed into one file only, since UI only
|
|
@@ -114,7 +114,9 @@ module Datadog
|
|
|
114
114
|
settings = self.settings
|
|
115
115
|
|
|
116
116
|
mod = Module.new do
|
|
117
|
-
define_method(method_name) do |*args, **kwargs, &target_block| # steep:ignore
|
|
117
|
+
define_method(method_name) do |*args, **kwargs, &target_block| # steep:ignore NoMethod
|
|
118
|
+
# Steep: Unsure why it cannot detect kwargs in this block. Workaround:
|
|
119
|
+
# @type var kwargs: ::Hash[::Symbol, untyped]
|
|
118
120
|
continue = true
|
|
119
121
|
if condition = probe.condition
|
|
120
122
|
begin
|
|
@@ -166,7 +168,10 @@ module Datadog
|
|
|
166
168
|
depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
|
|
167
169
|
attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
|
|
168
170
|
end
|
|
169
|
-
|
|
171
|
+
# We intentionally do not use Core::Utils::Time.get_time
|
|
172
|
+
# here because the time provider may be overridden by the
|
|
173
|
+
# customer, and DI is not allowed to invoke customer code.
|
|
174
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
170
175
|
|
|
171
176
|
rv = nil
|
|
172
177
|
begin
|
|
@@ -190,7 +195,7 @@ module Datadog
|
|
|
190
195
|
# the instrumentation callback runs.
|
|
191
196
|
end
|
|
192
197
|
|
|
193
|
-
duration =
|
|
198
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
|
194
199
|
# The method itself is not part of the stack trace because
|
|
195
200
|
# we are getting the stack trace from outside of the method.
|
|
196
201
|
# Add the method in manually as the top frame.
|
|
@@ -206,7 +211,8 @@ module Datadog
|
|
|
206
211
|
# that location here.
|
|
207
212
|
[]
|
|
208
213
|
end
|
|
209
|
-
|
|
214
|
+
# Steep: https://github.com/ruby/rbs/pull/2745
|
|
215
|
+
caller_locs = method_frame + caller_locations # steep:ignore ArgumentTypeMismatch
|
|
210
216
|
# TODO capture arguments at exit
|
|
211
217
|
|
|
212
218
|
context = Context.new(locals: nil, target_self: self,
|
|
@@ -256,6 +262,8 @@ module Datadog
|
|
|
256
262
|
|
|
257
263
|
probe.instrumentation_module = mod
|
|
258
264
|
cls.send(:prepend, mod)
|
|
265
|
+
|
|
266
|
+
DI.instrumented_count_inc(:method)
|
|
259
267
|
end
|
|
260
268
|
end
|
|
261
269
|
|
|
@@ -268,6 +276,8 @@ module Datadog
|
|
|
268
276
|
if mod = probe.instrumentation_module
|
|
269
277
|
mod.send(:remove_method, probe.method_name)
|
|
270
278
|
probe.instrumentation_module = nil
|
|
279
|
+
|
|
280
|
+
DI.instrumented_count_dec(:method)
|
|
271
281
|
end
|
|
272
282
|
end
|
|
273
283
|
end
|
|
@@ -298,7 +308,10 @@ module Datadog
|
|
|
298
308
|
|
|
299
309
|
iseq = nil
|
|
300
310
|
if code_tracker
|
|
301
|
-
|
|
311
|
+
# Steep: Complex type narrowing (before calling hook_line,
|
|
312
|
+
# we check that probe.line? is true which itself checks that probe.file is not nil)
|
|
313
|
+
# Annotation do not work here as `file` is a method on probe, not a local variable.
|
|
314
|
+
ret = code_tracker.iseqs_for_path_suffix(probe.file) # steep:ignore ArgumentTypeMismatch
|
|
302
315
|
unless ret
|
|
303
316
|
if permit_untargeted_trace_points
|
|
304
317
|
# Continue withoout targeting the trace point.
|
|
@@ -470,12 +483,16 @@ module Datadog
|
|
|
470
483
|
# actual_path could be nil if we don't use targeted trace points.
|
|
471
484
|
probe.instrumented_path = actual_path
|
|
472
485
|
|
|
473
|
-
|
|
486
|
+
# TracePoint#enable returns false when it succeeds.
|
|
487
|
+
rv = if iseq
|
|
474
488
|
tp.enable(target: iseq, target_line: line_no)
|
|
475
489
|
else
|
|
476
490
|
tp.enable
|
|
477
491
|
end
|
|
478
|
-
|
|
492
|
+
|
|
493
|
+
DI.instrumented_count_inc(:line)
|
|
494
|
+
|
|
495
|
+
rv
|
|
479
496
|
end
|
|
480
497
|
true
|
|
481
498
|
end
|
|
@@ -485,6 +502,8 @@ module Datadog
|
|
|
485
502
|
if tp = probe.instrumentation_trace_point
|
|
486
503
|
tp.disable
|
|
487
504
|
probe.instrumentation_trace_point = nil
|
|
505
|
+
|
|
506
|
+
DI.instrumented_count_dec(:line)
|
|
488
507
|
end
|
|
489
508
|
end
|
|
490
509
|
end
|
data/lib/datadog/di/logger.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Datadog
|
|
|
8
8
|
#
|
|
9
9
|
# @api private
|
|
10
10
|
class Logger
|
|
11
|
-
extend Forwardable
|
|
11
|
+
extend Forwardable
|
|
12
12
|
|
|
13
13
|
def initialize(settings, target)
|
|
14
14
|
@settings = settings
|
|
@@ -18,7 +18,7 @@ module Datadog
|
|
|
18
18
|
attr_reader :settings
|
|
19
19
|
attr_reader :target
|
|
20
20
|
|
|
21
|
-
def_delegators :target, :debug
|
|
21
|
+
def_delegators :target, :debug
|
|
22
22
|
|
|
23
23
|
def trace(&block)
|
|
24
24
|
if settings.dynamic_instrumentation.internal.trace_logging
|
|
@@ -6,7 +6,7 @@ module Datadog
|
|
|
6
6
|
# Railtie class initializes dynamic instrumentation contrib code
|
|
7
7
|
# in Rails environments.
|
|
8
8
|
class Railtie < Rails::Railtie
|
|
9
|
-
initializer 'datadog.dynamic_instrumentation.load_probe_file' do |app|
|
|
9
|
+
initializer 'datadog.dynamic_instrumentation.load_probe_file' do |app|
|
|
10
10
|
ProbeFileLoader.load_now
|
|
11
11
|
end
|
|
12
12
|
end
|
|
@@ -94,6 +94,19 @@ module Datadog
|
|
|
94
94
|
# matches.
|
|
95
95
|
def add_probe(probe)
|
|
96
96
|
@lock.synchronize do
|
|
97
|
+
if @installed_probes[probe.id]
|
|
98
|
+
# Either this probe was already installed, or another probe was
|
|
99
|
+
# installed with the same id (previous version perhaps?).
|
|
100
|
+
# Since our state tracking is keyed by probe id, we cannot
|
|
101
|
+
# install this probe since we won't have a way of removing the
|
|
102
|
+
# instrumentation for the probe with the same id which is already
|
|
103
|
+
# installed.
|
|
104
|
+
#
|
|
105
|
+
# The exception raised here will be caught below and logged and
|
|
106
|
+
# reported to telemetry.
|
|
107
|
+
raise Error::AlreadyInstrumented, "Probe with id #{probe.id} is already in installed probes"
|
|
108
|
+
end
|
|
109
|
+
|
|
97
110
|
# Probe failed to install previously, do not try to install it again.
|
|
98
111
|
if msg = @failed_probes[probe.id]
|
|
99
112
|
# TODO test this path
|
|
@@ -134,38 +147,30 @@ module Datadog
|
|
|
134
147
|
end
|
|
135
148
|
end
|
|
136
149
|
|
|
137
|
-
# Removes
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
# Remote config contains the list of currently defined probes; any
|
|
141
|
-
# probes not in that list have been removed by user and should be
|
|
142
|
-
# de-instrumented from the application.
|
|
143
|
-
def remove_other_probes(probe_ids)
|
|
150
|
+
# Removes probe with specified id. The probe could be pending or
|
|
151
|
+
# installed. Does nothing if there is no probe with the specified id.
|
|
152
|
+
def remove_probe(probe_id)
|
|
144
153
|
@lock.synchronize do
|
|
145
|
-
@pending_probes.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
logger.debug { "di: error removing #{probe.type} probe at #{probe.location} (#{probe.id}): #{exc.class}: #{exc}" }
|
|
166
|
-
telemetry&.report(exc, description: "Error removing probe")
|
|
167
|
-
end
|
|
168
|
-
end
|
|
154
|
+
@pending_probes.delete(probe_id)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Do not delete the probe from the registry here in case
|
|
158
|
+
# deinstrumentation fails - though I don't know why deinstrumentation
|
|
159
|
+
# would fail and how we could recover if it does.
|
|
160
|
+
# I plan on tracking the number of outstanding (instrumented) probes
|
|
161
|
+
# in the future, and if deinstrumentation fails I would want to
|
|
162
|
+
# keep that probe as "installed" for the count, so that we can
|
|
163
|
+
# investigate the situation.
|
|
164
|
+
if probe = @installed_probes[probe_id]
|
|
165
|
+
begin
|
|
166
|
+
instrumenter.unhook(probe)
|
|
167
|
+
@installed_probes.delete(probe_id)
|
|
168
|
+
rescue => exc
|
|
169
|
+
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
170
|
+
# Silence all exceptions?
|
|
171
|
+
# TODO should we propagate here and rescue upstream?
|
|
172
|
+
logger.debug { "di: error removing #{probe.type} probe at #{probe.location} (#{probe.id}): #{exc.class}: #{exc}" }
|
|
173
|
+
telemetry&.report(exc, description: "Error removing probe")
|
|
169
174
|
end
|
|
170
175
|
end
|
|
171
176
|
end
|
|
@@ -185,6 +190,7 @@ module Datadog
|
|
|
185
190
|
# TODO is it OK to hook from trace point handler?
|
|
186
191
|
# TODO the class is now defined, but can hooking still fail?
|
|
187
192
|
instrumenter.hook(probe, self)
|
|
193
|
+
@installed_probes[probe.id] = probe
|
|
188
194
|
@pending_probes.delete(probe.id)
|
|
189
195
|
break
|
|
190
196
|
rescue Error::DITargetNotDefined
|
|
@@ -46,7 +46,7 @@ module Datadog
|
|
|
46
46
|
build_snapshot(context)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
NANOSECONDS =
|
|
49
|
+
NANOSECONDS = 1_000_000_000
|
|
50
50
|
MILLISECONDS = 1000
|
|
51
51
|
|
|
52
52
|
def build_snapshot(context)
|
|
@@ -134,7 +134,7 @@ module Datadog
|
|
|
134
134
|
format_caller_locations(caller_locations)
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
-
{
|
|
137
|
+
payload = {
|
|
138
138
|
service: settings.service,
|
|
139
139
|
debugger: {
|
|
140
140
|
type: 'snapshot',
|
|
@@ -189,6 +189,10 @@ module Datadog
|
|
|
189
189
|
message: message,
|
|
190
190
|
timestamp: timestamp,
|
|
191
191
|
}
|
|
192
|
+
|
|
193
|
+
tag_process_tags!(payload, settings)
|
|
194
|
+
|
|
195
|
+
payload
|
|
192
196
|
end
|
|
193
197
|
|
|
194
198
|
def build_status(probe, message:, status:)
|
|
@@ -236,6 +240,15 @@ module Datadog
|
|
|
236
240
|
[message, evaluation_errors]
|
|
237
241
|
end
|
|
238
242
|
|
|
243
|
+
def tag_process_tags!(payload, settings)
|
|
244
|
+
return unless settings.experimental_propagate_process_tags_enabled
|
|
245
|
+
|
|
246
|
+
process_tags = Core::Environment::Process.serialized
|
|
247
|
+
return if process_tags.empty?
|
|
248
|
+
|
|
249
|
+
payload[:process_tags] = process_tags
|
|
250
|
+
end
|
|
251
|
+
|
|
239
252
|
def timestamp_now
|
|
240
253
|
(Core::Utils::Time.now.to_f * MILLISECONDS).to_i
|
|
241
254
|
end
|
|
@@ -249,12 +249,12 @@ module Datadog
|
|
|
249
249
|
@lock.synchronize do
|
|
250
250
|
batch = instance_variable_get("@#{event_type}_queue")
|
|
251
251
|
instance_variable_set("@#{event_type}_queue", [])
|
|
252
|
-
@io_in_progress = batch.any?
|
|
252
|
+
@io_in_progress = batch.any?
|
|
253
253
|
end
|
|
254
|
-
logger.trace { "di: #{self.class.name}: checking #{event_type} queue - #{batch.length} entries" }
|
|
255
|
-
if batch.any?
|
|
254
|
+
logger.trace { "di: #{self.class.name}: checking #{event_type} queue - #{batch.length} entries" }
|
|
255
|
+
if batch.any?
|
|
256
256
|
begin
|
|
257
|
-
logger.trace { "di: sending #{batch.length} #{event_type} event(s) to agent" }
|
|
257
|
+
logger.trace { "di: sending #{batch.length} #{event_type} event(s) to agent" }
|
|
258
258
|
send("do_send_#{event_type}", batch)
|
|
259
259
|
time = Core::Utils::Time.get_time
|
|
260
260
|
@lock.synchronize do
|
|
@@ -268,7 +268,7 @@ module Datadog
|
|
|
268
268
|
# telemetry message would also fail.
|
|
269
269
|
end
|
|
270
270
|
end
|
|
271
|
-
batch.any?
|
|
271
|
+
batch.any?
|
|
272
272
|
rescue ThreadError => exc
|
|
273
273
|
# Normally the queue should only be consumed in this method,
|
|
274
274
|
# however if anyone consumes it elsewhere we don't want to block
|
data/lib/datadog/di/remote.rb
CHANGED
|
@@ -12,8 +12,6 @@ module Datadog
|
|
|
12
12
|
#
|
|
13
13
|
# @api private
|
|
14
14
|
module Remote
|
|
15
|
-
class ReadError < StandardError; end
|
|
16
|
-
|
|
17
15
|
class << self
|
|
18
16
|
PRODUCT = 'LIVE_DEBUGGING'
|
|
19
17
|
|
|
@@ -28,7 +26,7 @@ module Datadog
|
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
def receivers(telemetry)
|
|
31
|
-
receiver do |repository,
|
|
29
|
+
receiver do |repository, changes|
|
|
32
30
|
# DEV: Filter our by product. Given it will be very common
|
|
33
31
|
# DEV: we can filter this out before we receive the data in this method.
|
|
34
32
|
# DEV: Apply this refactor to AppSec as well if implemented.
|
|
@@ -41,84 +39,23 @@ module Datadog
|
|
|
41
39
|
# If the component is nil for some reason, we also don't have a
|
|
42
40
|
# logger instance to report the issue.
|
|
43
41
|
if component
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# TODO test exception capture
|
|
60
|
-
probe_manager.add_probe(probe)
|
|
61
|
-
content.applied
|
|
62
|
-
rescue DI::Error::DITargetNotInRegistry => exc
|
|
63
|
-
component.telemetry&.report(exc, description: "Line probe is targeting a loaded file that is not in code tracker")
|
|
64
|
-
|
|
65
|
-
payload = probe_notification_builder.build_errored(probe, exc)
|
|
66
|
-
probe_notifier_worker.add_status(payload)
|
|
67
|
-
|
|
68
|
-
# If a probe fails to install, we will mark the content
|
|
69
|
-
# as errored. On subsequent remote configuration application
|
|
70
|
-
# attemps, probe manager will raise the "previously errored"
|
|
71
|
-
# exception and we'll rescue it here, again marking the
|
|
72
|
-
# content as errored but with a somewhat different exception
|
|
73
|
-
# message.
|
|
74
|
-
# TODO assert content state (errored for this example)
|
|
75
|
-
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
76
|
-
rescue => exc
|
|
77
|
-
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
78
|
-
|
|
79
|
-
component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc}" }
|
|
80
|
-
component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
|
|
81
|
-
|
|
82
|
-
# TODO test this path
|
|
83
|
-
payload = probe_notification_builder.build_errored(probe, exc)
|
|
84
|
-
probe_notifier_worker.add_status(payload)
|
|
85
|
-
|
|
86
|
-
# If a probe fails to install, we will mark the content
|
|
87
|
-
# as errored. On subsequent remote configuration application
|
|
88
|
-
# attemps, probe manager will raise the "previously errored"
|
|
89
|
-
# exception and we'll rescue it here, again marking the
|
|
90
|
-
# content as errored but with a somewhat different exception
|
|
91
|
-
# message.
|
|
92
|
-
# TODO assert content state (errored for this example)
|
|
93
|
-
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Important: even if processing fails for this probe config,
|
|
97
|
-
# we need to note it as being current so that we do not
|
|
98
|
-
# try to remove instrumentation that is still supposed to be
|
|
99
|
-
# active.
|
|
100
|
-
current_probe_ids[probe_spec.fetch('id')] = true
|
|
101
|
-
rescue => exc
|
|
102
|
-
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
103
|
-
|
|
104
|
-
component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc}" }
|
|
105
|
-
component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
|
|
106
|
-
|
|
107
|
-
# TODO assert content state (errored for this example)
|
|
108
|
-
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
109
|
-
end
|
|
42
|
+
changes.each do |change|
|
|
43
|
+
case change.type
|
|
44
|
+
when :insert
|
|
45
|
+
add_probe(change.content, component)
|
|
46
|
+
when :update
|
|
47
|
+
# We do not implement updates at the moment, remove the
|
|
48
|
+
# probe and reinstall.
|
|
49
|
+
remove_probe(change.content, component)
|
|
50
|
+
add_probe(change.content, component)
|
|
51
|
+
when :delete
|
|
52
|
+
remove_probe(change.previous, component)
|
|
53
|
+
else
|
|
54
|
+
# This really should never happen since we generate the
|
|
55
|
+
# change types in the library.
|
|
56
|
+
component.logger.debug { "di: unrecognized change type: #{change.type}" }
|
|
110
57
|
end
|
|
111
58
|
end
|
|
112
|
-
|
|
113
|
-
begin
|
|
114
|
-
# TODO test exception capture
|
|
115
|
-
probe_manager.remove_other_probes(current_probe_ids.keys)
|
|
116
|
-
rescue => exc
|
|
117
|
-
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
118
|
-
|
|
119
|
-
component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
|
|
120
|
-
component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
|
|
121
|
-
end
|
|
122
59
|
end
|
|
123
60
|
end
|
|
124
61
|
end
|
|
@@ -130,14 +67,82 @@ module Datadog
|
|
|
130
67
|
|
|
131
68
|
private
|
|
132
69
|
|
|
133
|
-
def
|
|
134
|
-
|
|
70
|
+
def add_probe(content, component)
|
|
71
|
+
probe_spec = parse_content(content)
|
|
72
|
+
probe = component.parse_probe_spec_and_notify(probe_spec)
|
|
73
|
+
component.logger.debug { "di: received #{probe.type} probe at #{probe.location} (#{probe.id}) via RC" }
|
|
74
|
+
|
|
75
|
+
begin
|
|
76
|
+
# TODO test exception capture
|
|
77
|
+
component.probe_manager.add_probe(probe)
|
|
78
|
+
content.applied
|
|
79
|
+
rescue DI::Error::DITargetNotInRegistry => exc
|
|
80
|
+
component.telemetry&.report(exc, description: "Line probe is targeting a loaded file that is not in code tracker")
|
|
81
|
+
|
|
82
|
+
payload = component.probe_notification_builder.build_errored(probe, exc)
|
|
83
|
+
component.probe_notifier_worker.add_status(payload)
|
|
84
|
+
|
|
85
|
+
# If a probe fails to install, we will mark the content
|
|
86
|
+
# as errored. On subsequent remote configuration application
|
|
87
|
+
# attemps, probe manager will raise the "previously errored"
|
|
88
|
+
# exception and we'll rescue it here, again marking the
|
|
89
|
+
# content as errored but with a somewhat different exception
|
|
90
|
+
# message.
|
|
91
|
+
# TODO assert content state (errored for this example)
|
|
92
|
+
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
93
|
+
rescue => exc
|
|
94
|
+
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
95
|
+
|
|
96
|
+
component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc}" }
|
|
97
|
+
component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
|
|
98
|
+
|
|
99
|
+
# TODO test this path
|
|
100
|
+
payload = component.probe_notification_builder.build_errored(probe, exc)
|
|
101
|
+
component.probe_notifier_worker.add_status(payload)
|
|
102
|
+
|
|
103
|
+
# If a probe fails to install, we will mark the content
|
|
104
|
+
# as errored. On subsequent remote configuration application
|
|
105
|
+
# attemps, probe manager will raise the "previously errored"
|
|
106
|
+
# exception and we'll rescue it here, again marking the
|
|
107
|
+
# content as errored but with a somewhat different exception
|
|
108
|
+
# message.
|
|
109
|
+
# TODO assert content state (errored for this example)
|
|
110
|
+
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Important: even if processing fails for this probe config,
|
|
114
|
+
# we need to note it as being current so that we do not
|
|
115
|
+
# try to remove instrumentation that is still supposed to be
|
|
116
|
+
# active.
|
|
117
|
+
#current_probe_ids[probe_spec.fetch('id')] = true
|
|
118
|
+
rescue => exc
|
|
119
|
+
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
120
|
+
|
|
121
|
+
component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc}" }
|
|
122
|
+
component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
|
|
135
123
|
|
|
136
|
-
content
|
|
124
|
+
# TODO assert content state (errored for this example)
|
|
125
|
+
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
|
126
|
+
end
|
|
137
127
|
|
|
138
|
-
|
|
128
|
+
# This method does not mark +previous_content+ as succeeded or errored,
|
|
129
|
+
# because that content is from a previous RC response and has already
|
|
130
|
+
# been marked. Removal of probes happens when an RC entry disappears,
|
|
131
|
+
# as such there is nothing to mark.
|
|
132
|
+
def remove_probe(previous_content, component)
|
|
133
|
+
# TODO test exception capture
|
|
134
|
+
probe_spec = parse_content(previous_content)
|
|
135
|
+
probe_id = probe_spec.fetch('id')
|
|
136
|
+
component.probe_manager.remove_probe(probe_id)
|
|
137
|
+
rescue => exc
|
|
138
|
+
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
139
|
+
|
|
140
|
+
component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
|
|
141
|
+
component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
|
|
142
|
+
end
|
|
139
143
|
|
|
140
|
-
|
|
144
|
+
def parse_content(content)
|
|
145
|
+
JSON.parse(content.data)
|
|
141
146
|
end
|
|
142
147
|
end
|
|
143
148
|
end
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
3
5
|
require_relative '../../core/transport/parcel'
|
|
6
|
+
require_relative '../../core/transport/request'
|
|
7
|
+
require_relative '../../core/transport/transport'
|
|
4
8
|
require_relative 'http/diagnostics'
|
|
5
9
|
|
|
6
10
|
module Datadog
|
|
@@ -14,46 +18,14 @@ module Datadog
|
|
|
14
18
|
class Request < Datadog::Core::Transport::Request
|
|
15
19
|
end
|
|
16
20
|
|
|
17
|
-
class Transport
|
|
18
|
-
attr_reader :client, :apis, :default_api, :current_api_id, :logger
|
|
19
|
-
|
|
20
|
-
def initialize(apis, default_api, logger:)
|
|
21
|
-
@apis = apis
|
|
22
|
-
@logger = logger
|
|
23
|
-
|
|
24
|
-
@client = DI::Transport::HTTP::Diagnostics::Client.new(current_api, logger: logger)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def current_api
|
|
28
|
-
@apis[HTTP::API::DIAGNOSTICS]
|
|
29
|
-
end
|
|
30
|
-
|
|
21
|
+
class Transport < Core::Transport::Transport
|
|
31
22
|
def send_diagnostics(payload)
|
|
23
|
+
# TODO use transport encoder functionality?
|
|
32
24
|
json = JSON.dump(payload)
|
|
33
25
|
parcel = EncodedParcel.new(json)
|
|
34
26
|
request = Request.new(parcel)
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
unless response.ok?
|
|
38
|
-
# TODO Datadog::Core::Transport::InternalErrorResponse
|
|
39
|
-
# does not have +code+ method, what is the actual API of
|
|
40
|
-
# these response objects?
|
|
41
|
-
raise Error::AgentCommunicationError, "send_diagnostics failed: #{begin
|
|
42
|
-
response.code
|
|
43
|
-
rescue
|
|
44
|
-
"???"
|
|
45
|
-
end}: #{response.payload}"
|
|
46
|
-
end
|
|
47
|
-
rescue Error::AgentCommunicationError
|
|
48
|
-
raise
|
|
49
|
-
# Datadog::Core::Transport does not perform any exception mapping,
|
|
50
|
-
# therefore we could have any exception here from failure to parse
|
|
51
|
-
# agent URI for example.
|
|
52
|
-
# If we ever implement retries for network errors, we should distinguish
|
|
53
|
-
# actual network errors from non-network errors that are raised by
|
|
54
|
-
# transport code.
|
|
55
|
-
rescue => exc
|
|
56
|
-
raise Error::AgentCommunicationError, "send_diagnostics failed: #{exc.class}: #{exc}"
|
|
28
|
+
client.send_request(:diagnostics, request)
|
|
57
29
|
end
|
|
58
30
|
end
|
|
59
31
|
end
|