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
data/lib/datadog/di/redactor.rb
CHANGED
data/lib/datadog/di/remote.rb
CHANGED
@@ -49,28 +49,48 @@ module Datadog
|
|
49
49
|
begin
|
50
50
|
probe_spec = parse_content(content)
|
51
51
|
probe = ProbeBuilder.build_from_remote_config(probe_spec)
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
probe_notification_builder = component.probe_notification_builder
|
53
|
+
payload = probe_notification_builder.build_received(probe)
|
54
|
+
probe_notifier_worker = component.probe_notifier_worker
|
55
|
+
probe_notifier_worker.add_status(payload)
|
56
|
+
component.logger.debug { "di: received probe from RC: #{probe.type} #{probe.location}" }
|
55
57
|
|
56
58
|
begin
|
57
59
|
# TODO test exception capture
|
58
60
|
probe_manager.add_probe(probe)
|
59
61
|
content.applied
|
62
|
+
rescue DI::Error::DITargetNotInRegistry => exc
|
63
|
+
component.telemetry&.report(exc, description: "Line probe is targeting a loaded file that is not in code tracker")
|
64
|
+
|
65
|
+
payload = probe_notification_builder.build_errored(probe, exc)
|
66
|
+
probe_notifier_worker.add_status(payload)
|
67
|
+
|
68
|
+
# If a probe fails to install, we will mark the content
|
69
|
+
# as errored. On subsequent remote configuration application
|
70
|
+
# attemps, probe manager will raise the "previously errored"
|
71
|
+
# exception and we'll rescue it here, again marking the
|
72
|
+
# content as errored but with a somewhat different exception
|
73
|
+
# message.
|
74
|
+
# TODO assert content state (errored for this example)
|
75
|
+
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
60
76
|
rescue => exc
|
61
77
|
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
62
78
|
|
63
|
-
component.logger.
|
79
|
+
component.logger.debug { "di: unhandled exception adding probe in DI remote receiver: #{exc.class}: #{exc}" }
|
64
80
|
component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
|
65
81
|
|
82
|
+
# TODO test this path
|
83
|
+
payload = probe_notification_builder.build_errored(probe, exc)
|
84
|
+
probe_notifier_worker.add_status(payload)
|
85
|
+
|
66
86
|
# If a probe fails to install, we will mark the content
|
67
87
|
# as errored. On subsequent remote configuration application
|
68
88
|
# attemps, probe manager will raise the "previously errored"
|
69
89
|
# exception and we'll rescue it here, again marking the
|
70
90
|
# content as errored but with a somewhat different exception
|
71
91
|
# message.
|
72
|
-
# TODO
|
73
|
-
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}
|
92
|
+
# TODO assert content state (errored for this example)
|
93
|
+
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
74
94
|
end
|
75
95
|
|
76
96
|
# Important: even if processing fails for this probe config,
|
@@ -81,10 +101,11 @@ module Datadog
|
|
81
101
|
rescue => exc
|
82
102
|
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
83
103
|
|
84
|
-
component.logger.
|
104
|
+
component.logger.debug { "di: unhandled exception handling probe in DI remote receiver: #{exc.class}: #{exc}" }
|
85
105
|
component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
|
86
106
|
|
87
|
-
|
107
|
+
# TODO assert content state (errored for this example)
|
108
|
+
content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
|
88
109
|
end
|
89
110
|
end
|
90
111
|
end
|
@@ -95,7 +116,7 @@ module Datadog
|
|
95
116
|
rescue => exc
|
96
117
|
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
97
118
|
|
98
|
-
component.logger.
|
119
|
+
component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
|
99
120
|
component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
|
100
121
|
end
|
101
122
|
end
|
data/lib/datadog/di/transport.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'ostruct'
|
4
3
|
require_relative 'error'
|
4
|
+
require_relative '../core/transport/http/adapters/net'
|
5
5
|
|
6
6
|
module Datadog
|
7
7
|
module DI
|
@@ -60,9 +60,7 @@ module Datadog
|
|
60
60
|
attr_reader :client
|
61
61
|
|
62
62
|
def send_request(desc, **options)
|
63
|
-
|
64
|
-
env = OpenStruct.new(**options)
|
65
|
-
# steep:ignore:end
|
63
|
+
env = Core::Transport::HTTP::Env.new(nil, options)
|
66
64
|
response = client.post(env)
|
67
65
|
unless response.ok?
|
68
66
|
raise Error::AgentCommunicationError, "#{desc} failed: #{response.code}: #{response.payload}"
|
data/lib/datadog/di.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'di/base'
|
3
4
|
require_relative 'di/error'
|
4
5
|
require_relative 'di/code_tracker'
|
5
6
|
require_relative 'di/component'
|
@@ -17,21 +18,6 @@ require_relative 'di/serializer'
|
|
17
18
|
require_relative 'di/transport'
|
18
19
|
require_relative 'di/utils'
|
19
20
|
|
20
|
-
if defined?(ActiveRecord::Base)
|
21
|
-
# The third-party library integrations need to be loaded after the
|
22
|
-
# third-party libraries are loaded. Tracing and appsec use Railtie
|
23
|
-
# to delay integrations until all of the application's dependencies
|
24
|
-
# are loaded, when running under Rails. We should do the same here in
|
25
|
-
# principle, however DI currently only has an ActiveRecord integration
|
26
|
-
# and AR should be loaded before any application code is loaded, being
|
27
|
-
# part of Rails, therefore for now we should be OK to just require the
|
28
|
-
# AR integration from here.
|
29
|
-
#
|
30
|
-
# TODO this require might need to be delayed via Rails post-initialization
|
31
|
-
# logic?
|
32
|
-
require_relative 'di/contrib/active_record'
|
33
|
-
end
|
34
|
-
|
35
21
|
module Datadog
|
36
22
|
# Namespace for Datadog dynamic instrumentation.
|
37
23
|
#
|
@@ -46,67 +32,7 @@ module Datadog
|
|
46
32
|
# Expose DI to global shared objects
|
47
33
|
Extensions.activate!
|
48
34
|
|
49
|
-
LOCK = Mutex.new
|
50
|
-
|
51
35
|
class << self
|
52
|
-
attr_reader :code_tracker
|
53
|
-
|
54
|
-
# Activates code tracking. Normally this method should be called
|
55
|
-
# when the application starts. If instrumenting third-party code,
|
56
|
-
# code tracking needs to be enabled before the third-party libraries
|
57
|
-
# are loaded. If you definitely will not be instrumenting
|
58
|
-
# third-party libraries, activating tracking after third-party libraries
|
59
|
-
# have been loaded may improve lookup performance.
|
60
|
-
#
|
61
|
-
# TODO test that activating tracker multiple times preserves
|
62
|
-
# existing mappings in the registry
|
63
|
-
def activate_tracking!
|
64
|
-
(@code_tracker ||= CodeTracker.new).start
|
65
|
-
end
|
66
|
-
|
67
|
-
# Activates code tracking if possible.
|
68
|
-
#
|
69
|
-
# This method does nothing if invoked in an environment that does not
|
70
|
-
# implement required trace points for code tracking (MRI Ruby < 2.6,
|
71
|
-
# JRuby) and rescues any exceptions that may be raised by downstream
|
72
|
-
# DI code.
|
73
|
-
def activate_tracking
|
74
|
-
# :script_compiled trace point was added in Ruby 2.6.
|
75
|
-
return unless RUBY_VERSION >= '2.6'
|
76
|
-
|
77
|
-
begin
|
78
|
-
# Activate code tracking by default because line trace points will not work
|
79
|
-
# without it.
|
80
|
-
Datadog::DI.activate_tracking!
|
81
|
-
rescue => exc
|
82
|
-
if defined?(Datadog.logger)
|
83
|
-
Datadog.logger.warn("Failed to activate code tracking for DI: #{exc.class}: #{exc}")
|
84
|
-
else
|
85
|
-
# We do not have Datadog logger potentially because DI code tracker is
|
86
|
-
# being loaded early in application boot process and the rest of datadog
|
87
|
-
# wasn't loaded yet. Output to standard error.
|
88
|
-
warn("Failed to activate code tracking for DI: #{exc.class}: #{exc}")
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# Deactivates code tracking. In normal usage of DI this method should
|
94
|
-
# never be called, however it is used by DI's test suite to reset
|
95
|
-
# state for individual tests.
|
96
|
-
#
|
97
|
-
# Note that deactivating tracking clears out the registry, losing
|
98
|
-
# the ability to look up files that have been loaded into the process
|
99
|
-
# already.
|
100
|
-
def deactivate_tracking!
|
101
|
-
code_tracker&.stop
|
102
|
-
end
|
103
|
-
|
104
|
-
# Returns whether code tracking is available.
|
105
|
-
# This method should be used instead of querying #code_tracker
|
106
|
-
# because the latter one may be nil.
|
107
|
-
def code_tracking_active?
|
108
|
-
code_tracker&.active? || false
|
109
|
-
end
|
110
36
|
|
111
37
|
# This method is called from DI Remote handler to issue DI operations
|
112
38
|
# to the probe manager (add or remove probes).
|
@@ -120,39 +46,6 @@ module Datadog
|
|
120
46
|
def component
|
121
47
|
Datadog.send(:components).dynamic_instrumentation
|
122
48
|
end
|
123
|
-
|
124
|
-
# DI code tracker is instantiated globally before the regular set of
|
125
|
-
# components is created, but the code tracker needs to call out to the
|
126
|
-
# "current" DI component to perform instrumentation when application
|
127
|
-
# code is loaded. Because this call may happen prior to Datadog
|
128
|
-
# components having been initialized, we maintain the "current component"
|
129
|
-
# which contains a reference to the most recently instantiated
|
130
|
-
# DI::Component. This way, if a DI component hasn't been instantiated,
|
131
|
-
# we do not try to reference Datadog.components.
|
132
|
-
def current_component
|
133
|
-
LOCK.synchronize do
|
134
|
-
@current_components&.last
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# To avoid potential races with DI::Component being added and removed,
|
139
|
-
# we maintain a list of the components. Normally the list should contain
|
140
|
-
# either zero or one component depending on whether DI is enabled in
|
141
|
-
# Datadog configuration. However, if a new instance of DI::Component
|
142
|
-
# is created while the previous instance is still running, we are
|
143
|
-
# guaranteed to not end up with no component when one is running.
|
144
|
-
def add_current_component(component)
|
145
|
-
LOCK.synchronize do
|
146
|
-
@current_components ||= []
|
147
|
-
@current_components << component
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def remove_current_component(component)
|
152
|
-
LOCK.synchronize do
|
153
|
-
@current_components&.delete(component)
|
154
|
-
end
|
155
|
-
end
|
156
49
|
end
|
157
50
|
end
|
158
51
|
end
|
@@ -167,3 +60,7 @@ if %w(1 true).include?(ENV['DD_DYNAMIC_INSTRUMENTATION_ENABLED']) # steep:ignore
|
|
167
60
|
# for line probes to work) activate tracking in an initializer.
|
168
61
|
Datadog::DI.activate_tracking
|
169
62
|
end
|
63
|
+
|
64
|
+
require_relative 'di/contrib'
|
65
|
+
|
66
|
+
Datadog::DI::Contrib.load_now_or_later
|
@@ -136,9 +136,9 @@ module Datadog
|
|
136
136
|
private
|
137
137
|
|
138
138
|
def set_trace_and_span_context(method, trace = nil, span = nil)
|
139
|
-
if (
|
140
|
-
trace =
|
141
|
-
span =
|
139
|
+
if (appsec_context = Datadog::AppSec.active_context)
|
140
|
+
trace = appsec_context.trace
|
141
|
+
span = appsec_context.span
|
142
142
|
end
|
143
143
|
|
144
144
|
trace ||= Datadog::Tracing.active_trace
|
data/lib/datadog/kit/identity.rb
CHANGED
@@ -66,7 +66,7 @@ module Datadog
|
|
66
66
|
active_span.set_tag("usr.#{k}", v) unless v.nil?
|
67
67
|
end
|
68
68
|
|
69
|
-
if Datadog::AppSec.
|
69
|
+
if Datadog::AppSec.active_context
|
70
70
|
user = ::Datadog::AppSec::Instrumentation::Gateway::User.new(id)
|
71
71
|
::Datadog::AppSec::Instrumentation.gateway.push('identity.set_user', user)
|
72
72
|
end
|
@@ -78,9 +78,9 @@ module Datadog
|
|
78
78
|
private
|
79
79
|
|
80
80
|
def set_trace_and_span_context(method, trace = nil, span = nil)
|
81
|
-
if (
|
82
|
-
trace =
|
83
|
-
span =
|
81
|
+
if (appsec_context = Datadog::AppSec.active_context)
|
82
|
+
trace = appsec_context.trace
|
83
|
+
span = appsec_context.span
|
84
84
|
end
|
85
85
|
|
86
86
|
trace ||= Datadog::Tracing.active_trace
|
@@ -4,10 +4,13 @@ module Datadog
|
|
4
4
|
module Profiling
|
5
5
|
# Responsible for wiring up the Profiler for execution
|
6
6
|
module Component
|
7
|
+
ALLOCATION_WITH_RACTORS_ONLY_ONCE = Datadog::Core::Utils::OnlyOnce.new
|
8
|
+
private_constant :ALLOCATION_WITH_RACTORS_ONLY_ONCE
|
9
|
+
|
7
10
|
# Passing in a `nil` tracer is supported and will disable the following profiling features:
|
8
|
-
# *
|
11
|
+
# * Profiling in the trace viewer, as well as scoping a profile down to a span
|
9
12
|
# * Endpoint aggregation in the profiler UX, including normalization (resource per endpoint call)
|
10
|
-
def self.build_profiler_component(settings:, agent_settings:, optional_tracer:) # rubocop:disable Metrics/MethodLength
|
13
|
+
def self.build_profiler_component(settings:, agent_settings:, optional_tracer:, logger:) # rubocop:disable Metrics/MethodLength
|
11
14
|
return [nil, {profiling_enabled: false}] unless settings.profiling.enabled
|
12
15
|
|
13
16
|
# Workaround for weird dependency direction: the Core::Configuration::Components class currently has a
|
@@ -36,14 +39,14 @@ module Datadog
|
|
36
39
|
|
37
40
|
# NOTE: Please update the Initialization section of ProfilingDevelopment.md with any changes to this method
|
38
41
|
|
39
|
-
no_signals_workaround_enabled = no_signals_workaround_enabled?(settings)
|
42
|
+
no_signals_workaround_enabled = no_signals_workaround_enabled?(settings, logger)
|
40
43
|
timeline_enabled = settings.profiling.advanced.timeline_enabled
|
41
|
-
allocation_profiling_enabled = enable_allocation_profiling?(settings)
|
44
|
+
allocation_profiling_enabled = enable_allocation_profiling?(settings, logger)
|
42
45
|
heap_sample_every = get_heap_sample_every(settings)
|
43
|
-
heap_profiling_enabled = enable_heap_profiling?(settings, allocation_profiling_enabled, heap_sample_every)
|
44
|
-
heap_size_profiling_enabled = enable_heap_size_profiling?(settings, heap_profiling_enabled)
|
46
|
+
heap_profiling_enabled = enable_heap_profiling?(settings, allocation_profiling_enabled, heap_sample_every, logger)
|
47
|
+
heap_size_profiling_enabled = enable_heap_size_profiling?(settings, heap_profiling_enabled, logger)
|
45
48
|
|
46
|
-
overhead_target_percentage = valid_overhead_target(settings.profiling.advanced.overhead_target_percentage)
|
49
|
+
overhead_target_percentage = valid_overhead_target(settings.profiling.advanced.overhead_target_percentage, logger)
|
47
50
|
upload_period_seconds = [60, settings.profiling.advanced.upload_period_seconds].max
|
48
51
|
|
49
52
|
recorder = Datadog::Profiling::StackRecorder.new(
|
@@ -57,13 +60,13 @@ module Datadog
|
|
57
60
|
)
|
58
61
|
thread_context_collector = build_thread_context_collector(settings, recorder, optional_tracer, timeline_enabled)
|
59
62
|
worker = Datadog::Profiling::Collectors::CpuAndWallTimeWorker.new(
|
60
|
-
gc_profiling_enabled: enable_gc_profiling?(settings),
|
63
|
+
gc_profiling_enabled: enable_gc_profiling?(settings, logger),
|
61
64
|
no_signals_workaround_enabled: no_signals_workaround_enabled,
|
62
65
|
thread_context_collector: thread_context_collector,
|
63
66
|
dynamic_sampling_rate_overhead_target_percentage: overhead_target_percentage,
|
64
67
|
allocation_profiling_enabled: allocation_profiling_enabled,
|
65
68
|
allocation_counting_enabled: settings.profiling.advanced.allocation_counting_enabled,
|
66
|
-
gvl_profiling_enabled: enable_gvl_profiling?(settings),
|
69
|
+
gvl_profiling_enabled: enable_gvl_profiling?(settings, logger),
|
67
70
|
)
|
68
71
|
|
69
72
|
internal_metadata = {
|
@@ -120,7 +123,7 @@ module Datadog
|
|
120
123
|
)
|
121
124
|
end
|
122
125
|
|
123
|
-
private_class_method def self.enable_gc_profiling?(settings)
|
126
|
+
private_class_method def self.enable_gc_profiling?(settings, logger)
|
124
127
|
return false unless settings.profiling.advanced.gc_enabled
|
125
128
|
|
126
129
|
# SEVERE - Only with Ractors
|
@@ -131,14 +134,14 @@ module Datadog
|
|
131
134
|
if RUBY_VERSION.start_with?("3.0.") ||
|
132
135
|
(RUBY_VERSION.start_with?("3.1.") && RUBY_VERSION < "3.1.4") ||
|
133
136
|
(RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3")
|
134
|
-
|
137
|
+
logger.warn(
|
135
138
|
"Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling GC profiling would cause " \
|
136
139
|
"crashes (https://bugs.ruby-lang.org/issues/18464). GC profiling has been disabled."
|
137
140
|
)
|
138
141
|
return false
|
139
142
|
elsif RUBY_VERSION.start_with?("3.")
|
140
|
-
|
141
|
-
"
|
143
|
+
logger.debug(
|
144
|
+
"Using Ractors may result in GC profiling unexpectedly " \
|
142
145
|
"stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
|
143
146
|
"application stability or performance. This does not happen if Ractors are not used."
|
144
147
|
)
|
@@ -155,7 +158,7 @@ module Datadog
|
|
155
158
|
heap_sample_rate
|
156
159
|
end
|
157
160
|
|
158
|
-
private_class_method def self.enable_allocation_profiling?(settings)
|
161
|
+
private_class_method def self.enable_allocation_profiling?(settings, logger)
|
159
162
|
return false unless settings.profiling.allocation_enabled
|
160
163
|
|
161
164
|
# Allocation sampling is safe and supported on Ruby 2.x, but has a few caveats on Ruby 3.x.
|
@@ -165,7 +168,7 @@ module Datadog
|
|
165
168
|
# https://github.com/ruby/ruby/pull/7464) that makes this crash in any configuration. This bug is
|
166
169
|
# fixed on Ruby versions 3.2.3 and 3.3.0.
|
167
170
|
if RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3"
|
168
|
-
|
171
|
+
logger.warn(
|
169
172
|
"Allocation profiling is not supported in Ruby versions 3.2.0, 3.2.1 and 3.2.2 and will be forcibly " \
|
170
173
|
"disabled. This is due to a VM bug that can lead to crashes (https://bugs.ruby-lang.org/issues/19482). " \
|
171
174
|
"Other Ruby versions do not suffer from this issue."
|
@@ -181,7 +184,7 @@ module Datadog
|
|
181
184
|
if RUBY_VERSION.start_with?("3.0.") ||
|
182
185
|
(RUBY_VERSION.start_with?("3.1.") && RUBY_VERSION < "3.1.4") ||
|
183
186
|
(RUBY_VERSION.start_with?("3.2.") && RUBY_VERSION < "3.2.3")
|
184
|
-
|
187
|
+
logger.warn(
|
185
188
|
"Current Ruby version (#{RUBY_VERSION}) has a VM bug where enabling allocation profiling while using " \
|
186
189
|
"Ractors may cause unexpected issues, including crashes (https://bugs.ruby-lang.org/issues/18464). " \
|
187
190
|
"This does not happen if Ractors are not used."
|
@@ -190,25 +193,27 @@ module Datadog
|
|
190
193
|
# On all known versions of Ruby 3.x, due to https://bugs.ruby-lang.org/issues/19112, when a ractor gets
|
191
194
|
# garbage collected, Ruby will disable all active tracepoints, which this feature internally relies on.
|
192
195
|
elsif RUBY_VERSION.start_with?("3.")
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
196
|
+
ALLOCATION_WITH_RACTORS_ONLY_ONCE.run do
|
197
|
+
logger.info(
|
198
|
+
"Using Ractors may result in allocation profiling " \
|
199
|
+
"stopping (https://bugs.ruby-lang.org/issues/19112). Note that this stop has no impact in your " \
|
200
|
+
"application stability or performance. This does not happen if Ractors are not used."
|
201
|
+
)
|
202
|
+
end
|
198
203
|
end
|
199
204
|
|
200
|
-
|
205
|
+
logger.debug("Enabled allocation profiling")
|
201
206
|
|
202
207
|
true
|
203
208
|
end
|
204
209
|
|
205
|
-
private_class_method def self.enable_heap_profiling?(settings, allocation_profiling_enabled, heap_sample_rate)
|
210
|
+
private_class_method def self.enable_heap_profiling?(settings, allocation_profiling_enabled, heap_sample_rate, logger)
|
206
211
|
heap_profiling_enabled = settings.profiling.advanced.experimental_heap_enabled
|
207
212
|
|
208
213
|
return false unless heap_profiling_enabled
|
209
214
|
|
210
215
|
if RUBY_VERSION < "3.1"
|
211
|
-
|
216
|
+
logger.warn(
|
212
217
|
"Current Ruby version (#{RUBY_VERSION}) cannot support heap profiling due to VM limitations. " \
|
213
218
|
"Please upgrade to Ruby >= 3.1 in order to use this feature. Heap profiling has been disabled."
|
214
219
|
)
|
@@ -219,7 +224,7 @@ module Datadog
|
|
219
224
|
raise ArgumentError, "Heap profiling requires allocation profiling to be enabled"
|
220
225
|
end
|
221
226
|
|
222
|
-
|
227
|
+
logger.warn(
|
223
228
|
"Enabled experimental heap profiling: heap_sample_rate=#{heap_sample_rate}. This is experimental, not " \
|
224
229
|
"recommended, and will increase overhead!"
|
225
230
|
)
|
@@ -227,25 +232,23 @@ module Datadog
|
|
227
232
|
true
|
228
233
|
end
|
229
234
|
|
230
|
-
private_class_method def self.enable_heap_size_profiling?(settings, heap_profiling_enabled)
|
235
|
+
private_class_method def self.enable_heap_size_profiling?(settings, heap_profiling_enabled, logger)
|
231
236
|
heap_size_profiling_enabled = settings.profiling.advanced.experimental_heap_size_enabled
|
232
237
|
|
233
238
|
return false unless heap_profiling_enabled && heap_size_profiling_enabled
|
234
239
|
|
235
|
-
|
240
|
+
logger.warn(
|
236
241
|
"Enabled experimental heap size profiling. This is experimental, not recommended, and will increase overhead!"
|
237
242
|
)
|
238
243
|
|
239
244
|
true
|
240
245
|
end
|
241
246
|
|
242
|
-
private_class_method def self.no_signals_workaround_enabled?(settings) # rubocop:disable Metrics/MethodLength
|
247
|
+
private_class_method def self.no_signals_workaround_enabled?(settings, logger) # rubocop:disable Metrics/MethodLength
|
243
248
|
setting_value = settings.profiling.advanced.no_signals_workaround_enabled
|
244
|
-
legacy_ruby_that_should_use_workaround = RUBY_VERSION.start_with?("2.5.")
|
245
249
|
|
246
250
|
unless [true, false, :auto].include?(setting_value)
|
247
|
-
|
248
|
-
Datadog.logger.error(
|
251
|
+
logger.warn(
|
249
252
|
"Ignoring invalid value for profiling no_signals_workaround_enabled setting: #{setting_value.inspect}. " \
|
250
253
|
"Valid options are `true`, `false` or (default) `:auto`."
|
251
254
|
)
|
@@ -254,23 +257,23 @@ module Datadog
|
|
254
257
|
end
|
255
258
|
|
256
259
|
if setting_value == false
|
257
|
-
if
|
258
|
-
|
259
|
-
'The profiling "no signals" workaround has been disabled via configuration on
|
260
|
-
"
|
261
|
-
"in production environments, as due to limitations in Ruby APIs, we suspect it may lead to crashes " \
|
262
|
-
"
|
260
|
+
if RUBY_VERSION.start_with?("2.5.")
|
261
|
+
logger.warn(
|
262
|
+
'The profiling "no signals" workaround has been disabled via configuration on Ruby 2.5. ' \
|
263
|
+
"This is not recommended " \
|
264
|
+
"in production environments, as due to limitations in Ruby APIs, we suspect it may lead to rare crashes " \
|
265
|
+
"Please report any issues you run into to Datadog support or " \
|
263
266
|
"via <https://github.com/datadog/dd-trace-rb/issues/new>!"
|
264
267
|
)
|
265
268
|
else
|
266
|
-
|
269
|
+
logger.warn('Profiling "no signals" workaround disabled via configuration')
|
267
270
|
end
|
268
271
|
|
269
272
|
return false
|
270
273
|
end
|
271
274
|
|
272
275
|
if setting_value == true
|
273
|
-
|
276
|
+
logger.warn(
|
274
277
|
'Profiling "no signals" workaround enabled via configuration. Profiling data will have lower quality.'
|
275
278
|
)
|
276
279
|
|
@@ -280,10 +283,10 @@ module Datadog
|
|
280
283
|
# Setting is in auto mode. Let's probe to see if we should enable it:
|
281
284
|
|
282
285
|
# We don't warn users in this situation because "upgrade your Ruby" is not a great warning
|
283
|
-
return true if
|
286
|
+
return true if RUBY_VERSION.start_with?("2.5.")
|
284
287
|
|
285
|
-
if Gem.loaded_specs["mysql2"] && incompatible_libmysqlclient_version?(settings)
|
286
|
-
|
288
|
+
if Gem.loaded_specs["mysql2"] && incompatible_libmysqlclient_version?(settings, logger)
|
289
|
+
logger.warn(
|
287
290
|
'Enabling the profiling "no signals" workaround because an incompatible version of the mysql2 gem is ' \
|
288
291
|
"installed. Profiling data will have lower quality. " \
|
289
292
|
"To fix this, upgrade the libmysqlclient in your OS image to version 8.0.0 or above."
|
@@ -292,7 +295,7 @@ module Datadog
|
|
292
295
|
end
|
293
296
|
|
294
297
|
if Gem.loaded_specs["rugged"]
|
295
|
-
|
298
|
+
logger.warn(
|
296
299
|
'Enabling the profiling "no signals" workaround because the rugged gem is installed. ' \
|
297
300
|
"This is needed because some operations on this gem are currently incompatible with the normal working mode " \
|
298
301
|
"of the profiler, as detailed in <https://github.com/datadog/dd-trace-rb/issues/2721>. " \
|
@@ -302,7 +305,7 @@ module Datadog
|
|
302
305
|
end
|
303
306
|
|
304
307
|
if (defined?(::PhusionPassenger) || Gem.loaded_specs["passenger"]) && incompatible_passenger_version?
|
305
|
-
|
308
|
+
logger.warn(
|
306
309
|
'Enabling the profiling "no signals" workaround because an incompatible version of the passenger gem is ' \
|
307
310
|
"installed. Profiling data will have lower quality." \
|
308
311
|
"To fix this, upgrade the passenger gem to version 6.0.19 or above."
|
@@ -322,10 +325,10 @@ module Datadog
|
|
322
325
|
#
|
323
326
|
# The `mysql2` gem's `info` method can be used to determine which `libmysqlclient` version is in use, and thus to
|
324
327
|
# detect if it's safe for the profiler to use signals or if we need to employ a fallback.
|
325
|
-
private_class_method def self.incompatible_libmysqlclient_version?(settings)
|
328
|
+
private_class_method def self.incompatible_libmysqlclient_version?(settings, logger)
|
326
329
|
return true if settings.profiling.advanced.skip_mysql2_check
|
327
330
|
|
328
|
-
|
331
|
+
logger.debug(
|
329
332
|
"Requiring `mysql2` to check if the `libmysqlclient` version it uses is compatible with profiling"
|
330
333
|
)
|
331
334
|
|
@@ -354,14 +357,14 @@ module Datadog
|
|
354
357
|
libmysqlclient_version >= Gem::Version.new("8.0.0") ||
|
355
358
|
looks_like_mariadb?(info, libmysqlclient_version)
|
356
359
|
|
357
|
-
|
360
|
+
logger.debug(
|
358
361
|
"The `mysql2` gem is using #{compatible ? "a compatible" : "an incompatible"} version of " \
|
359
362
|
"the `libmysqlclient` library (#{libmysqlclient_version})"
|
360
363
|
)
|
361
364
|
|
362
365
|
!compatible
|
363
366
|
rescue StandardError, LoadError => e
|
364
|
-
|
367
|
+
logger.warn(
|
365
368
|
"Failed to probe `mysql2` gem information. " \
|
366
369
|
"Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
|
367
370
|
)
|
@@ -383,12 +386,11 @@ module Datadog
|
|
383
386
|
end
|
384
387
|
end
|
385
388
|
|
386
|
-
private_class_method def self.valid_overhead_target(overhead_target_percentage)
|
389
|
+
private_class_method def self.valid_overhead_target(overhead_target_percentage, logger)
|
387
390
|
if overhead_target_percentage > 0 && overhead_target_percentage <= 20
|
388
391
|
overhead_target_percentage
|
389
392
|
else
|
390
|
-
|
391
|
-
Datadog.logger.error(
|
393
|
+
logger.warn(
|
392
394
|
"Ignoring invalid value for profiling overhead_target_percentage setting: " \
|
393
395
|
"#{overhead_target_percentage.inspect}. Falling back to default value."
|
394
396
|
)
|
@@ -432,10 +434,10 @@ module Datadog
|
|
432
434
|
settings.profiling.advanced.dir_interruption_workaround_enabled
|
433
435
|
end
|
434
436
|
|
435
|
-
private_class_method def self.enable_gvl_profiling?(settings)
|
437
|
+
private_class_method def self.enable_gvl_profiling?(settings, logger)
|
436
438
|
if RUBY_VERSION < "3.2"
|
437
439
|
if settings.profiling.advanced.preview_gvl_enabled
|
438
|
-
|
440
|
+
logger.warn("GVL profiling is currently not supported in Ruby < 3.2 and will not be enabled.")
|
439
441
|
end
|
440
442
|
|
441
443
|
return false
|
@@ -13,13 +13,11 @@ module Datadog
|
|
13
13
|
def initialize(agent_settings:, site:, api_key:, upload_timeout_seconds:)
|
14
14
|
@upload_timeout_milliseconds = (upload_timeout_seconds * 1_000).to_i
|
15
15
|
|
16
|
-
validate_agent_settings(agent_settings)
|
17
|
-
|
18
16
|
@exporter_configuration =
|
19
17
|
if agentless?(site, api_key)
|
20
18
|
[:agentless, site, api_key].freeze
|
21
19
|
else
|
22
|
-
[:agent,
|
20
|
+
[:agent, agent_settings.url].freeze
|
23
21
|
end
|
24
22
|
|
25
23
|
status, result = validate_exporter(exporter_configuration)
|
@@ -75,29 +73,6 @@ module Datadog
|
|
75
73
|
|
76
74
|
private
|
77
75
|
|
78
|
-
def base_url_from(agent_settings)
|
79
|
-
case agent_settings.adapter
|
80
|
-
when Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
|
81
|
-
"#{agent_settings.ssl ? "https" : "http"}://#{agent_settings.hostname}:#{agent_settings.port}/"
|
82
|
-
when Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER
|
83
|
-
"unix://#{agent_settings.uds_path}"
|
84
|
-
else
|
85
|
-
raise ArgumentError, "Unexpected adapter: #{agent_settings.adapter}"
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def validate_agent_settings(agent_settings)
|
90
|
-
supported_adapters = [
|
91
|
-
Datadog::Core::Configuration::Ext::Agent::UnixSocket::ADAPTER,
|
92
|
-
Datadog::Core::Configuration::Ext::Agent::HTTP::ADAPTER
|
93
|
-
]
|
94
|
-
unless supported_adapters.include?(agent_settings.adapter)
|
95
|
-
raise ArgumentError,
|
96
|
-
"Unsupported transport configuration for profiling: Adapter #{agent_settings.adapter} " \
|
97
|
-
" is not supported"
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
76
|
def agentless?(site, api_key)
|
102
77
|
site && api_key && Core::Environment::VariableHelpers.env_to_bool(Profiling::Ext::ENV_AGENTLESS, false)
|
103
78
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require_relative '../integration'
|
4
4
|
require_relative 'configuration/settings'
|
5
5
|
require_relative 'patcher'
|
6
|
-
require_relative '
|
6
|
+
require_relative '../../../core/contrib/rails/utils'
|
7
7
|
|
8
8
|
module Datadog
|
9
9
|
module Tracing
|
@@ -17,6 +17,9 @@ module Datadog
|
|
17
17
|
|
18
18
|
# @public_api Changing the integration name or integration options can cause breaking changes
|
19
19
|
register_as :action_cable, auto_patch: false
|
20
|
+
def self.gem_name
|
21
|
+
'actioncable'
|
22
|
+
end
|
20
23
|
|
21
24
|
def self.version
|
22
25
|
Gem.loaded_specs['actioncable'] && Gem.loaded_specs['actioncable'].version
|
@@ -33,7 +36,7 @@ module Datadog
|
|
33
36
|
# enabled by rails integration so should only auto instrument
|
34
37
|
# if detected that it is being used without rails
|
35
38
|
def auto_instrument?
|
36
|
-
!Contrib::Rails::Utils.railtie_supported?
|
39
|
+
!Core::Contrib::Rails::Utils.railtie_supported?
|
37
40
|
end
|
38
41
|
|
39
42
|
def new_configuration
|