datadog 2.8.0 → 2.10.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 +62 -1
- data/ext/datadog_profiling_native_extension/clock_id.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +66 -56
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
- data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.h +1 -1
- data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +16 -16
- data/ext/datadog_profiling_native_extension/collectors_stack.c +7 -7
- data/ext/datadog_profiling_native_extension/collectors_stack.h +2 -2
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +221 -127
- data/ext/datadog_profiling_native_extension/heap_recorder.c +50 -92
- data/ext/datadog_profiling_native_extension/heap_recorder.h +2 -2
- data/ext/datadog_profiling_native_extension/http_transport.c +4 -4
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +3 -0
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +3 -1
- data/ext/datadog_profiling_native_extension/profiling.c +10 -8
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +8 -8
- data/ext/datadog_profiling_native_extension/stack_recorder.c +63 -76
- data/ext/datadog_profiling_native_extension/stack_recorder.h +2 -2
- data/ext/datadog_profiling_native_extension/time_helpers.h +1 -1
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.c +47 -0
- data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +31 -0
- data/ext/libdatadog_api/crashtracker.c +3 -0
- data/lib/datadog/appsec/actions_handler.rb +27 -0
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +355 -157
- data/lib/datadog/appsec/assets/waf_rules/strict.json +62 -32
- data/lib/datadog/appsec/component.rb +14 -8
- data/lib/datadog/appsec/configuration/settings.rb +9 -0
- data/lib/datadog/appsec/context.rb +74 -0
- data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +12 -8
- data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +6 -6
- data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +4 -4
- data/lib/datadog/appsec/contrib/graphql/appsec_trace.rb +1 -7
- data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +20 -30
- data/lib/datadog/appsec/contrib/graphql/reactive/multiplex.rb +6 -6
- data/lib/datadog/appsec/contrib/rack/gateway/response.rb +3 -3
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +67 -96
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +11 -11
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +6 -6
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +7 -7
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +10 -11
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +43 -60
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +23 -33
- data/lib/datadog/appsec/contrib/rails/patcher.rb +4 -14
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +7 -7
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +45 -65
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +5 -28
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +6 -6
- data/lib/datadog/appsec/event.rb +6 -6
- data/lib/datadog/appsec/ext.rb +8 -1
- data/lib/datadog/appsec/metrics/collector.rb +38 -0
- data/lib/datadog/appsec/metrics/exporter.rb +35 -0
- data/lib/datadog/appsec/metrics/telemetry.rb +23 -0
- data/lib/datadog/appsec/metrics.rb +13 -0
- data/lib/datadog/appsec/monitor/gateway/watcher.rb +23 -32
- data/lib/datadog/appsec/monitor/reactive/set_user.rb +6 -6
- data/lib/datadog/appsec/processor/rule_loader.rb +0 -3
- data/lib/datadog/appsec/processor.rb +4 -3
- data/lib/datadog/appsec/response.rb +18 -80
- data/lib/datadog/appsec/security_engine/result.rb +67 -0
- data/lib/datadog/appsec/security_engine/runner.rb +88 -0
- data/lib/datadog/appsec/security_engine.rb +9 -0
- data/lib/datadog/appsec.rb +17 -8
- data/lib/datadog/auto_instrument.rb +3 -0
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +39 -11
- data/lib/datadog/core/configuration/components.rb +4 -2
- data/lib/datadog/core/configuration.rb +1 -1
- data/lib/datadog/{tracing → core}/contrib/rails/utils.rb +1 -3
- data/lib/datadog/core/crashtracking/component.rb +1 -3
- data/lib/datadog/core/telemetry/event.rb +87 -3
- data/lib/datadog/core/telemetry/logging.rb +2 -2
- data/lib/datadog/core/telemetry/metric.rb +22 -0
- data/lib/datadog/core/telemetry/worker.rb +33 -0
- data/lib/datadog/di/base.rb +115 -0
- data/lib/datadog/di/code_tracker.rb +7 -4
- data/lib/datadog/di/component.rb +19 -11
- data/lib/datadog/di/configuration/settings.rb +11 -1
- data/lib/datadog/di/contrib/railtie.rb +15 -0
- data/lib/datadog/di/contrib.rb +26 -0
- data/lib/datadog/di/error.rb +5 -0
- data/lib/datadog/di/instrumenter.rb +39 -18
- data/lib/datadog/di/{init.rb → preload.rb} +2 -4
- data/lib/datadog/di/probe_manager.rb +4 -4
- data/lib/datadog/di/probe_notification_builder.rb +22 -2
- data/lib/datadog/di/probe_notifier_worker.rb +5 -6
- data/lib/datadog/di/redactor.rb +0 -1
- data/lib/datadog/di/remote.rb +30 -9
- data/lib/datadog/di/transport.rb +2 -4
- data/lib/datadog/di.rb +5 -108
- data/lib/datadog/kit/appsec/events.rb +3 -3
- data/lib/datadog/kit/identity.rb +4 -4
- data/lib/datadog/profiling/component.rb +55 -53
- data/lib/datadog/profiling/http_transport.rb +1 -26
- data/lib/datadog/tracing/contrib/action_cable/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/action_mailer/integration.rb +6 -2
- data/lib/datadog/tracing/contrib/action_pack/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/action_view/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/active_job/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/active_record/integration.rb +6 -2
- data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +3 -1
- data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +10 -0
- data/lib/datadog/tracing/contrib/active_support/integration.rb +5 -2
- data/lib/datadog/tracing/contrib/auto_instrument.rb +2 -2
- data/lib/datadog/tracing/contrib/aws/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/extensions.rb +15 -3
- data/lib/datadog/tracing/contrib/http/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/httprb/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/kafka/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/mongodb/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/opensearch/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/presto/integration.rb +3 -0
- data/lib/datadog/tracing/contrib/rack/integration.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/framework.rb +2 -2
- data/lib/datadog/tracing/contrib/rails/patcher.rb +1 -1
- data/lib/datadog/tracing/contrib/rest_client/integration.rb +3 -0
- data/lib/datadog/tracing/span.rb +12 -4
- data/lib/datadog/tracing/span_event.rb +123 -3
- data/lib/datadog/tracing/span_operation.rb +6 -0
- data/lib/datadog/tracing/transport/serializable_trace.rb +24 -6
- data/lib/datadog/version.rb +1 -1
- metadata +40 -17
- data/lib/datadog/appsec/contrib/sinatra/ext.rb +0 -14
- data/lib/datadog/appsec/processor/context.rb +0 -107
- data/lib/datadog/appsec/reactive/operation.rb +0 -68
- data/lib/datadog/appsec/scope.rb +0 -58
- data/lib/datadog/core/crashtracking/agent_base_url.rb +0 -21
@@ -41,6 +41,18 @@ module Datadog
|
|
41
41
|
}
|
42
42
|
end
|
43
43
|
|
44
|
+
def ==(other)
|
45
|
+
other.is_a?(self.class) &&
|
46
|
+
name == other.name &&
|
47
|
+
values == other.values && tags == other.tags && common == other.common && type == other.type
|
48
|
+
end
|
49
|
+
|
50
|
+
alias eql? ==
|
51
|
+
|
52
|
+
def hash
|
53
|
+
[self.class, name, values, tags, common, type].hash
|
54
|
+
end
|
55
|
+
|
44
56
|
private
|
45
57
|
|
46
58
|
def tags_to_array(tags)
|
@@ -71,6 +83,16 @@ module Datadog
|
|
71
83
|
res[:interval] = interval
|
72
84
|
res
|
73
85
|
end
|
86
|
+
|
87
|
+
def ==(other)
|
88
|
+
super && interval == other.interval
|
89
|
+
end
|
90
|
+
|
91
|
+
alias eql? ==
|
92
|
+
|
93
|
+
def hash
|
94
|
+
[super, interval].hash
|
95
|
+
end
|
74
96
|
end
|
75
97
|
|
76
98
|
# Count metric adds up all the submitted values in a time interval. This would be suitable for a
|
@@ -97,6 +97,8 @@ module Datadog
|
|
97
97
|
return if events.empty?
|
98
98
|
return if !enabled? || !sent_started_event?
|
99
99
|
|
100
|
+
events = deduplicate_logs(events)
|
101
|
+
|
100
102
|
Datadog.logger.debug { "Sending #{events&.count} telemetry events" }
|
101
103
|
send_event(Event::MessageBatch.new(events))
|
102
104
|
end
|
@@ -167,6 +169,37 @@ module Datadog
|
|
167
169
|
Datadog.logger.debug('Agent does not support telemetry; disabling future telemetry events.')
|
168
170
|
disable!
|
169
171
|
end
|
172
|
+
|
173
|
+
# Deduplicate logs by counting the number of repeated occurrences of the same log
|
174
|
+
# entry and replacing them with a single entry with the calculated `count` value.
|
175
|
+
# Non-log events are unchanged.
|
176
|
+
def deduplicate_logs(events)
|
177
|
+
return events if events.empty?
|
178
|
+
|
179
|
+
all_logs = []
|
180
|
+
other_events = events.reject do |event|
|
181
|
+
if event.is_a?(Event::Log)
|
182
|
+
all_logs << event
|
183
|
+
true
|
184
|
+
else
|
185
|
+
false
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
return events if all_logs.empty?
|
190
|
+
|
191
|
+
uniq_logs = all_logs.group_by(&:itself).map do |_, logs|
|
192
|
+
log = logs.first
|
193
|
+
if logs.size > 1
|
194
|
+
# New log event with a count of repeated occurrences
|
195
|
+
Event::Log.new(message: log.message, level: log.level, stack_trace: log.stack_trace, count: logs.size)
|
196
|
+
else
|
197
|
+
log
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
other_events + uniq_logs
|
202
|
+
end
|
170
203
|
end
|
171
204
|
end
|
172
205
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is loaded by datadog/di/preload.rb.
|
4
|
+
# It contains just the global DI reference to the (normally one and only)
|
5
|
+
# code tracker for the current process.
|
6
|
+
# This file should not require the rest of DI, specifically none of the
|
7
|
+
# contrib code that is meant to be loaded after third-party libraries
|
8
|
+
# are loaded, and also none of the rest of datadog library which also
|
9
|
+
# has contrib code in other products.
|
10
|
+
|
11
|
+
require_relative 'code_tracker'
|
12
|
+
|
13
|
+
module Datadog
|
14
|
+
# Namespace for Datadog dynamic instrumentation.
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
module DI
|
18
|
+
LOCK = Mutex.new
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_reader :code_tracker
|
22
|
+
|
23
|
+
# Activates code tracking. Normally this method should be called
|
24
|
+
# when the application starts. If instrumenting third-party code,
|
25
|
+
# code tracking needs to be enabled before the third-party libraries
|
26
|
+
# are loaded. Any third-party code loaded before code tracking is
|
27
|
+
# activated will NOT be instrumentable using dynamic instrumentation.
|
28
|
+
#
|
29
|
+
# TODO test that activating tracker multiple times preserves
|
30
|
+
# existing mappings in the registry
|
31
|
+
def activate_tracking!
|
32
|
+
(@code_tracker ||= CodeTracker.new).start
|
33
|
+
end
|
34
|
+
|
35
|
+
# Activates code tracking if possible.
|
36
|
+
#
|
37
|
+
# This method does nothing if invoked in an environment that does not
|
38
|
+
# implement required trace points for code tracking (MRI Ruby < 2.6,
|
39
|
+
# JRuby) and rescues any exceptions that may be raised by downstream
|
40
|
+
# DI code.
|
41
|
+
def activate_tracking
|
42
|
+
# :script_compiled trace point was added in Ruby 2.6.
|
43
|
+
return unless RUBY_VERSION >= '2.6'
|
44
|
+
|
45
|
+
begin
|
46
|
+
# Activate code tracking by default because line trace points will not work
|
47
|
+
# without it.
|
48
|
+
Datadog::DI.activate_tracking!
|
49
|
+
rescue => exc
|
50
|
+
if defined?(Datadog.logger)
|
51
|
+
Datadog.logger.warn { "di: Failed to activate code tracking for DI: #{exc.class}: #{exc}" }
|
52
|
+
else
|
53
|
+
# We do not have Datadog logger potentially because DI code tracker is
|
54
|
+
# being loaded early in application boot process and the rest of datadog
|
55
|
+
# wasn't loaded yet. Output to standard error.
|
56
|
+
warn("datadog: di: Failed to activate code tracking for DI: #{exc.class}: #{exc}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Deactivates code tracking. In normal usage of DI this method should
|
62
|
+
# never be called, however it is used by DI's test suite to reset
|
63
|
+
# state for individual tests.
|
64
|
+
#
|
65
|
+
# Note that deactivating tracking clears out the registry, losing
|
66
|
+
# the ability to look up files that have been loaded into the process
|
67
|
+
# already.
|
68
|
+
def deactivate_tracking!
|
69
|
+
code_tracker&.stop
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns whether code tracking is available.
|
73
|
+
# This method should be used instead of querying #code_tracker
|
74
|
+
# because the latter one may be nil.
|
75
|
+
def code_tracking_active?
|
76
|
+
code_tracker&.active? || false
|
77
|
+
end
|
78
|
+
|
79
|
+
# DI code tracker is instantiated globally before the regular set of
|
80
|
+
# components is created, but the code tracker needs to call out to the
|
81
|
+
# "current" DI component to perform instrumentation when application
|
82
|
+
# code is loaded. Because this call may happen prior to Datadog
|
83
|
+
# components having been initialized, we maintain the "current component"
|
84
|
+
# which contains a reference to the most recently instantiated
|
85
|
+
# DI::Component. This way, if a DI component hasn't been instantiated,
|
86
|
+
# we do not try to reference Datadog.components.
|
87
|
+
# In other words, this method exists so that we never attempt to call
|
88
|
+
# Datadog.components from the code tracker.
|
89
|
+
def current_component
|
90
|
+
LOCK.synchronize do
|
91
|
+
@current_components&.last
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# To avoid potential races with DI::Component being added and removed,
|
96
|
+
# we maintain a list of the components. Normally the list should contain
|
97
|
+
# either zero or one component depending on whether DI is enabled in
|
98
|
+
# Datadog configuration. However, if a new instance of DI::Component
|
99
|
+
# is created while the previous instance is still running, we are
|
100
|
+
# guaranteed to not end up with no component when one is running.
|
101
|
+
def add_current_component(component)
|
102
|
+
LOCK.synchronize do
|
103
|
+
@current_components ||= []
|
104
|
+
@current_components << component
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def remove_current_component(component)
|
109
|
+
LOCK.synchronize do
|
110
|
+
@current_components&.delete(component)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
# rubocop:disable Lint/AssignmentInCondition
|
4
4
|
|
5
|
+
require_relative 'error'
|
6
|
+
|
5
7
|
module Datadog
|
6
8
|
module DI
|
7
9
|
# Tracks loaded Ruby code by source file and maintains a map from
|
@@ -87,11 +89,12 @@ module Datadog
|
|
87
89
|
# rescue any exceptions that might not be handled to not break said
|
88
90
|
# customer applications.
|
89
91
|
rescue => exc
|
90
|
-
#
|
91
|
-
#
|
92
|
-
|
92
|
+
# Code tracker may be loaded without the rest of DI,
|
93
|
+
# in which case DI.component will not yet be defined,
|
94
|
+
# but we will have DI.current_component (set to nil).
|
95
|
+
if component = DI.current_component
|
93
96
|
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
94
|
-
component.logger.
|
97
|
+
component.logger.debug { "di: unhandled exception in script_compiled trace point: #{exc.class}: #{exc}" }
|
95
98
|
component.telemetry&.report(exc, description: "Unhandled exception in script_compiled trace point")
|
96
99
|
# TODO test this path
|
97
100
|
else
|
data/lib/datadog/di/component.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../core'
|
4
|
+
|
3
5
|
module Datadog
|
4
6
|
module DI
|
5
7
|
# Component for dynamic instrumentation.
|
@@ -14,22 +16,22 @@ module Datadog
|
|
14
16
|
# resources and installed tracepoints upon shutdown.
|
15
17
|
class Component
|
16
18
|
class << self
|
17
|
-
def build(settings, agent_settings, telemetry: nil)
|
19
|
+
def build(settings, agent_settings, logger, telemetry: nil)
|
18
20
|
return unless settings.respond_to?(:dynamic_instrumentation) && settings.dynamic_instrumentation.enabled
|
19
21
|
|
20
22
|
unless settings.respond_to?(:remote) && settings.remote.enabled
|
21
|
-
|
23
|
+
logger.warn("di: dynamic instrumentation could not be enabled because Remote Configuration Management is not available. To enable Remote Configuration, see https://docs.datadoghq.com/agent/remote_config")
|
22
24
|
return
|
23
25
|
end
|
24
26
|
|
25
|
-
return unless environment_supported?(settings)
|
27
|
+
return unless environment_supported?(settings, logger)
|
26
28
|
|
27
|
-
new(settings, agent_settings,
|
29
|
+
new(settings, agent_settings, logger, code_tracker: DI.code_tracker, telemetry: telemetry).tap do |component|
|
28
30
|
DI.add_current_component(component)
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
32
|
-
def build!(settings, agent_settings, telemetry: nil)
|
34
|
+
def build!(settings, agent_settings, logger, telemetry: nil)
|
33
35
|
unless settings.respond_to?(:dynamic_instrumentation) && settings.dynamic_instrumentation.enabled
|
34
36
|
raise "Requested DI component but DI is not enabled in settings"
|
35
37
|
end
|
@@ -38,27 +40,31 @@ module Datadog
|
|
38
40
|
raise "Requested DI component but remote config is not enabled in settings"
|
39
41
|
end
|
40
42
|
|
41
|
-
unless environment_supported?(settings)
|
43
|
+
unless environment_supported?(settings, logger)
|
42
44
|
raise "DI does not support the environment (development or Ruby version too low or not MRI)"
|
43
45
|
end
|
44
46
|
|
45
|
-
new(settings, agent_settings,
|
47
|
+
new(settings, agent_settings, logger, code_tracker: DI.code_tracker, telemetry: telemetry)
|
46
48
|
end
|
47
49
|
|
48
50
|
# Checks whether the runtime environment is supported by
|
49
51
|
# dynamic instrumentation. Currently we only require that, if Rails
|
50
52
|
# is used, that Rails environment is not development because
|
51
53
|
# DI does not currently support code unloading and reloading.
|
52
|
-
def environment_supported?(settings)
|
54
|
+
def environment_supported?(settings, logger)
|
53
55
|
# TODO add tests?
|
54
56
|
unless settings.dynamic_instrumentation.internal.development
|
55
57
|
if Datadog::Core::Environment::Execution.development?
|
56
|
-
|
58
|
+
logger.warn("di: development environment detected; not enabling dynamic instrumentation")
|
57
59
|
return false
|
58
60
|
end
|
59
61
|
end
|
60
|
-
if RUBY_ENGINE != 'ruby'
|
61
|
-
|
62
|
+
if RUBY_ENGINE != 'ruby'
|
63
|
+
logger.warn("di: cannot enable dynamic instrumentation: MRI is required, but running on #{RUBY_ENGINE}")
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
if RUBY_VERSION < '2.6'
|
67
|
+
logger.warn("di: cannot enable dynamic instrumentation: Ruby 2.6+ is required, but running on #{RUBY_VERSION}")
|
62
68
|
return false
|
63
69
|
end
|
64
70
|
true
|
@@ -70,6 +76,7 @@ module Datadog
|
|
70
76
|
@agent_settings = agent_settings
|
71
77
|
@logger = logger
|
72
78
|
@telemetry = telemetry
|
79
|
+
@code_tracker = code_tracker
|
73
80
|
@redactor = Redactor.new(settings)
|
74
81
|
@serializer = Serializer.new(settings, redactor, telemetry: telemetry)
|
75
82
|
@instrumenter = Instrumenter.new(settings, serializer, logger, code_tracker: code_tracker, telemetry: telemetry)
|
@@ -84,6 +91,7 @@ module Datadog
|
|
84
91
|
attr_reader :agent_settings
|
85
92
|
attr_reader :logger
|
86
93
|
attr_reader :telemetry
|
94
|
+
attr_reader :code_tracker
|
87
95
|
attr_reader :instrumenter
|
88
96
|
attr_reader :transport
|
89
97
|
attr_reader :probe_notifier_worker
|
@@ -166,10 +166,20 @@ module Datadog
|
|
166
166
|
# being sent out by the probe notifier worker) and creates a
|
167
167
|
# possibility of dropping payloads if the queue gets too long.
|
168
168
|
option :min_send_interval do |o|
|
169
|
-
o.type :
|
169
|
+
o.type :float
|
170
170
|
o.default 3
|
171
171
|
end
|
172
172
|
|
173
|
+
# Number of snapshots that can be stored in the probe
|
174
|
+
# notifier worker queue. Larger capacity runs the risk of
|
175
|
+
# creating snapshots that exceed the agent's request size
|
176
|
+
# limit. Smaller capacity increases the risk of dropping
|
177
|
+
# snapshots.
|
178
|
+
option :snapshot_queue_capacity do |o|
|
179
|
+
o.type :int
|
180
|
+
o.default 100
|
181
|
+
end
|
182
|
+
|
173
183
|
# Enable dynamic instrumentation in development environments.
|
174
184
|
# Currently DI does not fully implement support for code
|
175
185
|
# unloading and reloading, and is not supported in
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module DI
|
5
|
+
module Contrib
|
6
|
+
# Railtie class initializes dynamic instrumentation contrib code
|
7
|
+
# in Rails environments.
|
8
|
+
class Railtie < Rails::Railtie
|
9
|
+
initializer 'datadog.dynamic_instrumentation.initialize' do |app|
|
10
|
+
Contrib.load_now
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../core/contrib/rails/utils'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module DI
|
7
|
+
module Contrib
|
8
|
+
module_function def load_now_or_later
|
9
|
+
if Datadog::Core::Contrib::Rails::Utils.railtie_supported?
|
10
|
+
require_relative 'contrib/railtie'
|
11
|
+
else
|
12
|
+
load_now
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# This method can be called more than once, to attempt to load
|
17
|
+
# DI components that depend on third-party libraries after additional
|
18
|
+
# dependencies are loaded (or potentially loaded).
|
19
|
+
module_function def load_now
|
20
|
+
if defined?(ActiveRecord::Base)
|
21
|
+
require_relative 'contrib/active_record'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/datadog/di/error.rb
CHANGED
@@ -27,6 +27,11 @@ module Datadog
|
|
27
27
|
class DITargetNotDefined < Error
|
28
28
|
end
|
29
29
|
|
30
|
+
# Attempting to instrument a line and the file containing the line
|
31
|
+
# was loaded prior to code tracking being enabled.
|
32
|
+
class DITargetNotInRegistry < Error
|
33
|
+
end
|
34
|
+
|
30
35
|
# Raised when trying to install a probe whose installation failed
|
31
36
|
# earlier in the same process. This exception should contain the
|
32
37
|
# original exception report from initial installation attempt.
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative '../core/utils/time'
|
4
4
|
|
5
|
-
|
5
|
+
# rubocop:disable Lint/AssignmentInCondition
|
6
6
|
|
7
7
|
module Datadog
|
8
8
|
module DI
|
@@ -115,22 +115,21 @@ module Datadog
|
|
115
115
|
depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
|
116
116
|
attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
|
117
117
|
end
|
118
|
-
|
118
|
+
start_time = Core::Utils::Time.get_time
|
119
119
|
# Under Ruby 2.6 we cannot just call super(*args, **kwargs)
|
120
120
|
# for methods defined via method_missing.
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
super(*args, **kwargs, &target_block)
|
125
|
-
else
|
126
|
-
super(*args, &target_block)
|
127
|
-
end
|
128
|
-
elsif kwargs.any?
|
129
|
-
super(**kwargs, &target_block)
|
121
|
+
rv = if args.any?
|
122
|
+
if kwargs.any?
|
123
|
+
super(*args, **kwargs, &target_block)
|
130
124
|
else
|
131
|
-
super(&target_block)
|
125
|
+
super(*args, &target_block)
|
132
126
|
end
|
127
|
+
elsif kwargs.any?
|
128
|
+
super(**kwargs, &target_block)
|
129
|
+
else
|
130
|
+
super(&target_block)
|
133
131
|
end
|
132
|
+
duration = Core::Utils::Time.get_time - start_time
|
134
133
|
# The method itself is not part of the stack trace because
|
135
134
|
# we are getting the stack trace from outside of the method.
|
136
135
|
# Add the method in manually as the top frame.
|
@@ -246,11 +245,12 @@ module Datadog
|
|
246
245
|
#
|
247
246
|
# If the requested file is not in code tracker's registry,
|
248
247
|
# or the code tracker does not exist at all,
|
249
|
-
# do not attempt to
|
248
|
+
# do not attempt to instrument now.
|
250
249
|
# The caller should add the line to the list of pending lines
|
251
250
|
# to instrument and install the hook when the file in
|
252
251
|
# question is loaded (and hopefully, by then code tracking
|
253
252
|
# is active, otherwise the line will never be instrumented.)
|
253
|
+
raise_if_probe_in_loaded_features(probe)
|
254
254
|
raise Error::DITargetNotDefined, "File not in code tracker registry: #{probe.file}"
|
255
255
|
end
|
256
256
|
end
|
@@ -258,6 +258,7 @@ module Datadog
|
|
258
258
|
# Same as previous comment, if untargeted trace points are not
|
259
259
|
# explicitly defined, and we do not have code tracking, do not
|
260
260
|
# instrument the method.
|
261
|
+
raise_if_probe_in_loaded_features(probe)
|
261
262
|
raise Error::DITargetNotDefined, "File not in code tracker registry: #{probe.file}"
|
262
263
|
end
|
263
264
|
|
@@ -303,13 +304,13 @@ module Datadog
|
|
303
304
|
end
|
304
305
|
rescue => exc
|
305
306
|
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
306
|
-
logger.
|
307
|
+
logger.debug { "di: unhandled exception in line trace point: #{exc.class}: #{exc}" }
|
307
308
|
telemetry&.report(exc, description: "Unhandled exception in line trace point")
|
308
309
|
# TODO test this path
|
309
310
|
end
|
310
311
|
rescue => exc
|
311
312
|
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
312
|
-
logger.
|
313
|
+
logger.debug { "di: unhandled exception in line trace point: #{exc.class}: #{exc}" }
|
313
314
|
telemetry&.report(exc, description: "Unhandled exception in line trace point")
|
314
315
|
# TODO test this path
|
315
316
|
end
|
@@ -355,7 +356,7 @@ module Datadog
|
|
355
356
|
hook_line(probe, &block)
|
356
357
|
else
|
357
358
|
# TODO add test coverage for this path
|
358
|
-
logger.
|
359
|
+
logger.debug { "di: unknown probe type to hook: #{probe}" }
|
359
360
|
end
|
360
361
|
end
|
361
362
|
|
@@ -366,7 +367,7 @@ module Datadog
|
|
366
367
|
unhook_line(probe)
|
367
368
|
else
|
368
369
|
# TODO add test coverage for this path
|
369
|
-
logger.
|
370
|
+
logger.debug { "di: unknown probe type to unhook: #{probe}" }
|
370
371
|
end
|
371
372
|
end
|
372
373
|
|
@@ -374,6 +375,26 @@ module Datadog
|
|
374
375
|
|
375
376
|
attr_reader :lock
|
376
377
|
|
378
|
+
def raise_if_probe_in_loaded_features(probe)
|
379
|
+
return unless probe.file
|
380
|
+
|
381
|
+
# If the probe file is in the list of loaded files
|
382
|
+
# (as per $LOADED_FEATURES, using either exact or suffix match),
|
383
|
+
# raise an error indicating that
|
384
|
+
# code tracker is missing the loaded file because the file
|
385
|
+
# won't be loaded again (DI only works in production environments
|
386
|
+
# that do not normally reload code).
|
387
|
+
if $LOADED_FEATURES.include?(probe.file)
|
388
|
+
raise Error::DITargetNotInRegistry, "File loaded but is not in code tracker registry: #{probe.file}"
|
389
|
+
end
|
390
|
+
# Ths is an expensive check
|
391
|
+
$LOADED_FEATURES.each do |path|
|
392
|
+
if Utils.path_matches_suffix?(path, probe.file)
|
393
|
+
raise Error::DITargetNotInRegistry, "File matching probe path (#{probe.file}) was loaded and is not in code tracker registry: #{path}"
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
377
398
|
# TODO test that this resolves qualified names e.g. A::B
|
378
399
|
def symbolize_class_name(cls_name)
|
379
400
|
Object.const_get(cls_name)
|
@@ -1,12 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Require 'datadog/di/
|
3
|
+
# Require 'datadog/di/preload' early in the application boot process to
|
4
4
|
# enable dynamic instrumentation for third-party libraries used by the
|
5
5
|
# application.
|
6
6
|
|
7
|
-
require_relative '
|
8
|
-
require_relative '../tracing/contrib'
|
9
|
-
require_relative '../di'
|
7
|
+
require_relative 'base'
|
10
8
|
|
11
9
|
# Code tracking is required for line probes to work; see the comments
|
12
10
|
# on the activate_tracking methods in di.rb for further details.
|
@@ -32,7 +32,7 @@ module Datadog
|
|
32
32
|
install_pending_method_probes(tp.self)
|
33
33
|
rescue => exc
|
34
34
|
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
35
|
-
logger.
|
35
|
+
logger.debug { "di: unhandled exception in definition trace point: #{exc.class}: #{exc}" }
|
36
36
|
telemetry&.report(exc, description: "Unhandled exception in definition trace point")
|
37
37
|
# TODO test this path
|
38
38
|
end
|
@@ -120,7 +120,7 @@ module Datadog
|
|
120
120
|
# In "propagate all exceptions" mode we will try to instrument again.
|
121
121
|
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
122
122
|
|
123
|
-
logger.
|
123
|
+
logger.debug { "di: error processing probe configuration: #{exc.class}: #{exc}" }
|
124
124
|
telemetry&.report(exc, description: "Error processing probe configuration")
|
125
125
|
# TODO report probe as failed to agent since we won't attempt to
|
126
126
|
# install it again.
|
@@ -160,7 +160,7 @@ module Datadog
|
|
160
160
|
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
161
161
|
# Silence all exceptions?
|
162
162
|
# TODO should we propagate here and rescue upstream?
|
163
|
-
logger.
|
163
|
+
logger.debug { "di: error removing probe #{probe.id}: #{exc.class}: #{exc}" }
|
164
164
|
telemetry&.report(exc, description: "Error removing probe")
|
165
165
|
end
|
166
166
|
end
|
@@ -190,7 +190,7 @@ module Datadog
|
|
190
190
|
rescue => exc
|
191
191
|
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
192
192
|
|
193
|
-
logger.
|
193
|
+
logger.debug { "di: error installing probe after class is defined: #{exc.class}: #{exc}" }
|
194
194
|
telemetry&.report(exc, description: "Error installing probe after class is defined")
|
195
195
|
end
|
196
196
|
end
|
@@ -32,6 +32,12 @@ module Datadog
|
|
32
32
|
status: 'EMITTING',)
|
33
33
|
end
|
34
34
|
|
35
|
+
def build_errored(probe, exc)
|
36
|
+
build_status(probe,
|
37
|
+
message: "Instrumentation for probe #{probe.id} failed: #{exc}",
|
38
|
+
status: 'ERROR',)
|
39
|
+
end
|
40
|
+
|
35
41
|
# Duration is in seconds.
|
36
42
|
def build_executed(probe,
|
37
43
|
trace_point: nil, rv: nil, duration: nil, caller_locations: nil,
|
@@ -141,8 +147,8 @@ module Datadog
|
|
141
147
|
version: 2,
|
142
148
|
},
|
143
149
|
# TODO add tests that the trace/span id is correctly propagated
|
144
|
-
"dd.trace_id":
|
145
|
-
"dd.span_id":
|
150
|
+
"dd.trace_id": active_trace&.id&.to_s,
|
151
|
+
"dd.span_id": active_span&.id&.to_s,
|
146
152
|
ddsource: 'dd_debugger',
|
147
153
|
message: probe.template && evaluate_template(probe.template,
|
148
154
|
duration: duration ? duration * 1000 : 0),
|
@@ -150,6 +156,8 @@ module Datadog
|
|
150
156
|
}
|
151
157
|
end
|
152
158
|
|
159
|
+
private
|
160
|
+
|
153
161
|
def build_status(probe, message:, status:)
|
154
162
|
{
|
155
163
|
service: settings.service,
|
@@ -200,6 +208,18 @@ module Datadog
|
|
200
208
|
map[name] = value
|
201
209
|
end
|
202
210
|
end
|
211
|
+
|
212
|
+
def active_trace
|
213
|
+
if defined?(Datadog::Tracing)
|
214
|
+
Datadog::Tracing.active_trace
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def active_span
|
219
|
+
if defined?(Datadog::Tracing)
|
220
|
+
Datadog::Tracing.active_span
|
221
|
+
end
|
222
|
+
end
|
203
223
|
end
|
204
224
|
end
|
205
225
|
end
|
@@ -77,7 +77,7 @@ module Datadog
|
|
77
77
|
rescue => exc
|
78
78
|
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
79
79
|
|
80
|
-
logger.
|
80
|
+
logger.debug { "di: error in probe notifier worker: #{exc.class}: #{exc} (at #{exc.backtrace.first})" }
|
81
81
|
telemetry&.report(exc, description: "Error in probe notifier worker")
|
82
82
|
end
|
83
83
|
@lock.synchronize do
|
@@ -183,9 +183,8 @@ module Datadog
|
|
183
183
|
define_method("add_#{event_type}") do |event|
|
184
184
|
@lock.synchronize do
|
185
185
|
queue = send("#{event_type}_queue")
|
186
|
-
|
187
|
-
|
188
|
-
logger.warn("#{self.class.name}: dropping #{event_type} because queue is full")
|
186
|
+
if queue.length > settings.dynamic_instrumentation.internal.snapshot_queue_capacity
|
187
|
+
logger.debug { "di: #{self.class.name}: dropping #{event_type} because queue is full" }
|
189
188
|
else
|
190
189
|
queue << event
|
191
190
|
end
|
@@ -242,7 +241,7 @@ module Datadog
|
|
242
241
|
end
|
243
242
|
rescue => exc
|
244
243
|
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
245
|
-
logger.
|
244
|
+
logger.debug { "di: failed to send #{event_name}: #{exc.class}: #{exc} (at #{exc.backtrace.first})" }
|
246
245
|
# Should we report this error to telemetry? Most likely failure
|
247
246
|
# to send is due to a network issue, and trying to send a
|
248
247
|
# telemetry message would also fail.
|
@@ -253,7 +252,7 @@ module Datadog
|
|
253
252
|
# Normally the queue should only be consumed in this method,
|
254
253
|
# however if anyone consumes it elsewhere we don't want to block
|
255
254
|
# while consuming it here. Rescue ThreadError and return.
|
256
|
-
logger.
|
255
|
+
logger.debug { "di: unexpected #{event_name} queue underflow - consumed elsewhere?" }
|
257
256
|
telemetry&.report(exc, description: "Unexpected #{event_name} queue underflow")
|
258
257
|
ensure
|
259
258
|
@lock.synchronize do
|