datadog 2.21.0 → 2.23.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 +106 -2
- data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_stack.c +4 -0
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
- data/ext/datadog_profiling_native_extension/extconf.rb +6 -4
- data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
- data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
- data/ext/libdatadog_api/ddsketch.c +106 -0
- data/ext/libdatadog_api/feature_flags.c +554 -0
- data/ext/libdatadog_api/feature_flags.h +5 -0
- data/ext/libdatadog_api/init.c +5 -0
- data/ext/libdatadog_api/library_config.c +34 -25
- data/ext/libdatadog_api/process_discovery.c +19 -13
- 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 +23 -6
- data/lib/datadog/appsec/api_security/sampler.rb +7 -4
- data/lib/datadog/appsec/assets/blocked.html +8 -0
- data/lib/datadog/appsec/assets/blocked.json +1 -1
- data/lib/datadog/appsec/assets/blocked.text +3 -1
- 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/assets.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 +29 -13
- data/lib/datadog/appsec/response.rb +18 -4
- 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/components.rb +44 -9
- data/lib/datadog/core/configuration/config_helper.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +14 -0
- data/lib/datadog/core/configuration/stable_config.rb +10 -0
- data/lib/datadog/core/configuration/supported_configurations.rb +330 -299
- data/lib/datadog/core/configuration.rb +1 -1
- data/lib/datadog/core/ddsketch.rb +19 -0
- data/lib/datadog/core/environment/ext.rb +6 -0
- data/lib/datadog/core/environment/process.rb +79 -0
- data/lib/datadog/core/environment/yjit.rb +2 -1
- data/lib/datadog/core/feature_flags.rb +61 -0
- data/lib/datadog/core/pin.rb +4 -8
- data/lib/datadog/core/process_discovery.rb +4 -2
- data/lib/datadog/core/remote/client/capabilities.rb +7 -0
- data/lib/datadog/core/remote/component.rb +4 -6
- data/lib/datadog/core/remote/transport/config.rb +2 -10
- data/lib/datadog/core/remote/transport/http/config.rb +9 -9
- data/lib/datadog/core/remote/transport/http/negotiation.rb +17 -8
- data/lib/datadog/core/remote/transport/http.rb +2 -0
- data/lib/datadog/core/remote/transport/negotiation.rb +2 -18
- data/lib/datadog/core/remote/worker.rb +25 -37
- data/lib/datadog/core/tag_builder.rb +0 -4
- data/lib/datadog/core/tag_normalizer.rb +84 -0
- data/lib/datadog/core/telemetry/component.rb +18 -3
- 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 +52 -49
- data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
- data/lib/datadog/core/telemetry/event.rb +1 -0
- data/lib/datadog/core/telemetry/logger.rb +2 -2
- data/lib/datadog/core/telemetry/logging.rb +2 -8
- data/lib/datadog/core/telemetry/transport/http/telemetry.rb +5 -6
- data/lib/datadog/core/telemetry/transport/telemetry.rb +1 -2
- data/lib/datadog/core/transport/http/client.rb +69 -0
- data/lib/datadog/core/transport/response.rb +4 -1
- data/lib/datadog/core/utils/array.rb +29 -0
- data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
- data/lib/datadog/core/utils/network.rb +22 -1
- data/lib/datadog/core/utils/only_once_successful.rb +6 -2
- data/lib/datadog/core/utils.rb +2 -0
- data/lib/datadog/data_streams/configuration/settings.rb +49 -0
- data/lib/datadog/data_streams/configuration.rb +11 -0
- data/lib/datadog/data_streams/ext.rb +11 -0
- data/lib/datadog/data_streams/extensions.rb +16 -0
- data/lib/datadog/data_streams/pathway_context.rb +169 -0
- data/lib/datadog/data_streams/processor.rb +509 -0
- data/lib/datadog/data_streams/transport/http/api.rb +33 -0
- data/lib/datadog/data_streams/transport/http/client.rb +21 -0
- data/lib/datadog/data_streams/transport/http/stats.rb +87 -0
- data/lib/datadog/data_streams/transport/http.rb +41 -0
- data/lib/datadog/data_streams/transport/stats.rb +60 -0
- data/lib/datadog/data_streams.rb +100 -0
- data/lib/datadog/di/boot.rb +1 -0
- data/lib/datadog/di/component.rb +14 -16
- 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 +29 -0
- data/lib/datadog/di/instrumenter.rb +163 -48
- data/lib/datadog/di/probe.rb +55 -15
- data/lib/datadog/di/probe_builder.rb +39 -1
- data/lib/datadog/di/probe_manager.rb +13 -4
- data/lib/datadog/di/probe_notification_builder.rb +105 -67
- data/lib/datadog/di/proc_responder.rb +32 -0
- data/lib/datadog/di/serializer.rb +151 -7
- data/lib/datadog/di/transport/diagnostics.rb +2 -2
- data/lib/datadog/di/transport/http/diagnostics.rb +2 -4
- data/lib/datadog/di/transport/http/input.rb +2 -4
- data/lib/datadog/di/transport/http.rb +6 -2
- data/lib/datadog/di/transport/input.rb +64 -4
- data/lib/datadog/open_feature/component.rb +60 -0
- data/lib/datadog/open_feature/configuration.rb +27 -0
- data/lib/datadog/open_feature/evaluation_engine.rb +69 -0
- data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
- data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
- data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
- data/lib/datadog/open_feature/exposures/event.rb +60 -0
- data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
- data/lib/datadog/open_feature/exposures/worker.rb +116 -0
- data/lib/datadog/open_feature/ext.rb +14 -0
- data/lib/datadog/open_feature/native_evaluator.rb +38 -0
- data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
- data/lib/datadog/open_feature/provider.rb +141 -0
- data/lib/datadog/open_feature/remote.rb +74 -0
- data/lib/datadog/open_feature/resolution_details.rb +35 -0
- data/lib/datadog/open_feature/transport.rb +72 -0
- data/lib/datadog/open_feature.rb +19 -0
- data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
- data/lib/datadog/opentelemetry/metrics.rb +110 -0
- data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
- data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +38 -0
- data/lib/datadog/opentelemetry.rb +3 -0
- data/lib/datadog/profiling/collectors/code_provenance.rb +15 -6
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
- data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
- data/lib/datadog/profiling/profiler.rb +4 -0
- data/lib/datadog/profiling/tag_builder.rb +36 -3
- data/lib/datadog/profiling.rb +1 -2
- data/lib/datadog/single_step_instrument.rb +1 -1
- data/lib/datadog/tracing/component.rb +6 -17
- data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
- data/lib/datadog/tracing/configuration/ext.rb +9 -0
- data/lib/datadog/tracing/configuration/settings.rb +77 -3
- data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
- data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
- data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
- data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
- data/lib/datadog/tracing/contrib/component.rb +2 -2
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
- data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
- data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
- 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 +74 -44
- data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
- data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
- data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
- data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
- data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
- data/lib/datadog/tracing/contrib/karafka/patcher.rb +32 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
- data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
- data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
- data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
- data/lib/datadog/tracing/contrib/status_range_matcher.rb +7 -0
- data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
- data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
- data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
- data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
- data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +46 -0
- data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
- data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/metadata/ext.rb +9 -1
- data/lib/datadog/tracing/transport/http/client.rb +12 -26
- data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
- data/lib/datadog/tracing/transport/traces.rb +3 -5
- data/lib/datadog/version.rb +2 -2
- data/lib/datadog.rb +2 -0
- metadata +92 -16
- data/ext/libdatadog_api/macos_development.md +0 -26
- data/lib/datadog/core/remote/transport/http/client.rb +0 -49
- data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
- data/lib/datadog/di/transport/http/client.rb +0 -47
data/lib/datadog/di/error.rb
CHANGED
|
@@ -10,6 +10,10 @@ module Datadog
|
|
|
10
10
|
#
|
|
11
11
|
# @api private
|
|
12
12
|
class Error < StandardError
|
|
13
|
+
# Internal Dynamic Instrumentation error ("should never happen").
|
|
14
|
+
class InternalError < Error
|
|
15
|
+
end
|
|
16
|
+
|
|
13
17
|
# Probe does not contain a line number (i.e., is not a line probe).
|
|
14
18
|
class MissingLineNumber < Error
|
|
15
19
|
end
|
|
@@ -48,6 +52,31 @@ module Datadog
|
|
|
48
52
|
# and the user will need to make their suffix more precise.
|
|
49
53
|
class MultiplePathsMatch < Error
|
|
50
54
|
end
|
|
55
|
+
|
|
56
|
+
# Base class for exceptions arising during expression language AST
|
|
57
|
+
# compilation into Ruby code.
|
|
58
|
+
#
|
|
59
|
+
# Expression language does not specify behavior in all cases,
|
|
60
|
+
# leaving some choices to the language implementation in the tracers.
|
|
61
|
+
# It is therefore possible that some technically valid expressions are
|
|
62
|
+
# prohibited by our implementation.
|
|
63
|
+
#
|
|
64
|
+
# It is also possible that the sanitizers/validators prohibit some
|
|
65
|
+
# esoteric constructs that are technically valid in Ruby,
|
|
66
|
+
# for example if instance variable name rules are relaxed to allow
|
|
67
|
+
# arbitrary characters in them as permitted in method names.
|
|
68
|
+
class InvalidExpression < Error
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Variable name with invalid characters in an expression language
|
|
72
|
+
# expression.
|
|
73
|
+
class BadVariableName < InvalidExpression
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Base class for exceptions arising when evaluating expression language
|
|
77
|
+
# expressions.
|
|
78
|
+
class ExpressionEvaluationError < Error
|
|
79
|
+
end
|
|
51
80
|
end
|
|
52
81
|
end
|
|
53
82
|
end
|
|
@@ -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
|
|
@@ -88,11 +89,7 @@ module Datadog
|
|
|
88
89
|
# from the method but from outside of the method).
|
|
89
90
|
Location = Struct.new(:path, :lineno, :label)
|
|
90
91
|
|
|
91
|
-
def hook_method(probe,
|
|
92
|
-
unless block
|
|
93
|
-
raise ArgumentError, 'block is required'
|
|
94
|
-
end
|
|
95
|
-
|
|
92
|
+
def hook_method(probe, responder)
|
|
96
93
|
lock.synchronize do
|
|
97
94
|
if probe.instrumentation_module
|
|
98
95
|
# Already instrumented, warn?
|
|
@@ -118,7 +115,50 @@ module Datadog
|
|
|
118
115
|
|
|
119
116
|
mod = Module.new do
|
|
120
117
|
define_method(method_name) do |*args, **kwargs, &target_block| # steep:ignore
|
|
121
|
-
|
|
118
|
+
continue = true
|
|
119
|
+
if condition = probe.condition
|
|
120
|
+
begin
|
|
121
|
+
# This context will be recreated later, unlike for line probes.
|
|
122
|
+
context = Context.new(
|
|
123
|
+
locals: serializer.combine_args(args, kwargs, self),
|
|
124
|
+
target_self: self,
|
|
125
|
+
probe: probe, settings: settings, serializer: serializer,
|
|
126
|
+
caller_locations: caller_locations,
|
|
127
|
+
)
|
|
128
|
+
continue = condition.satisfied?(context)
|
|
129
|
+
rescue => exc
|
|
130
|
+
# Evaluation error exception can be raised for "expected"
|
|
131
|
+
# errors, we probably need another setting to control whether
|
|
132
|
+
# these exceptions are propagated.
|
|
133
|
+
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions &&
|
|
134
|
+
!exc.is_a?(DI::Error::ExpressionEvaluationError)
|
|
135
|
+
|
|
136
|
+
if context
|
|
137
|
+
# We want to report evaluation errors for conditions
|
|
138
|
+
# as probe snapshots. However, if we failed to create
|
|
139
|
+
# the context, we won't be able to report anything as
|
|
140
|
+
# the probe notifier builder requires a context.
|
|
141
|
+
begin
|
|
142
|
+
responder.probe_condition_evaluation_failed_callback(context, exc)
|
|
143
|
+
rescue
|
|
144
|
+
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
145
|
+
|
|
146
|
+
# TODO log / report via telemetry?
|
|
147
|
+
end
|
|
148
|
+
else
|
|
149
|
+
_ = 42 # stop standard from wrecking this code
|
|
150
|
+
|
|
151
|
+
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
152
|
+
|
|
153
|
+
# TODO log / report via telemetry?
|
|
154
|
+
# If execution gets here, there is probably a bug in the tracer.
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
continue = false
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
if continue and rate_limiter.nil? || rate_limiter.allow?
|
|
122
162
|
# Arguments may be mutated by the method, therefore
|
|
123
163
|
# they need to be serialized prior to method invocation.
|
|
124
164
|
serialized_entry_args = if probe.capture_snapshot?
|
|
@@ -127,19 +167,29 @@ module Datadog
|
|
|
127
167
|
attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
|
|
128
168
|
end
|
|
129
169
|
start_time = Core::Utils::Time.get_time
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
170
|
+
|
|
171
|
+
rv = nil
|
|
172
|
+
begin
|
|
173
|
+
# Under Ruby 2.6 we cannot just call super(*args, **kwargs)
|
|
174
|
+
# for methods defined via method_missing.
|
|
175
|
+
rv = if args.any?
|
|
176
|
+
if kwargs.any?
|
|
177
|
+
super(*args, **kwargs, &target_block)
|
|
178
|
+
else
|
|
179
|
+
super(*args, &target_block)
|
|
180
|
+
end
|
|
181
|
+
elsif kwargs.any?
|
|
182
|
+
super(**kwargs, &target_block)
|
|
135
183
|
else
|
|
136
|
-
super(
|
|
184
|
+
super(&target_block)
|
|
137
185
|
end
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
186
|
+
rescue NoMemoryError, Interrupt, SystemExit
|
|
187
|
+
raise
|
|
188
|
+
rescue Exception => exc # standard:disable Lint/RescueException
|
|
189
|
+
# We will raise the exception captured here later, after
|
|
190
|
+
# the instrumentation callback runs.
|
|
142
191
|
end
|
|
192
|
+
|
|
143
193
|
duration = Core::Utils::Time.get_time - start_time
|
|
144
194
|
# The method itself is not part of the stack trace because
|
|
145
195
|
# we are getting the stack trace from outside of the method.
|
|
@@ -158,12 +208,19 @@ module Datadog
|
|
|
158
208
|
end
|
|
159
209
|
caller_locs = method_frame + caller_locations # steep:ignore
|
|
160
210
|
# TODO capture arguments at exit
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
211
|
+
|
|
212
|
+
context = Context.new(locals: nil, target_self: self,
|
|
213
|
+
probe: probe, settings: settings, serializer: serializer,
|
|
214
|
+
serialized_entry_args: serialized_entry_args,
|
|
215
|
+
caller_locations: caller_locs,
|
|
216
|
+
return_value: rv, duration: duration, exception: exc,)
|
|
217
|
+
|
|
218
|
+
responder.probe_executed_callback(context)
|
|
219
|
+
if exc
|
|
220
|
+
raise exc
|
|
221
|
+
else
|
|
222
|
+
rv
|
|
223
|
+
end
|
|
167
224
|
else
|
|
168
225
|
# stop standard from trying to mess up my code
|
|
169
226
|
_ = 42
|
|
@@ -220,11 +277,7 @@ module Datadog
|
|
|
220
277
|
# not for eval'd code, unless the eval'd code is associated with
|
|
221
278
|
# a file name and client invokes this method with the correct
|
|
222
279
|
# file name for the eval'd code.
|
|
223
|
-
def hook_line(probe,
|
|
224
|
-
unless block
|
|
225
|
-
raise ArgumentError, 'No block given to hook_line'
|
|
226
|
-
end
|
|
227
|
-
|
|
280
|
+
def hook_line(probe, responder)
|
|
228
281
|
lock.synchronize do
|
|
229
282
|
if probe.instrumentation_trace_point
|
|
230
283
|
# Already instrumented, warn?
|
|
@@ -307,27 +360,86 @@ module Datadog
|
|
|
307
360
|
# are invoked for *each* line of Ruby executed.
|
|
308
361
|
# TODO find out exactly when the path in trace point is relative.
|
|
309
362
|
# Looks like this is the case when line trace point is not targeted?
|
|
310
|
-
|
|
363
|
+
continue = iseq || tp.lineno == probe.line_no && (
|
|
311
364
|
probe.file == tp.path || probe.file_matches?(tp.path)
|
|
312
365
|
)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
366
|
+
|
|
367
|
+
# We set the trace point on :return to be able to instrument
|
|
368
|
+
# 'end' lines. This also causes the trace point to be invoked on
|
|
369
|
+
# non-'end' lines when a line raises an exception, since the
|
|
370
|
+
# exception causes the method to stop executing and stack unwends.
|
|
371
|
+
# We do not want two invocations of the trace point.
|
|
372
|
+
# Therefore, if a trace point is invoked with a :line event,
|
|
373
|
+
# mark it as such and ignore subsequent :return events.
|
|
374
|
+
continue &&= if probe.executed_on_line?
|
|
375
|
+
tp.event == :line
|
|
376
|
+
else
|
|
377
|
+
if tp.event == :line
|
|
378
|
+
probe.executed_on_line!
|
|
379
|
+
end
|
|
380
|
+
true
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
if continue
|
|
384
|
+
if condition = probe.condition
|
|
385
|
+
begin
|
|
386
|
+
context = Context.new(
|
|
387
|
+
locals: Instrumenter.get_local_variables(tp),
|
|
388
|
+
target_self: tp.self,
|
|
389
|
+
probe: probe, settings: settings, serializer: serializer,
|
|
390
|
+
path: tp.path,
|
|
391
|
+
caller_locations: caller_locations,
|
|
392
|
+
)
|
|
393
|
+
continue = condition.satisfied?(context)
|
|
394
|
+
rescue => exc
|
|
395
|
+
# Evaluation error exception can be raised for "expected"
|
|
396
|
+
# errors, we probably need another setting to control whether
|
|
397
|
+
# these exceptions are propagated.
|
|
398
|
+
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions &&
|
|
399
|
+
!exc.is_a?(DI::Error::ExpressionEvaluationError)
|
|
400
|
+
|
|
401
|
+
continue = false
|
|
402
|
+
if context
|
|
403
|
+
# We want to report evaluation errors for conditions
|
|
404
|
+
# as probe snapshots. However, if we failed to create
|
|
405
|
+
# the context, we won't be able to report anything as
|
|
406
|
+
# the probe notifier builder requires a context.
|
|
407
|
+
begin
|
|
408
|
+
responder.probe_condition_evaluation_failed_callback(context, condition, exc)
|
|
409
|
+
rescue
|
|
410
|
+
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
411
|
+
|
|
412
|
+
# TODO log / report via telemetry?
|
|
413
|
+
end
|
|
414
|
+
else
|
|
415
|
+
_ = 42 # stop standard from wrecking this code
|
|
416
|
+
|
|
417
|
+
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
418
|
+
|
|
419
|
+
# TODO log / report via telemetry?
|
|
420
|
+
# If execution gets here, there is probably a bug in the tracer.
|
|
421
|
+
end
|
|
323
422
|
end
|
|
324
|
-
# & is to stop steep complaints, block is always present here.
|
|
325
|
-
block&.call(probe: probe,
|
|
326
|
-
serialized_locals: serialized_locals,
|
|
327
|
-
target_self: tp.self,
|
|
328
|
-
path: tp.path, caller_locations: caller_locations)
|
|
329
423
|
end
|
|
330
424
|
end
|
|
425
|
+
|
|
426
|
+
continue &&= rate_limiter.nil? || rate_limiter.allow? # standard:disable Style/AndOr
|
|
427
|
+
|
|
428
|
+
if continue
|
|
429
|
+
# The context creation is relatively expensive and we don't
|
|
430
|
+
# want to run it if the callback won't be executed due to the
|
|
431
|
+
# rate limit.
|
|
432
|
+
# Thus the copy-paste of the creation call here.
|
|
433
|
+
context ||= Context.new(
|
|
434
|
+
locals: Instrumenter.get_local_variables(tp),
|
|
435
|
+
target_self: tp.self,
|
|
436
|
+
probe: probe, settings: settings, serializer: serializer,
|
|
437
|
+
path: tp.path,
|
|
438
|
+
caller_locations: caller_locations,
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
responder.probe_executed_callback(context)
|
|
442
|
+
end
|
|
331
443
|
rescue => exc
|
|
332
444
|
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
|
333
445
|
logger.debug { "di: unhandled exception in line trace point: #{exc.class}: #{exc}" }
|
|
@@ -341,9 +453,11 @@ module Datadog
|
|
|
341
453
|
# TODO test this path
|
|
342
454
|
end
|
|
343
455
|
|
|
344
|
-
#
|
|
456
|
+
# Internal sanity check - untargeted trace points create a huge
|
|
457
|
+
# performance impact, and we absolutely do not want to set them
|
|
458
|
+
# accidentally.
|
|
345
459
|
if !iseq && !permit_untargeted_trace_points
|
|
346
|
-
raise "Trying to use an untargeted trace point when user did not permit it"
|
|
460
|
+
raise Error::InternalError, "Trying to use an untargeted trace point when user did not permit it"
|
|
347
461
|
end
|
|
348
462
|
|
|
349
463
|
lock.synchronize do
|
|
@@ -375,11 +489,11 @@ module Datadog
|
|
|
375
489
|
end
|
|
376
490
|
end
|
|
377
491
|
|
|
378
|
-
def hook(probe,
|
|
492
|
+
def hook(probe, responder)
|
|
379
493
|
if probe.method?
|
|
380
|
-
hook_method(probe,
|
|
494
|
+
hook_method(probe, responder)
|
|
381
495
|
elsif probe.line?
|
|
382
|
-
hook_line(probe,
|
|
496
|
+
hook_line(probe, responder)
|
|
383
497
|
else
|
|
384
498
|
# TODO add test coverage for this path
|
|
385
499
|
logger.debug { "di: unknown probe type to hook: #{probe}" }
|
|
@@ -449,3 +563,4 @@ module Datadog
|
|
|
449
563
|
end
|
|
450
564
|
|
|
451
565
|
# rubocop:enable Lint/AssignmentInCondition
|
|
566
|
+
# 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,21 +77,25 @@ 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)
|
|
81
88
|
|
|
89
|
+
# At most one report per second.
|
|
90
|
+
# We create the rate limiter here even though it may never be used,
|
|
91
|
+
# to avoid having to synchronize the creation since method probes
|
|
92
|
+
# can be executed on multiple threads concurrently (even if line
|
|
93
|
+
# probes are never executed concurrently since those are done in a
|
|
94
|
+
# trace point).
|
|
95
|
+
if condition
|
|
96
|
+
@condition_evaluation_failed_rate_limiter = Datadog::Core::TokenBucket.new(1)
|
|
97
|
+
end
|
|
98
|
+
|
|
82
99
|
@emitting_notified = false
|
|
83
100
|
end
|
|
84
101
|
|
|
@@ -89,6 +106,10 @@ module Datadog
|
|
|
89
106
|
attr_reader :type_name
|
|
90
107
|
attr_reader :method_name
|
|
91
108
|
attr_reader :template
|
|
109
|
+
attr_reader :template_segments
|
|
110
|
+
|
|
111
|
+
# The compiled condition for the probe, as a String.
|
|
112
|
+
attr_reader :condition
|
|
92
113
|
|
|
93
114
|
# Configured maximum capture depth. Can be nil in which case
|
|
94
115
|
# the global default will be used.
|
|
@@ -104,6 +125,16 @@ module Datadog
|
|
|
104
125
|
# Rate limiter object. For internal DI use only.
|
|
105
126
|
attr_reader :rate_limiter
|
|
106
127
|
|
|
128
|
+
# Rate limiter object for sending snapshots with evaluation errors
|
|
129
|
+
# for when probe condition evaluation fails.
|
|
130
|
+
# This rate limit is separate from the "base" rate limit for the probe
|
|
131
|
+
# because when the condition evaluation succeeds we want the "base"
|
|
132
|
+
# rate limit applied, not tainted by any evaluation errors
|
|
133
|
+
# (for example, the condition can be highly selective, and when it
|
|
134
|
+
# does not hold the evaluation may fail - we don't want to use up the
|
|
135
|
+
# probe rate limit for the errors).
|
|
136
|
+
attr_reader :condition_evaluation_failed_rate_limiter
|
|
137
|
+
|
|
107
138
|
def capture_snapshot?
|
|
108
139
|
@capture_snapshot
|
|
109
140
|
end
|
|
@@ -122,7 +153,7 @@ module Datadog
|
|
|
122
153
|
|
|
123
154
|
# Returns whether the probe is a method probe.
|
|
124
155
|
def method?
|
|
125
|
-
|
|
156
|
+
line_no.nil?
|
|
126
157
|
end
|
|
127
158
|
|
|
128
159
|
# Returns the line number associated with the probe, raising
|
|
@@ -186,6 +217,15 @@ module Datadog
|
|
|
186
217
|
def emitting_notified?
|
|
187
218
|
!!@emitting_notified
|
|
188
219
|
end
|
|
220
|
+
|
|
221
|
+
def executed_on_line?
|
|
222
|
+
!!@executed_on_line
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def executed_on_line!
|
|
226
|
+
# TODO lock?
|
|
227
|
+
@executed_on_line = true
|
|
228
|
+
end
|
|
189
229
|
end
|
|
190
230
|
end
|
|
191
231
|
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
|
|
@@ -101,7 +101,7 @@ module Datadog
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
begin
|
|
104
|
-
instrumenter.hook(probe,
|
|
104
|
+
instrumenter.hook(probe, self)
|
|
105
105
|
|
|
106
106
|
@installed_probes[probe.id] = probe
|
|
107
107
|
payload = probe_notification_builder.build_installed(probe)
|
|
@@ -184,7 +184,7 @@ module Datadog
|
|
|
184
184
|
begin
|
|
185
185
|
# TODO is it OK to hook from trace point handler?
|
|
186
186
|
# TODO the class is now defined, but can hooking still fail?
|
|
187
|
-
instrumenter.hook(probe,
|
|
187
|
+
instrumenter.hook(probe, self)
|
|
188
188
|
@pending_probes.delete(probe.id)
|
|
189
189
|
break
|
|
190
190
|
rescue Error::DITargetNotDefined
|
|
@@ -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,10 +238,18 @@ 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
|
|
|
245
|
+
def probe_condition_evaluation_failed_callback(context, expr, exc)
|
|
246
|
+
probe = context.probe
|
|
247
|
+
if probe.condition_evaluation_failed_rate_limiter&.allow?
|
|
248
|
+
payload = probe_notification_builder.build_condition_evaluation_failed(context, expr, exc)
|
|
249
|
+
probe_notifier_worker.add_snapshot(payload)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
244
253
|
# Class/module definition trace point (:end type).
|
|
245
254
|
# Used to install hooks when the target classes/modules aren't yet
|
|
246
255
|
# defined when the hook request is received.
|