datadog 2.20.0 → 2.22.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 +73 -1
- data/README.md +0 -1
- data/ext/LIBDATADOG_DEVELOPMENT.md +60 -0
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/libdatadog_api/ddsketch.c +106 -0
- data/ext/libdatadog_api/init.c +3 -0
- data/ext/libdatadog_api/library_config.c +35 -27
- data/ext/libdatadog_api/process_discovery.c +24 -18
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
- data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
- data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
- data/lib/datadog/appsec/api_security/route_extractor.rb +6 -2
- data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
- data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
- data/lib/datadog/appsec/autoload.rb +1 -1
- data/lib/datadog/appsec/compressed_json.rb +1 -1
- data/lib/datadog/appsec/configuration/settings.rb +9 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
- data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
- data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
- data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
- data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
- data/lib/datadog/appsec/event.rb +12 -14
- data/lib/datadog/appsec/metrics/collector.rb +19 -3
- data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
- data/lib/datadog/appsec/remote.rb +25 -13
- data/lib/datadog/appsec/security_engine/result.rb +28 -9
- data/lib/datadog/appsec/security_engine/runner.rb +17 -7
- data/lib/datadog/appsec/security_event.rb +5 -7
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -4
- data/lib/datadog/core/configuration/components.rb +22 -8
- data/lib/datadog/core/configuration/config_helper.rb +100 -0
- data/lib/datadog/core/configuration/deprecations.rb +36 -0
- data/lib/datadog/core/configuration/ext.rb +0 -1
- data/lib/datadog/core/configuration/option.rb +38 -43
- data/lib/datadog/core/configuration/option_definition.rb +0 -9
- data/lib/datadog/core/configuration/options.rb +1 -5
- data/lib/datadog/core/configuration/settings.rb +10 -6
- data/lib/datadog/core/configuration/stable_config.rb +10 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +337 -0
- data/lib/datadog/core/configuration.rb +2 -2
- data/lib/datadog/core/ddsketch.rb +21 -0
- data/lib/datadog/core/deprecations.rb +2 -2
- data/lib/datadog/core/environment/ext.rb +0 -2
- data/lib/datadog/core/environment/git.rb +2 -2
- data/lib/datadog/core/environment/variable_helpers.rb +3 -3
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/metrics/client.rb +2 -2
- data/lib/datadog/core/pin.rb +4 -8
- data/lib/datadog/core/process_discovery/tracer_memfd.rb +2 -4
- data/lib/datadog/core/process_discovery.rb +48 -23
- data/lib/datadog/core/remote/component.rb +4 -6
- data/lib/datadog/core/runtime/ext.rb +0 -1
- data/lib/datadog/core/telemetry/component.rb +11 -0
- data/lib/datadog/core/telemetry/emitter.rb +6 -6
- data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
- data/lib/datadog/core/telemetry/event/app_started.rb +2 -2
- data/lib/datadog/core/telemetry/event.rb +1 -0
- data/lib/datadog/core/transport/response.rb +4 -1
- data/lib/datadog/core/utils/network.rb +19 -0
- data/lib/datadog/core.rb +2 -0
- data/lib/datadog/di/boot.rb +5 -3
- data/lib/datadog/di/component.rb +14 -0
- data/lib/datadog/di/context.rb +70 -0
- data/lib/datadog/di/el/compiler.rb +164 -0
- data/lib/datadog/di/el/evaluator.rb +159 -0
- data/lib/datadog/di/el/expression.rb +42 -0
- data/lib/datadog/di/el.rb +5 -0
- data/lib/datadog/di/error.rb +25 -0
- data/lib/datadog/di/instrumenter.rb +101 -32
- data/lib/datadog/di/probe.rb +35 -15
- data/lib/datadog/di/probe_builder.rb +39 -1
- data/lib/datadog/di/probe_file_loader.rb +1 -1
- data/lib/datadog/di/probe_manager.rb +3 -2
- data/lib/datadog/di/probe_notification_builder.rb +50 -51
- data/lib/datadog/di/serializer.rb +151 -7
- data/lib/datadog/opentelemetry/sdk/configurator.rb +1 -1
- data/lib/datadog/profiling/collectors/info.rb +1 -1
- data/lib/datadog/profiling/ext.rb +2 -1
- data/lib/datadog/profiling/http_transport.rb +1 -1
- data/lib/datadog/profiling/tasks/exec.rb +2 -2
- data/lib/datadog/tracing/component.rb +6 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
- data/lib/datadog/tracing/configuration/ext.rb +0 -3
- data/lib/datadog/tracing/configuration/settings.rb +15 -10
- data/lib/datadog/tracing/contrib/component.rb +2 -2
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
- data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +63 -27
- data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -0
- data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +7 -1
- data/lib/datadog/tracing/contrib/rails/ext.rb +2 -1
- data/lib/datadog/tracing/contrib/rails/integration.rb +1 -1
- data/lib/datadog/tracing/contrib/span_attribute_schema.rb +1 -1
- data/lib/datadog/tracing/metadata/ext.rb +8 -0
- data/lib/datadog/version.rb +1 -1
- metadata +25 -9
- data/ext/libdatadog_api/macos_development.md +0 -26
@@ -3,6 +3,7 @@
|
|
3
3
|
require_relative '../core/utils/time'
|
4
4
|
|
5
5
|
# rubocop:disable Lint/AssignmentInCondition
|
6
|
+
# rubocop:disable Style/AndOr
|
6
7
|
|
7
8
|
module Datadog
|
8
9
|
module DI
|
@@ -118,7 +119,26 @@ module Datadog
|
|
118
119
|
|
119
120
|
mod = Module.new do
|
120
121
|
define_method(method_name) do |*args, **kwargs, &target_block| # steep:ignore
|
121
|
-
|
122
|
+
continue = true
|
123
|
+
if condition = probe.condition
|
124
|
+
begin
|
125
|
+
# This context will be recreated later, unlike for line probes.
|
126
|
+
context = Context.new(
|
127
|
+
locals: serializer.combine_args(args, kwargs, self),
|
128
|
+
target_self: self,
|
129
|
+
probe: probe, settings: settings, serializer: serializer,
|
130
|
+
caller_locations: caller_locations,
|
131
|
+
)
|
132
|
+
continue = condition.satisfied?(context)
|
133
|
+
rescue
|
134
|
+
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
135
|
+
|
136
|
+
# TODO log / report via telemetry?
|
137
|
+
continue = false
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
if continue and rate_limiter.nil? || rate_limiter.allow?
|
122
142
|
# Arguments may be mutated by the method, therefore
|
123
143
|
# they need to be serialized prior to method invocation.
|
124
144
|
serialized_entry_args = if probe.capture_snapshot?
|
@@ -127,19 +147,29 @@ module Datadog
|
|
127
147
|
attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
|
128
148
|
end
|
129
149
|
start_time = Core::Utils::Time.get_time
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
150
|
+
|
151
|
+
rv = nil
|
152
|
+
begin
|
153
|
+
# Under Ruby 2.6 we cannot just call super(*args, **kwargs)
|
154
|
+
# for methods defined via method_missing.
|
155
|
+
rv = if args.any?
|
156
|
+
if kwargs.any?
|
157
|
+
super(*args, **kwargs, &target_block)
|
158
|
+
else
|
159
|
+
super(*args, &target_block)
|
160
|
+
end
|
161
|
+
elsif kwargs.any?
|
162
|
+
super(**kwargs, &target_block)
|
135
163
|
else
|
136
|
-
super(
|
164
|
+
super(&target_block)
|
137
165
|
end
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
166
|
+
rescue NoMemoryError, Interrupt, SystemExit
|
167
|
+
raise
|
168
|
+
rescue Exception => exc # standard:disable Lint/RescueException
|
169
|
+
# We will raise the exception captured here later, after
|
170
|
+
# the instrumentation callback runs.
|
142
171
|
end
|
172
|
+
|
143
173
|
duration = Core::Utils::Time.get_time - start_time
|
144
174
|
# The method itself is not part of the stack trace because
|
145
175
|
# we are getting the stack trace from outside of the method.
|
@@ -158,12 +188,20 @@ module Datadog
|
|
158
188
|
end
|
159
189
|
caller_locs = method_frame + caller_locations # steep:ignore
|
160
190
|
# TODO capture arguments at exit
|
191
|
+
|
192
|
+
context = Context.new(locals: nil, target_self: self,
|
193
|
+
probe: probe, settings: settings, serializer: serializer,
|
194
|
+
serialized_entry_args: serialized_entry_args,
|
195
|
+
caller_locations: caller_locs,
|
196
|
+
return_value: rv, duration: duration, exception: exc,)
|
197
|
+
|
161
198
|
# & is to stop steep complaints, block is always present here.
|
162
|
-
block&.call(
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
199
|
+
block&.call(context)
|
200
|
+
if exc
|
201
|
+
raise exc
|
202
|
+
else
|
203
|
+
rv
|
204
|
+
end
|
167
205
|
else
|
168
206
|
# stop standard from trying to mess up my code
|
169
207
|
_ = 42
|
@@ -307,27 +345,57 @@ module Datadog
|
|
307
345
|
# are invoked for *each* line of Ruby executed.
|
308
346
|
# TODO find out exactly when the path in trace point is relative.
|
309
347
|
# Looks like this is the case when line trace point is not targeted?
|
310
|
-
|
348
|
+
continue = iseq || tp.lineno == probe.line_no && (
|
311
349
|
probe.file == tp.path || probe.file_matches?(tp.path)
|
312
350
|
)
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
351
|
+
|
352
|
+
# We set the trace point on :return to be able to instrument
|
353
|
+
# 'end' lines. This also causes the trace point to be invoked on
|
354
|
+
# non-'end' lines when a line raises an exception, since the
|
355
|
+
# exception causes the method to stop executing and stack unwends.
|
356
|
+
# We do not want two invocations of the trace point.
|
357
|
+
# Therefore, if a trace point is invoked with a :line event,
|
358
|
+
# mark it as such and ignore subsequent :return events.
|
359
|
+
continue &&= if probe.executed_on_line?
|
360
|
+
tp.event == :line
|
361
|
+
else
|
362
|
+
if tp.event == :line
|
363
|
+
probe.executed_on_line!
|
364
|
+
end
|
365
|
+
true
|
366
|
+
end
|
367
|
+
|
368
|
+
if continue
|
369
|
+
if condition = probe.condition
|
370
|
+
context = Context.new(
|
371
|
+
locals: Instrumenter.get_local_variables(tp),
|
327
372
|
target_self: tp.self,
|
328
|
-
|
373
|
+
probe: probe, settings: settings, serializer: serializer,
|
374
|
+
path: tp.path,
|
375
|
+
caller_locations: caller_locations,
|
376
|
+
)
|
377
|
+
continue = condition.satisfied?(context)
|
329
378
|
end
|
330
379
|
end
|
380
|
+
|
381
|
+
continue &&= rate_limiter.nil? || rate_limiter.allow? # standard:disable Style/AndOr
|
382
|
+
|
383
|
+
if continue
|
384
|
+
# The context creation is relatively expensive and we don't
|
385
|
+
# want to run it if the callback won't be executed due to the
|
386
|
+
# rate limit.
|
387
|
+
# Thus the copy-paste of the creation call here.
|
388
|
+
context ||= Context.new(
|
389
|
+
locals: Instrumenter.get_local_variables(tp),
|
390
|
+
target_self: tp.self,
|
391
|
+
probe: probe, settings: settings, serializer: serializer,
|
392
|
+
path: tp.path,
|
393
|
+
caller_locations: caller_locations,
|
394
|
+
)
|
395
|
+
|
396
|
+
# & is to stop steep complaints, block is always present here.
|
397
|
+
block&.call(context)
|
398
|
+
end
|
331
399
|
rescue => exc
|
332
400
|
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
333
401
|
logger.debug { "di: unhandled exception in line trace point: #{exc.class}: #{exc}" }
|
@@ -449,3 +517,4 @@ module Datadog
|
|
449
517
|
end
|
450
518
|
|
451
519
|
# rubocop:enable Lint/AssignmentInCondition
|
520
|
+
# rubocop:enable Style/AndOr
|
data/lib/datadog/di/probe.rb
CHANGED
@@ -17,7 +17,7 @@ module Datadog
|
|
17
17
|
# and remote config code must be prepared to deal with exceptions
|
18
18
|
# raised by Probe constructor in particular. Therefore, Probe constructor
|
19
19
|
# will raise an exception if it determines that there is not enough
|
20
|
-
# information (or
|
20
|
+
# information (or conflicting information) in the arguments to create a
|
21
21
|
# functional probe, and upstream code is tasked with not spamming logs
|
22
22
|
# with notifications of such errors (and potentially limiting the
|
23
23
|
# attempts to construct probe from a given payload).
|
@@ -36,8 +36,9 @@ module Datadog
|
|
36
36
|
|
37
37
|
def initialize(id:, type:,
|
38
38
|
file: nil, line_no: nil, type_name: nil, method_name: nil,
|
39
|
-
template: nil,
|
40
|
-
|
39
|
+
template: nil, template_segments: nil,
|
40
|
+
capture_snapshot: false, max_capture_depth: nil,
|
41
|
+
max_capture_attribute_count: nil, condition: nil,
|
41
42
|
rate_limit: nil)
|
42
43
|
# Perform some sanity checks here to detect unexpected attribute
|
43
44
|
# combinations, in order to not do them in subsequent code.
|
@@ -45,9 +46,17 @@ module Datadog
|
|
45
46
|
raise ArgumentError, "Unknown probe type: #{type}"
|
46
47
|
end
|
47
48
|
|
48
|
-
if
|
49
|
-
|
50
|
-
|
49
|
+
# Probe should be inferred to be a line probe if the specification
|
50
|
+
# contains a line number. This how Java tracer works and Go tracer
|
51
|
+
# is implementing the same behavior, and Go will have all 3 fields
|
52
|
+
# (file path, line number and method name) for line probes.
|
53
|
+
# Do not raise if line number and method name both exist - instead
|
54
|
+
# treat the probe as a line probe.
|
55
|
+
#
|
56
|
+
# In the future we want to provide type name and method name to line
|
57
|
+
# probes, so that the library can verify that the instrumented line
|
58
|
+
# is in the method that the frontend showed to the user when the
|
59
|
+
# user created the probe.
|
51
60
|
|
52
61
|
if line_no && !file
|
53
62
|
raise ArgumentError, "Probe contains line number but not file: #{id}"
|
@@ -57,6 +66,10 @@ module Datadog
|
|
57
66
|
raise ArgumentError, "Partial method probe definition: #{id}"
|
58
67
|
end
|
59
68
|
|
69
|
+
if line_no.nil? && method_name.nil?
|
70
|
+
raise ArgumentError, "Unhandled probe type: neither method nor line probe: #{id}"
|
71
|
+
end
|
72
|
+
|
60
73
|
@id = id
|
61
74
|
@type = type
|
62
75
|
@file = file
|
@@ -64,17 +77,11 @@ module Datadog
|
|
64
77
|
@type_name = type_name
|
65
78
|
@method_name = method_name
|
66
79
|
@template = template
|
80
|
+
@template_segments = template_segments
|
67
81
|
@capture_snapshot = !!capture_snapshot
|
68
82
|
@max_capture_depth = max_capture_depth
|
69
83
|
@max_capture_attribute_count = max_capture_attribute_count
|
70
|
-
|
71
|
-
# These checks use instance methods that have more complex logic
|
72
|
-
# than checking a single argument value. To avoid duplicating
|
73
|
-
# the logic here, use the methods and perform these checks after
|
74
|
-
# instance variable assignment.
|
75
|
-
unless method? || line?
|
76
|
-
raise ArgumentError, "Unhandled probe type: neither method nor line probe: #{id}"
|
77
|
-
end
|
84
|
+
@condition = condition
|
78
85
|
|
79
86
|
@rate_limit = rate_limit || (@capture_snapshot ? 1 : 5000)
|
80
87
|
@rate_limiter = Datadog::Core::TokenBucket.new(@rate_limit)
|
@@ -89,6 +96,10 @@ module Datadog
|
|
89
96
|
attr_reader :type_name
|
90
97
|
attr_reader :method_name
|
91
98
|
attr_reader :template
|
99
|
+
attr_reader :template_segments
|
100
|
+
|
101
|
+
# The compiled condition for the probe, as a String.
|
102
|
+
attr_reader :condition
|
92
103
|
|
93
104
|
# Configured maximum capture depth. Can be nil in which case
|
94
105
|
# the global default will be used.
|
@@ -122,7 +133,7 @@ module Datadog
|
|
122
133
|
|
123
134
|
# Returns whether the probe is a method probe.
|
124
135
|
def method?
|
125
|
-
|
136
|
+
line_no.nil?
|
126
137
|
end
|
127
138
|
|
128
139
|
# Returns the line number associated with the probe, raising
|
@@ -186,6 +197,15 @@ module Datadog
|
|
186
197
|
def emitting_notified?
|
187
198
|
!!@emitting_notified
|
188
199
|
end
|
200
|
+
|
201
|
+
def executed_on_line?
|
202
|
+
!!@executed_on_line
|
203
|
+
end
|
204
|
+
|
205
|
+
def executed_on_line!
|
206
|
+
# TODO lock?
|
207
|
+
@executed_on_line = true
|
208
|
+
end
|
189
209
|
end
|
190
210
|
end
|
191
211
|
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# rubocop:disable Lint/AssignmentInCondition
|
4
|
+
|
3
5
|
require_relative "probe"
|
6
|
+
require_relative 'el'
|
4
7
|
|
5
8
|
module Datadog
|
6
9
|
module DI
|
@@ -21,10 +24,19 @@ module Datadog
|
|
21
24
|
'LOG_PROBE' => :log,
|
22
25
|
}.freeze
|
23
26
|
|
24
|
-
module_function
|
27
|
+
module_function
|
28
|
+
|
29
|
+
def build_from_remote_config(config)
|
25
30
|
# The validations here are not yet comprehensive.
|
26
31
|
type = config.fetch('type')
|
27
32
|
type_symbol = PROBE_TYPES[type] or raise ArgumentError, "Unrecognized probe type: #{type}"
|
33
|
+
cond = if cond_spec = config['when']
|
34
|
+
unless cond_spec['dsl'] && cond_spec['json']
|
35
|
+
raise ArgumentError, "Malformed condition specification for probe: #{config}"
|
36
|
+
end
|
37
|
+
compiled = EL::Compiler.new.compile(cond_spec['json'])
|
38
|
+
EL::Expression.new(cond_spec['dsl'], compiled)
|
39
|
+
end
|
28
40
|
Probe.new(
|
29
41
|
id: config.fetch("id"),
|
30
42
|
type: type_symbol,
|
@@ -34,15 +46,41 @@ module Datadog
|
|
34
46
|
line_no: config["where"]&.[]("lines")&.compact&.map(&:to_i)&.first,
|
35
47
|
type_name: config["where"]&.[]("typeName"),
|
36
48
|
method_name: config["where"]&.[]("methodName"),
|
49
|
+
# We should not be using the template for anything - we instead
|
50
|
+
# use +segments+ - but keep the template for debugging.
|
37
51
|
template: config["template"],
|
52
|
+
template_segments: build_template_segments(config['segments']),
|
38
53
|
capture_snapshot: !!config["captureSnapshot"],
|
39
54
|
max_capture_depth: config["capture"]&.[]("maxReferenceDepth"),
|
40
55
|
max_capture_attribute_count: config["capture"]&.[]("maxFieldCount"),
|
41
56
|
rate_limit: config["sampling"]&.[]("snapshotsPerSecond"),
|
57
|
+
condition: cond,
|
42
58
|
)
|
43
59
|
rescue KeyError => exc
|
44
60
|
raise ArgumentError, "Malformed remote configuration entry for probe: #{exc.class}: #{exc}: #{config}"
|
45
61
|
end
|
62
|
+
|
63
|
+
def build_template_segments(segments)
|
64
|
+
segments&.map do |segment|
|
65
|
+
if Hash === segment
|
66
|
+
if str = segment['str']
|
67
|
+
str
|
68
|
+
elsif ast = segment['json']
|
69
|
+
unless dsl = segment['dsl']
|
70
|
+
raise ArgumentError, "Missing dsl for json in segment: #{segment}"
|
71
|
+
end
|
72
|
+
compiled = EL::Compiler.new.compile(ast)
|
73
|
+
EL::Expression.new(dsl, compiled)
|
74
|
+
else
|
75
|
+
# TODO report to telemetry?
|
76
|
+
end
|
77
|
+
else
|
78
|
+
# TODO report to telemetry?
|
79
|
+
end
|
80
|
+
end&.compact
|
81
|
+
end
|
46
82
|
end
|
47
83
|
end
|
48
84
|
end
|
85
|
+
|
86
|
+
# rubocop:enable Lint/AssignmentInCondition
|
@@ -20,7 +20,7 @@ module Datadog
|
|
20
20
|
module_function def load_now
|
21
21
|
should_propagate = false
|
22
22
|
|
23
|
-
probe_file_path =
|
23
|
+
probe_file_path = DATADOG_ENV['DD_DYNAMIC_INSTRUMENTATION_PROBE_FILE']
|
24
24
|
if probe_file_path.nil? || probe_file_path.empty?
|
25
25
|
return
|
26
26
|
end
|
@@ -229,7 +229,8 @@ module Datadog
|
|
229
229
|
# This method is responsible for queueing probe status to be sent to the
|
230
230
|
# backend (once per the probe's lifetime) and a snapshot corresponding
|
231
231
|
# to the current invocation.
|
232
|
-
def probe_executed_callback(
|
232
|
+
def probe_executed_callback(context)
|
233
|
+
probe = context.probe
|
233
234
|
logger.trace { "di: executed #{probe.type} probe at #{probe.location} (#{probe.id})" }
|
234
235
|
unless probe.emitting_notified?
|
235
236
|
payload = probe_notification_builder.build_emitting(probe)
|
@@ -237,7 +238,7 @@ module Datadog
|
|
237
238
|
probe.emitting_notified = true
|
238
239
|
end
|
239
240
|
|
240
|
-
payload = probe_notification_builder.build_executed(
|
241
|
+
payload = probe_notification_builder.build_executed(context)
|
241
242
|
probe_notifier_worker.add_snapshot(payload)
|
242
243
|
end
|
243
244
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# rubocop:disable Lint/AssignmentInCondition
|
4
|
+
|
3
5
|
module Datadog
|
4
6
|
module DI
|
5
7
|
# Builds probe status notification and snapshot payloads.
|
@@ -40,32 +42,17 @@ module Datadog
|
|
40
42
|
|
41
43
|
# Duration is in seconds.
|
42
44
|
# path is the actual path of the instrumented file.
|
43
|
-
def build_executed(
|
44
|
-
|
45
|
-
serialized_locals: nil, args: nil, kwargs: nil, target_self: nil,
|
46
|
-
serialized_entry_args: nil)
|
47
|
-
build_snapshot(probe, rv: rv, serialized_locals: serialized_locals,
|
48
|
-
# Actual path of the instrumented file.
|
49
|
-
path: path,
|
50
|
-
duration: duration,
|
51
|
-
# TODO check how many stack frames we should be keeping/sending,
|
52
|
-
# this should be all frames for enriched probes and no frames for
|
53
|
-
# non-enriched probes?
|
54
|
-
caller_locations: caller_locations,
|
55
|
-
args: args, kwargs: kwargs,
|
56
|
-
target_self: target_self,
|
57
|
-
serialized_entry_args: serialized_entry_args)
|
45
|
+
def build_executed(context)
|
46
|
+
build_snapshot(context)
|
58
47
|
end
|
59
48
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
serialized_entry_args: nil)
|
68
|
-
if probe.capture_snapshot? && !target_self
|
49
|
+
NANOSECONDS = 10**9
|
50
|
+
MILLISECONDS = 1000
|
51
|
+
|
52
|
+
def build_snapshot(context)
|
53
|
+
probe = context.probe
|
54
|
+
|
55
|
+
if probe.capture_snapshot? && !context.target_self
|
69
56
|
raise ArgumentError, "Asked to build snapshot with snapshot capture but target_self is nil"
|
70
57
|
end
|
71
58
|
|
@@ -74,22 +61,14 @@ module Datadog
|
|
74
61
|
captures = if probe.capture_snapshot?
|
75
62
|
if probe.method?
|
76
63
|
return_arguments = {
|
77
|
-
"@return": serializer.serialize_value(
|
64
|
+
"@return": serializer.serialize_value(context.return_value,
|
78
65
|
depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
|
79
66
|
attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count),
|
80
|
-
self: serializer.serialize_value(target_self),
|
67
|
+
self: serializer.serialize_value(context.target_self),
|
81
68
|
}
|
82
69
|
{
|
83
70
|
entry: {
|
84
|
-
|
85
|
-
arguments: if serialized_entry_args
|
86
|
-
serialized_entry_args
|
87
|
-
else
|
88
|
-
(args || kwargs) && serializer.serialize_args(args, kwargs, target_self,
|
89
|
-
depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
|
90
|
-
attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
|
91
|
-
end,
|
92
|
-
# standard:enable all
|
71
|
+
arguments: context.serialized_entry_args,
|
93
72
|
},
|
94
73
|
return: {
|
95
74
|
arguments: return_arguments,
|
@@ -98,10 +77,10 @@ module Datadog
|
|
98
77
|
}
|
99
78
|
elsif probe.line?
|
100
79
|
{
|
101
|
-
lines: serialized_locals && {
|
80
|
+
lines: (locals = context.serialized_locals) && {
|
102
81
|
probe.line_no => {
|
103
|
-
locals:
|
104
|
-
arguments: {self: serializer.serialize_value(target_self)},
|
82
|
+
locals: locals,
|
83
|
+
arguments: {self: serializer.serialize_value(context.target_self)},
|
105
84
|
},
|
106
85
|
},
|
107
86
|
}
|
@@ -110,7 +89,7 @@ module Datadog
|
|
110
89
|
|
111
90
|
location = if probe.line?
|
112
91
|
{
|
113
|
-
file: path,
|
92
|
+
file: context.path,
|
114
93
|
lines: [probe.line_no],
|
115
94
|
}
|
116
95
|
elsif probe.method?
|
@@ -120,17 +99,23 @@ module Datadog
|
|
120
99
|
}
|
121
100
|
end
|
122
101
|
|
123
|
-
stack = if caller_locations
|
102
|
+
stack = if caller_locations = context.caller_locations
|
124
103
|
format_caller_locations(caller_locations)
|
125
104
|
end
|
126
105
|
|
127
106
|
timestamp = timestamp_now
|
107
|
+
message = nil
|
108
|
+
evaluation_errors = []
|
109
|
+
if segments = probe.template_segments
|
110
|
+
message, evaluation_errors = evaluate_template(segments, context)
|
111
|
+
end
|
112
|
+
duration = context.duration
|
128
113
|
{
|
129
114
|
service: settings.service,
|
130
115
|
"debugger.snapshot": {
|
131
116
|
id: SecureRandom.uuid,
|
132
117
|
timestamp: timestamp,
|
133
|
-
evaluationErrors:
|
118
|
+
evaluationErrors: evaluation_errors,
|
134
119
|
probe: {
|
135
120
|
id: probe.id,
|
136
121
|
version: 0,
|
@@ -143,7 +128,7 @@ module Datadog
|
|
143
128
|
},
|
144
129
|
# In python tracer duration is under debugger.snapshot,
|
145
130
|
# but UI appears to expect it here at top level.
|
146
|
-
duration: duration ? (duration *
|
131
|
+
duration: duration ? (duration * NANOSECONDS).to_i : 0,
|
147
132
|
host: nil,
|
148
133
|
logger: {
|
149
134
|
name: probe.file,
|
@@ -160,8 +145,7 @@ module Datadog
|
|
160
145
|
"dd.trace_id": active_trace&.id&.to_s,
|
161
146
|
"dd.span_id": active_span&.id&.to_s,
|
162
147
|
ddsource: 'dd_debugger',
|
163
|
-
message:
|
164
|
-
duration: duration ? duration * 1000 : 0),
|
148
|
+
message: message,
|
165
149
|
timestamp: timestamp,
|
166
150
|
}
|
167
151
|
end
|
@@ -192,16 +176,29 @@ module Datadog
|
|
192
176
|
end
|
193
177
|
end
|
194
178
|
|
195
|
-
def evaluate_template(
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
179
|
+
def evaluate_template(template_segments, context)
|
180
|
+
evaluation_errors = []
|
181
|
+
message = template_segments.map do |segment|
|
182
|
+
case segment
|
183
|
+
when String
|
184
|
+
segment
|
185
|
+
when EL::Expression
|
186
|
+
serializer.serialize_value_for_message(segment.evaluate(context))
|
187
|
+
else
|
188
|
+
raise ArgumentError, "Invalid template segment type: #{segment}"
|
189
|
+
end
|
190
|
+
rescue => exc
|
191
|
+
evaluation_errors << {
|
192
|
+
message: "#{exc.class}: #{exc}",
|
193
|
+
expr: segment.dsl_expr,
|
194
|
+
}
|
195
|
+
'[evaluation error]'
|
196
|
+
end.join
|
197
|
+
[message, evaluation_errors]
|
201
198
|
end
|
202
199
|
|
203
200
|
def timestamp_now
|
204
|
-
(Core::Utils::Time.now.to_f *
|
201
|
+
(Core::Utils::Time.now.to_f * MILLISECONDS).to_i
|
205
202
|
end
|
206
203
|
|
207
204
|
def active_trace
|
@@ -218,3 +215,5 @@ module Datadog
|
|
218
215
|
end
|
219
216
|
end
|
220
217
|
end
|
218
|
+
|
219
|
+
# rubocop:enable Lint/AssignmentInCondition
|