datadog 2.6.0 → 2.7.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 +20 -1
- data/ext/libdatadog_api/crashtracker.c +6 -4
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/core/configuration/settings.rb +4 -4
- data/lib/datadog/di/code_tracker.rb +30 -3
- data/lib/datadog/di/component.rb +108 -0
- data/lib/datadog/di/configuration/settings.rb +69 -44
- data/lib/datadog/di/contrib/active_record.rb +11 -0
- data/lib/datadog/di/error.rb +17 -0
- data/lib/datadog/di/instrumenter.rb +27 -11
- data/lib/datadog/di/probe.rb +23 -1
- data/lib/datadog/di/probe_manager.rb +246 -0
- data/lib/datadog/di/probe_notification_builder.rb +4 -12
- data/lib/datadog/di/probe_notifier_worker.rb +68 -41
- data/lib/datadog/di/serializer.rb +143 -95
- data/lib/datadog/di/transport.rb +22 -9
- data/lib/datadog/di.rb +49 -1
- data/lib/datadog/version.rb +1 -1
- metadata +10 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c42d47b95292bfbb455a015e67700b3b8e0be052d8cfbe6f0927b6bc6602899b
|
4
|
+
data.tar.gz: ebcdd9d8d306a0edb62276a4598b4c1c80c6eeaf813ccc9d52bfea464896ef74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e071e16635f146c09550e0a835eefa1c61875339c9a64ce7283932fd646cc4401e7ea1acc0e8d0c7f7b60a20e519cce5f90eb23efdb8671b5372d02367f7bc95
|
7
|
+
data.tar.gz: 696bf465fe94fbfe76539a513b8d143373a42100ffd236b96317c5f3ffef1251d9ba3969048ad64cb87dc267dc1d7360bb1d3126a0ce9157fd3e53c5283d2130
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,21 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [2.7.0] - 2024-11-13
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
* Profiling: Enable "heap clean after GC" profiler optimization by default ([#4085][])
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
* Enable crashtracking by default ([#4083][])
|
14
|
+
* Upgrade to `libdatadog` 14.1 ([#4082][])
|
15
|
+
|
16
|
+
### Fixed
|
17
|
+
|
18
|
+
* Fix `Process.waitall` hanging and stack overflow when crashtracking enabled ([#4082][])
|
19
|
+
|
5
20
|
## [2.6.0] - 2024-11-06
|
6
21
|
|
7
22
|
### Changed
|
@@ -3013,7 +3028,8 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
|
|
3013
3028
|
Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
3014
3029
|
|
3015
3030
|
|
3016
|
-
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.
|
3031
|
+
[Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v2.7.0...master
|
3032
|
+
[2.7.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.6.0...v2.7.0
|
3017
3033
|
[2.6.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.5.0...v2.6.0
|
3018
3034
|
[2.5.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.4.0...v2.5.0
|
3019
3035
|
[2.4.0]: https://github.com/DataDog/dd-trace-rb/compare/v2.3.0...v2.4.0
|
@@ -4458,6 +4474,9 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
|
|
4458
4474
|
[#4033]: https://github.com/DataDog/dd-trace-rb/issues/4033
|
4459
4475
|
[#4065]: https://github.com/DataDog/dd-trace-rb/issues/4065
|
4460
4476
|
[#4078]: https://github.com/DataDog/dd-trace-rb/issues/4078
|
4477
|
+
[#4082]: https://github.com/DataDog/dd-trace-rb/issues/4082
|
4478
|
+
[#4083]: https://github.com/DataDog/dd-trace-rb/issues/4083
|
4479
|
+
[#4085]: https://github.com/DataDog/dd-trace-rb/issues/4085
|
4461
4480
|
[@AdrianLC]: https://github.com/AdrianLC
|
4462
4481
|
[@Azure7111]: https://github.com/Azure7111
|
4463
4482
|
[@BabyGroot]: https://github.com/BabyGroot
|
@@ -58,16 +58,18 @@ static VALUE _native_start_or_update_on_fork(int argc, VALUE *argv, DDTRACE_UNUS
|
|
58
58
|
|
59
59
|
ddog_crasht_Config config = {
|
60
60
|
.additional_files = {},
|
61
|
-
// The Ruby VM already uses an alt stack to detect stack overflows
|
61
|
+
// @ivoanjo: The Ruby VM already uses an alt stack to detect stack overflows.
|
62
62
|
//
|
63
|
-
//
|
63
|
+
// In libdatadog < 14 with `create_alt_stack = true` I saw a segfault, such as Ruby 2.6's bug with
|
64
64
|
// "Process.detach(fork { exit! }).instance_variable_get(:@foo)" being turned into a
|
65
65
|
// "-e:1:in `instance_variable_get': stack level too deep (SystemStackError)" by Ruby.
|
66
|
-
//
|
67
66
|
// The Ruby crash handler also seems to get confused when this option is enabled and
|
68
67
|
// "Process.kill('SEGV', Process.pid)" gets run.
|
68
|
+
//
|
69
|
+
// This actually changed in libdatadog 14, so I could see no issues with `create_alt_stack = true`, but not
|
70
|
+
// overridding what Ruby set up seems a saner default to keep anyway.
|
69
71
|
.create_alt_stack = false,
|
70
|
-
.use_alt_stack = true,
|
72
|
+
.use_alt_stack = true,
|
71
73
|
.endpoint = endpoint,
|
72
74
|
.resolve_frames = DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER,
|
73
75
|
.timeout_ms = FIX2INT(upload_timeout_seconds) * 1000,
|
@@ -8,7 +8,7 @@ module Datadog
|
|
8
8
|
module LibdatadogExtconfHelpers
|
9
9
|
# Used to make sure the correct gem version gets loaded, as extconf.rb does not get run with "bundle exec" and thus
|
10
10
|
# may see multiple libdatadog versions. See https://github.com/DataDog/dd-trace-rb/pull/2531 for the horror story.
|
11
|
-
LIBDATADOG_VERSION = '~> 14.
|
11
|
+
LIBDATADOG_VERSION = '~> 14.1.0.1.0'
|
12
12
|
|
13
13
|
# Used as an workaround for a limitation with how dynamic linking works in environments where the datadog gem and
|
14
14
|
# libdatadog are moved after the extension gets compiled.
|
@@ -518,13 +518,13 @@ module Datadog
|
|
518
518
|
# Controls if the heap profiler should attempt to clean young objects after GC, rather than just at
|
519
519
|
# serialization time. This lowers memory usage and high percentile latency.
|
520
520
|
#
|
521
|
-
# Only
|
521
|
+
# Only has effect when used together with `gc_enabled: true` and `experimental_heap_enabled: true`.
|
522
522
|
#
|
523
|
-
# @default
|
523
|
+
# @default true
|
524
524
|
option :heap_clean_after_gc_enabled do |o|
|
525
525
|
o.type :bool
|
526
526
|
o.env 'DD_PROFILING_HEAP_CLEAN_AFTER_GC_ENABLED'
|
527
|
-
o.default
|
527
|
+
o.default true
|
528
528
|
end
|
529
529
|
end
|
530
530
|
|
@@ -927,7 +927,7 @@ module Datadog
|
|
927
927
|
# Enables reporting of information when Ruby VM crashes.
|
928
928
|
option :enabled do |o|
|
929
929
|
o.type :bool
|
930
|
-
o.default
|
930
|
+
o.default true
|
931
931
|
o.env 'DD_CRASHTRACKING_ENABLED'
|
932
932
|
end
|
933
933
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# rubocop:disable Lint/AssignmentInCondition
|
4
|
+
|
3
5
|
module Datadog
|
4
6
|
module DI
|
5
7
|
# Tracks loaded Ruby code by source file and maintains a map from
|
@@ -77,6 +79,26 @@ module Datadog
|
|
77
79
|
registry[path] = tp.instruction_sequence
|
78
80
|
end
|
79
81
|
end
|
82
|
+
|
83
|
+
DI.component&.probe_manager&.install_pending_line_probes(path)
|
84
|
+
|
85
|
+
# Since this method normally is called from customer applications,
|
86
|
+
# rescue any exceptions that might not be handled to not break said
|
87
|
+
# customer applications.
|
88
|
+
rescue => exc
|
89
|
+
# TODO we do not have DI.component defined yet, remove steep:ignore
|
90
|
+
# before release.
|
91
|
+
if component = DI.component # steep:ignore
|
92
|
+
raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
93
|
+
component.logger.warn("Unhandled exception in script_compiled trace point: #{exc.class}: #{exc}")
|
94
|
+
component.telemetry&.report(exc, description: "Unhandled exception in script_compiled trace point")
|
95
|
+
# TODO test this path
|
96
|
+
else
|
97
|
+
# If we don't have a component, we cannot log anything properly.
|
98
|
+
# Do not just print a warning to avoid spamming customer logs.
|
99
|
+
# Don't reraise the exception either.
|
100
|
+
# TODO test this path
|
101
|
+
end
|
80
102
|
end
|
81
103
|
end
|
82
104
|
end
|
@@ -112,15 +134,18 @@ module Datadog
|
|
112
134
|
def iseqs_for_path_suffix(suffix)
|
113
135
|
registry_lock.synchronize do
|
114
136
|
exact = registry[suffix]
|
115
|
-
return [exact] if exact
|
137
|
+
return [suffix, exact] if exact
|
116
138
|
|
117
139
|
inexact = []
|
118
140
|
registry.each do |path, iseq|
|
119
141
|
if Utils.path_matches_suffix?(path, suffix)
|
120
|
-
inexact << iseq
|
142
|
+
inexact << [path, iseq]
|
121
143
|
end
|
122
144
|
end
|
123
|
-
inexact
|
145
|
+
if inexact.length > 1
|
146
|
+
raise Error::MultiplePathsMatch, "Multiple paths matched requested suffix"
|
147
|
+
end
|
148
|
+
inexact.first
|
124
149
|
end
|
125
150
|
end
|
126
151
|
|
@@ -164,3 +189,5 @@ module Datadog
|
|
164
189
|
end
|
165
190
|
end
|
166
191
|
end
|
192
|
+
|
193
|
+
# rubocop:enable Lint/AssignmentInCondition
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module DI
|
5
|
+
# Component for dynamic instrumentation.
|
6
|
+
#
|
7
|
+
# Only one instance of the Component should ever be active;
|
8
|
+
# if configuration is changed, the old distance should be shut down
|
9
|
+
# prior to the new instance being created.
|
10
|
+
#
|
11
|
+
# The Component instance stores all state related to DI, for example
|
12
|
+
# which probes have been retrieved via remote config,
|
13
|
+
# intalled tracepoints and so on. Component will clean up all
|
14
|
+
# resources and installed tracepoints upon shutdown.
|
15
|
+
class Component
|
16
|
+
class << self
|
17
|
+
def build(settings, agent_settings, telemetry: nil)
|
18
|
+
return unless settings.respond_to?(:dynamic_instrumentation) && settings.dynamic_instrumentation.enabled
|
19
|
+
|
20
|
+
unless settings.respond_to?(:remote) && settings.remote.enabled
|
21
|
+
Datadog.logger.debug("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
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
return unless environment_supported?(settings)
|
26
|
+
|
27
|
+
new(settings, agent_settings, Datadog.logger, code_tracker: DI.code_tracker, telemetry: telemetry)
|
28
|
+
end
|
29
|
+
|
30
|
+
def build!(settings, agent_settings, telemetry: nil)
|
31
|
+
unless settings.respond_to?(:dynamic_instrumentation) && settings.dynamic_instrumentation.enabled
|
32
|
+
raise "Requested DI component but DI is not enabled in settings"
|
33
|
+
end
|
34
|
+
|
35
|
+
unless settings.respond_to?(:remote) && settings.remote.enabled
|
36
|
+
raise "Requested DI component but remote config is not enabled in settings"
|
37
|
+
end
|
38
|
+
|
39
|
+
unless environment_supported?(settings)
|
40
|
+
raise "DI does not support the environment (development or Ruby version too low or not MRI)"
|
41
|
+
end
|
42
|
+
|
43
|
+
new(settings, agent_settings, Datadog.logger, code_tracker: DI.code_tracker, telemetry: telemetry)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Checks whether the runtime environment is supported by
|
47
|
+
# dynamic instrumentation. Currently we only require that, if Rails
|
48
|
+
# is used, that Rails environment is not development because
|
49
|
+
# DI does not currently support code unloading and reloading.
|
50
|
+
def environment_supported?(settings)
|
51
|
+
# TODO add tests?
|
52
|
+
unless settings.dynamic_instrumentation.internal.development
|
53
|
+
if Datadog::Core::Environment::Execution.development?
|
54
|
+
Datadog.logger.debug("Not enabling dynamic instrumentation because we are in development environment")
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
if RUBY_ENGINE != 'ruby' || RUBY_VERSION < '2.6'
|
59
|
+
Datadog.logger.debug("Not enabling dynamic instrumentation because of unsupported Ruby version")
|
60
|
+
return false
|
61
|
+
end
|
62
|
+
true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def initialize(settings, agent_settings, logger, code_tracker: nil, telemetry: nil)
|
67
|
+
@settings = settings
|
68
|
+
@agent_settings = agent_settings
|
69
|
+
@logger = logger
|
70
|
+
@telemetry = telemetry
|
71
|
+
@redactor = Redactor.new(settings)
|
72
|
+
@serializer = Serializer.new(settings, redactor, telemetry: telemetry)
|
73
|
+
@instrumenter = Instrumenter.new(settings, serializer, logger, code_tracker: code_tracker, telemetry: telemetry)
|
74
|
+
@transport = Transport.new(agent_settings)
|
75
|
+
@probe_notifier_worker = ProbeNotifierWorker.new(settings, transport, logger, telemetry: telemetry)
|
76
|
+
@probe_notification_builder = ProbeNotificationBuilder.new(settings, serializer)
|
77
|
+
@probe_manager = ProbeManager.new(settings, instrumenter, probe_notification_builder, probe_notifier_worker, logger, telemetry: telemetry)
|
78
|
+
probe_notifier_worker.start
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_reader :settings
|
82
|
+
attr_reader :agent_settings
|
83
|
+
attr_reader :logger
|
84
|
+
attr_reader :telemetry
|
85
|
+
attr_reader :instrumenter
|
86
|
+
attr_reader :transport
|
87
|
+
attr_reader :probe_notifier_worker
|
88
|
+
attr_reader :probe_notification_builder
|
89
|
+
attr_reader :probe_manager
|
90
|
+
attr_reader :redactor
|
91
|
+
attr_reader :serializer
|
92
|
+
|
93
|
+
# Shuts down dynamic instrumentation.
|
94
|
+
#
|
95
|
+
# Removes all code hooks and stops background threads.
|
96
|
+
#
|
97
|
+
# Does not clear out the code tracker, because it's only populated
|
98
|
+
# by code when code is compiled and therefore, if the code tracker
|
99
|
+
# was replaced by a new instance, the new instance of it wouldn't have
|
100
|
+
# any of the already loaded code tracked.
|
101
|
+
def shutdown!(replacement = nil)
|
102
|
+
probe_manager.clear_hooks
|
103
|
+
probe_manager.close
|
104
|
+
probe_notifier_worker.stop
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -12,8 +12,6 @@ module Datadog
|
|
12
12
|
|
13
13
|
def self.add_settings!(base)
|
14
14
|
base.class_eval do
|
15
|
-
# The setting has "internal" prefix to prevent it from being
|
16
|
-
# prematurely turned on by customers.
|
17
15
|
settings :dynamic_instrumentation do
|
18
16
|
option :enabled do |o|
|
19
17
|
o.type :bool
|
@@ -26,48 +24,6 @@ module Datadog
|
|
26
24
|
o.default false
|
27
25
|
end
|
28
26
|
|
29
|
-
# This option instructs dynamic instrumentation to use
|
30
|
-
# untargeted trace points when installing line probes and
|
31
|
-
# code tracking is not active.
|
32
|
-
# WARNING: untargeted trace points carry a massive performance
|
33
|
-
# penalty for the entire file in which a line probe is placed.
|
34
|
-
#
|
35
|
-
# If this option is set to false, which is the default,
|
36
|
-
# dynamic instrumentation will add probes that reference
|
37
|
-
# unknown files to the list of pending probes, and when
|
38
|
-
# the respective files are loaded, the line probes will be
|
39
|
-
# installed using targeted trace points. If the file in
|
40
|
-
# question is already loaded when the probe is received
|
41
|
-
# (for example, it is in a third-party library loaded during
|
42
|
-
# application boot), and code tracking was not active when
|
43
|
-
# the file was loaded, such files will not be instrumentable
|
44
|
-
# via line probes.
|
45
|
-
#
|
46
|
-
# If this option is set to true
|
47
|
-
#
|
48
|
-
# activated, DI will in
|
49
|
-
# activated or because the files being targeted have beenIf true and code tracking is not enabled, dynamic instrumentation
|
50
|
-
# will use untargeted trace points.
|
51
|
-
# If false and code tracking is not enabled, dynamic
|
52
|
-
# instrumentation will not instrument any files loaded
|
53
|
-
# WARNING: these trace points will greatly degrade performance
|
54
|
-
# of all code in the instrumented files.
|
55
|
-
option :untargeted_trace_points do |o|
|
56
|
-
o.type :bool
|
57
|
-
o.default false
|
58
|
-
end
|
59
|
-
|
60
|
-
# If true, all of the catch-all rescue blocks in DI
|
61
|
-
# will propagate the exceptions onward.
|
62
|
-
# WARNING: for internal Datadog use only - this will break
|
63
|
-
# the DI product and potentially the library in general in
|
64
|
-
# a multitude of ways, cause resource leakage, permanent
|
65
|
-
# performance decreases, etc.
|
66
|
-
option :propagate_all_exceptions do |o|
|
67
|
-
o.type :bool
|
68
|
-
o.default false
|
69
|
-
end
|
70
|
-
|
71
27
|
# An array of variable and key names to redact in addition to
|
72
28
|
# the built-in list of identifiers.
|
73
29
|
#
|
@@ -154,6 +110,75 @@ module Datadog
|
|
154
110
|
o.type :int
|
155
111
|
o.default 20
|
156
112
|
end
|
113
|
+
|
114
|
+
# Settings in the 'internal' group are for internal Datadog
|
115
|
+
# use only, and are needed to test dynamic instrumentation or
|
116
|
+
# experiment with features not released to customers.
|
117
|
+
settings :internal do
|
118
|
+
# This option instructs dynamic instrumentation to use
|
119
|
+
# untargeted trace points when installing line probes and
|
120
|
+
# code tracking is not active.
|
121
|
+
# WARNING: untargeted trace points carry a massive performance
|
122
|
+
# penalty for the entire file in which a line probe is placed.
|
123
|
+
#
|
124
|
+
# If this option is set to false, which is the default,
|
125
|
+
# dynamic instrumentation will add probes that reference
|
126
|
+
# unknown files to the list of pending probes, and when
|
127
|
+
# the respective files are loaded, the line probes will be
|
128
|
+
# installed using targeted trace points. If the file in
|
129
|
+
# question is already loaded when the probe is received
|
130
|
+
# (for example, it is in a third-party library loaded during
|
131
|
+
# application boot), and code tracking was not active when
|
132
|
+
# the file was loaded, such files will not be instrumentable
|
133
|
+
# via line probes.
|
134
|
+
#
|
135
|
+
# If this option is set to true, dynamic instrumentation will
|
136
|
+
# install untargeted trace points for all line probes,
|
137
|
+
# regardless of whether the referenced file is loaded.
|
138
|
+
# This permits instrumenting code which was loaded prior to
|
139
|
+
# code tracking being activated and instrumenting lines when
|
140
|
+
# code tracking is not activated at all. However, untargeted
|
141
|
+
# trace points are extremely slow and will greatly degrade
|
142
|
+
# performance of *all* code executed while they are installed,
|
143
|
+
# not just the instrumentation target.
|
144
|
+
option :untargeted_trace_points do |o|
|
145
|
+
o.type :bool
|
146
|
+
o.default false
|
147
|
+
end
|
148
|
+
|
149
|
+
# If true, all of the catch-all rescue blocks in DI
|
150
|
+
# will propagate the exceptions onward.
|
151
|
+
# WARNING: for internal Datadog use only - this will break
|
152
|
+
# the DI product and potentially the library in general in
|
153
|
+
# a multitude of ways, cause resource leakage, permanent
|
154
|
+
# performance decreases, etc.
|
155
|
+
option :propagate_all_exceptions do |o|
|
156
|
+
o.type :bool
|
157
|
+
o.default false
|
158
|
+
end
|
159
|
+
|
160
|
+
# Minimum interval, in seconds, between probe status and
|
161
|
+
# snapshot submissions to the agent. Probe notifier worker will
|
162
|
+
# batch together payloads submitted during each interval.
|
163
|
+
# A longer interval reduces the overhead imposed by dynamic
|
164
|
+
# instrumentation on the application, but also increases the
|
165
|
+
# time when application code cannot run (when the batches are
|
166
|
+
# being sent out by the probe notifier worker) and creates a
|
167
|
+
# possibility of dropping payloads if the queue gets too long.
|
168
|
+
option :min_send_interval do |o|
|
169
|
+
o.type :int
|
170
|
+
o.default 3
|
171
|
+
end
|
172
|
+
|
173
|
+
# Enable dynamic instrumentation in development environments.
|
174
|
+
# Currently DI does not fully implement support for code
|
175
|
+
# unloading and reloading, and is not supported in
|
176
|
+
# non-production environments.
|
177
|
+
option :development do |o|
|
178
|
+
o.type :bool
|
179
|
+
o.default false
|
180
|
+
end
|
181
|
+
end
|
157
182
|
end
|
158
183
|
end
|
159
184
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Datadog::DI::Serializer.register(condition: lambda { |value| ActiveRecord::Base === value }) do |serializer, value, name:, depth:| # steep:ignore
|
4
|
+
# steep thinks all of the arguments are nil here
|
5
|
+
# steep:ignore:start
|
6
|
+
value_to_serialize = {
|
7
|
+
attributes: value.attributes,
|
8
|
+
}
|
9
|
+
serializer.serialize_value(value_to_serialize, depth: depth ? depth - 1 : nil, type: value.class)
|
10
|
+
# steep:ignore:end
|
11
|
+
end
|
data/lib/datadog/di/error.rb
CHANGED
@@ -26,6 +26,23 @@ module Datadog
|
|
26
26
|
# that does not in fact exist anywhere (e.g. due to a misspelling).
|
27
27
|
class DITargetNotDefined < Error
|
28
28
|
end
|
29
|
+
|
30
|
+
# Raised when trying to install a probe whose installation failed
|
31
|
+
# earlier in the same process. This exception should contain the
|
32
|
+
# original exception report from initial installation attempt.
|
33
|
+
class ProbePreviouslyFailed < Error
|
34
|
+
end
|
35
|
+
|
36
|
+
# Raised when installing a line probe and multiple files match the
|
37
|
+
# specified path suffix.
|
38
|
+
# A probe must be installed into one file only, since UI only
|
39
|
+
# supports one instrumented location for a probe.
|
40
|
+
# If multiple files match, UI cannot properly render the data from
|
41
|
+
# all of them, and arbitrarily choosing one file may be not what the
|
42
|
+
# user intended. Instrumentation will fail when multiple files match
|
43
|
+
# and the user will need to make their suffix more precise.
|
44
|
+
class MultiplePathsMatch < Error
|
45
|
+
end
|
29
46
|
end
|
30
47
|
end
|
31
48
|
end
|
@@ -54,10 +54,11 @@ module Datadog
|
|
54
54
|
#
|
55
55
|
# @api private
|
56
56
|
class Instrumenter
|
57
|
-
def initialize(settings, serializer, logger, code_tracker: nil)
|
57
|
+
def initialize(settings, serializer, logger, code_tracker: nil, telemetry: nil)
|
58
58
|
@settings = settings
|
59
59
|
@serializer = serializer
|
60
60
|
@logger = logger
|
61
|
+
@telemetry = telemetry
|
61
62
|
@code_tracker = code_tracker
|
62
63
|
|
63
64
|
@lock = Mutex.new
|
@@ -66,6 +67,7 @@ module Datadog
|
|
66
67
|
attr_reader :settings
|
67
68
|
attr_reader :serializer
|
68
69
|
attr_reader :logger
|
70
|
+
attr_reader :telemetry
|
69
71
|
attr_reader :code_tracker
|
70
72
|
|
71
73
|
# This is a substitute for Thread::Backtrace::Location
|
@@ -172,12 +174,12 @@ module Datadog
|
|
172
174
|
# we use mock objects and the methods may be mocked with
|
173
175
|
# individual invocations, yielding different return values on
|
174
176
|
# different calls to the same method.
|
175
|
-
permit_untargeted_trace_points = settings.dynamic_instrumentation.untargeted_trace_points
|
177
|
+
permit_untargeted_trace_points = settings.dynamic_instrumentation.internal.untargeted_trace_points
|
176
178
|
|
177
179
|
iseq = nil
|
178
180
|
if code_tracker
|
179
|
-
|
180
|
-
unless
|
181
|
+
ret = code_tracker.iseqs_for_path_suffix(probe.file) # steep:ignore
|
182
|
+
unless ret
|
181
183
|
if permit_untargeted_trace_points
|
182
184
|
# Continue withoout targeting the trace point.
|
183
185
|
# This is going to cause a serious performance penalty for
|
@@ -204,6 +206,10 @@ module Datadog
|
|
204
206
|
raise Error::DITargetNotDefined, "File not in code tracker registry: #{probe.file}"
|
205
207
|
end
|
206
208
|
|
209
|
+
if ret
|
210
|
+
actual_path, iseq = ret
|
211
|
+
end
|
212
|
+
|
207
213
|
# If trace point is not targeted, we only need one trace point per file.
|
208
214
|
# Creating a trace point for each probe does work but the performance
|
209
215
|
# penalty will be taken for each trace point defined in the file.
|
@@ -217,18 +223,26 @@ module Datadog
|
|
217
223
|
# this optimization just yet and create a trace point for each probe.
|
218
224
|
|
219
225
|
tp = TracePoint.new(:line) do |tp|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
if
|
225
|
-
|
226
|
-
|
226
|
+
begin
|
227
|
+
# If trace point is not targeted, we must verify that the invocation
|
228
|
+
# is the file & line that we want, because untargeted trace points
|
229
|
+
# are invoked for *each* line of Ruby executed.
|
230
|
+
if iseq || tp.lineno == probe.line_no && probe.file_matches?(tp.path)
|
231
|
+
if rate_limiter.nil? || rate_limiter.allow?
|
232
|
+
# & is to stop steep complaints, block is always present here.
|
233
|
+
block&.call(probe: probe, trace_point: tp, caller_locations: caller_locations)
|
234
|
+
end
|
227
235
|
end
|
236
|
+
rescue => exc
|
237
|
+
raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
|
238
|
+
logger.warn("Unhandled exception in line trace point: #{exc.class}: #{exc}")
|
239
|
+
telemetry&.report(exc, description: "Unhandled exception in line trace point")
|
240
|
+
# TODO test this path
|
228
241
|
end
|
229
242
|
rescue => exc
|
230
243
|
raise if settings.dynamic_instrumentation.propagate_all_exceptions
|
231
244
|
logger.warn("Unhandled exception in line trace point: #{exc.class}: #{exc}")
|
245
|
+
telemetry&.report(exc, description: "Unhandled exception in line trace point")
|
232
246
|
# TODO test this path
|
233
247
|
end
|
234
248
|
|
@@ -244,6 +258,8 @@ module Datadog
|
|
244
258
|
end
|
245
259
|
|
246
260
|
probe.instrumentation_trace_point = tp
|
261
|
+
# actual_path could be nil if we don't use targeted trace points.
|
262
|
+
probe.instrumented_path = actual_path
|
247
263
|
|
248
264
|
if iseq
|
249
265
|
tp.enable(target: iseq, target_line: line_no)
|
data/lib/datadog/di/probe.rb
CHANGED
@@ -47,6 +47,10 @@ module Datadog
|
|
47
47
|
raise ArgumentError, "Probe contains both line number and method name: #{id}"
|
48
48
|
end
|
49
49
|
|
50
|
+
if line_no && !file
|
51
|
+
raise ArgumentError, "Probe contains line number but not file: #{id}"
|
52
|
+
end
|
53
|
+
|
50
54
|
if type_name && !method_name || method_name && !type_name
|
51
55
|
raise ArgumentError, "Partial method probe definition: #{id}"
|
52
56
|
end
|
@@ -71,6 +75,8 @@ module Datadog
|
|
71
75
|
|
72
76
|
@rate_limit = rate_limit || (@capture_snapshot ? 1 : 5000)
|
73
77
|
@rate_limiter = Datadog::Core::TokenBucket.new(@rate_limit)
|
78
|
+
|
79
|
+
@emitting_notified = false
|
74
80
|
end
|
75
81
|
|
76
82
|
attr_reader :id
|
@@ -101,7 +107,10 @@ module Datadog
|
|
101
107
|
# method or for stack traversal purposes?), therefore we do not check
|
102
108
|
# for file name/path presence here and just consider the line number.
|
103
109
|
def line?
|
104
|
-
|
110
|
+
# Constructor checks that file is given if line number is given,
|
111
|
+
# but for safety, check again here since we somehow got a probe with
|
112
|
+
# a line number but no file in the wild.
|
113
|
+
!!(file && line_no)
|
105
114
|
end
|
106
115
|
|
107
116
|
# Returns whether the probe is a method probe.
|
@@ -157,6 +166,19 @@ module Datadog
|
|
157
166
|
# Line trace point for line probes. Normally this would be a targeted
|
158
167
|
# trace point.
|
159
168
|
attr_accessor :instrumentation_trace_point
|
169
|
+
|
170
|
+
# Actual path to the file instrumented by the probe, for line probes,
|
171
|
+
# when code tracking is available and line trace point is targeted.
|
172
|
+
# For untargeted line trace points instrumented path will be nil.
|
173
|
+
attr_accessor :instrumented_path
|
174
|
+
|
175
|
+
# TODO emitting_notified reads and writes should in theory be locked,
|
176
|
+
# however since DI is only implemented for MRI in practice the missing
|
177
|
+
# locking should not cause issues.
|
178
|
+
attr_writer :emitting_notified
|
179
|
+
def emitting_notified?
|
180
|
+
!!@emitting_notified
|
181
|
+
end
|
160
182
|
end
|
161
183
|
end
|
162
184
|
end
|