datadog 2.36.0 → 2.37.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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -1
  3. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +5 -12
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +6 -3
  5. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +17 -3
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +219 -98
  7. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +2 -0
  8. data/ext/datadog_profiling_native_extension/extconf.rb +11 -0
  9. data/ext/datadog_profiling_native_extension/ruby_helpers.c +7 -0
  10. data/ext/libdatadog_api/di.c +76 -0
  11. data/ext/libdatadog_api/extconf.rb +6 -0
  12. data/ext/libdatadog_extconf_helpers.rb +1 -1
  13. data/lib/datadog/ai_guard/api_client.rb +6 -4
  14. data/lib/datadog/appsec/api_security/route_extractor.rb +7 -4
  15. data/lib/datadog/appsec/configuration.rb +6 -0
  16. data/lib/datadog/appsec/context.rb +4 -2
  17. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +59 -4
  18. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +59 -5
  19. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +1 -1
  20. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +4 -0
  21. data/lib/datadog/appsec/contrib/rack/input_peeker.rb +4 -17
  22. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +21 -0
  23. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +2 -1
  24. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +60 -7
  25. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +4 -0
  26. data/lib/datadog/appsec/ext.rb +1 -0
  27. data/lib/datadog/appsec/metrics/collector.rb +18 -1
  28. data/lib/datadog/appsec/metrics/exporter.rb +9 -3
  29. data/lib/datadog/appsec/remote.rb +9 -3
  30. data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
  31. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
  32. data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
  33. data/lib/datadog/appsec/route_normalizer.rb +80 -0
  34. data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
  35. data/lib/datadog/core/configuration/components.rb +82 -2
  36. data/lib/datadog/core/configuration/components_state.rb +6 -1
  37. data/lib/datadog/core/configuration/settings.rb +0 -3
  38. data/lib/datadog/core/configuration/supported_configurations.rb +2 -0
  39. data/lib/datadog/core/evp.rb +11 -0
  40. data/lib/datadog/core/remote/client/capabilities.rb +34 -7
  41. data/lib/datadog/core/telemetry/event/app_started.rb +8 -1
  42. data/lib/datadog/di/base.rb +3 -1
  43. data/lib/datadog/di/code_tracker.rb +5 -2
  44. data/lib/datadog/di/component.rb +110 -36
  45. data/lib/datadog/di/error.rb +10 -0
  46. data/lib/datadog/di/fatal_exceptions.rb +26 -0
  47. data/lib/datadog/di/instrumenter.rb +381 -165
  48. data/lib/datadog/di/probe.rb +14 -0
  49. data/lib/datadog/di/probe_file_loader.rb +8 -3
  50. data/lib/datadog/di/probe_manager.rb +67 -9
  51. data/lib/datadog/di/probe_notification_builder.rb +4 -1
  52. data/lib/datadog/di/probe_notifier_worker.rb +52 -32
  53. data/lib/datadog/di/redactor.rb +16 -1
  54. data/lib/datadog/di/remote.rb +175 -14
  55. data/lib/datadog/di/serializer.rb +12 -5
  56. data/lib/datadog/di/transport/input.rb +8 -4
  57. data/lib/datadog/di.rb +63 -0
  58. data/lib/datadog/kit/appsec/events/v2.rb +60 -2
  59. data/lib/datadog/open_feature/component.rb +29 -6
  60. data/lib/datadog/open_feature/configuration.rb +8 -0
  61. data/lib/datadog/open_feature/ext.rb +2 -0
  62. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
  63. data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
  64. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +101 -0
  65. data/lib/datadog/open_feature/hooks/{flag_eval_hook.rb → flag_eval_metrics_hook.rb} +1 -1
  66. data/lib/datadog/open_feature/native_evaluator.rb +22 -0
  67. data/lib/datadog/open_feature/provider.rb +59 -27
  68. data/lib/datadog/open_feature/transport.rb +55 -1
  69. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +5 -12
  70. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +4 -11
  71. data/lib/datadog/opentelemetry/sdk.rb +19 -0
  72. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
  73. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -3
  74. data/lib/datadog/symbol_database/component.rb +164 -47
  75. data/lib/datadog/symbol_database/configuration.rb +12 -4
  76. data/lib/datadog/symbol_database/extensions.rb +19 -0
  77. data/lib/datadog/symbol_database/extractor.rb +124 -87
  78. data/lib/datadog/symbol_database/file_hash.rb +3 -1
  79. data/lib/datadog/symbol_database/remote.rb +8 -3
  80. data/lib/datadog/symbol_database/scope_batcher.rb +7 -3
  81. data/lib/datadog/symbol_database/uploader.rb +9 -2
  82. data/lib/datadog/symbol_database.rb +22 -0
  83. data/lib/datadog/tracing/distributed/baggage.rb +2 -1
  84. data/lib/datadog/tracing/ext.rb +9 -0
  85. data/lib/datadog/tracing/remote.rb +34 -3
  86. data/lib/datadog/version.rb +1 -1
  87. data/lib/datadog.rb +1 -0
  88. metadata +19 -6
@@ -5,9 +5,10 @@ module Datadog
5
5
  module Configuration
6
6
  # Stores the state of component tree when replacing the tree.
7
7
  class ComponentsState
8
- def initialize(telemetry_enabled:, remote_started:)
8
+ def initialize(telemetry_enabled:, remote_started:, di_implicitly_enabled: false)
9
9
  @telemetry_enabled = !!telemetry_enabled
10
10
  @remote_started = !!remote_started
11
+ @di_implicitly_enabled = !!di_implicitly_enabled
11
12
  end
12
13
 
13
14
  def telemetry_enabled?
@@ -17,6 +18,10 @@ module Datadog
17
18
  def remote_started?
18
19
  @remote_started
19
20
  end
21
+
22
+ def di_implicitly_enabled?
23
+ @di_implicitly_enabled
24
+ end
20
25
  end
21
26
  end
22
27
  end
@@ -14,7 +14,6 @@ require_relative '../../profiling/ext'
14
14
 
15
15
  require_relative '../../tracing/configuration/settings'
16
16
  require_relative '../../opentelemetry/configuration/settings'
17
- require_relative '../../symbol_database/configuration'
18
17
 
19
18
  module Datadog
20
19
  module Core
@@ -1130,8 +1129,6 @@ module Datadog
1130
1129
  extend Datadog::Tracing::Configuration::Settings
1131
1130
 
1132
1131
  extend Datadog::OpenTelemetry::Configuration::Settings
1133
-
1134
- extend Datadog::SymbolDatabase::Configuration::Settings
1135
1132
  end
1136
1133
  # standard:enable Metrics/BlockLength
1137
1134
  end
@@ -19,6 +19,7 @@ module Datadog
19
19
  "DD_API_SECURITY_DOWNSTREAM_BODY_ANALYSIS_SAMPLE_RATE",
20
20
  "DD_API_SECURITY_ENABLED",
21
21
  "DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED",
22
+ "DD_API_SECURITY_MAX_DOWNSTREAM_BODY_BYTES",
22
23
  "DD_API_SECURITY_MAX_DOWNSTREAM_REQUEST_BODY_ANALYSIS",
23
24
  "DD_API_SECURITY_REQUEST_SAMPLE_RATE",
24
25
  "DD_API_SECURITY_SAMPLE_DELAY",
@@ -59,6 +60,7 @@ module Datadog
59
60
  "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED",
60
61
  "DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED",
61
62
  "DD_EXTERNAL_ENV",
63
+ "DD_FLAGGING_EVALUATION_COUNTS_ENABLED",
62
64
  "DD_GIT_COMMIT_SHA",
63
65
  "DD_GIT_REPOSITORY_URL",
64
66
  "DD_HEALTH_METRICS_ENABLED",
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module EVP
6
+ SUBDOMAIN_HEADER_NAME = 'X-Datadog-EVP-Subdomain'
7
+ EVENT_PLATFORM_INTAKE_SUBDOMAIN = 'event-platform-intake'
8
+ PAYLOAD_SIZE_LIMIT_BYTES = 5 * 1024 * 1024
9
+ end
10
+ end
11
+ end
@@ -4,6 +4,7 @@ require_relative '../../utils/base64_codec'
4
4
  require_relative '../../../appsec/remote'
5
5
  require_relative '../../../tracing/remote'
6
6
  require_relative '../../../di/remote'
7
+ require_relative '../../../symbol_database'
7
8
  require_relative '../../../symbol_database/remote'
8
9
  require_relative '../../../open_feature/remote'
9
10
 
@@ -35,13 +36,43 @@ module Datadog
35
36
  register_receivers(Datadog::AppSec::Remote.receivers(@telemetry))
36
37
  end
37
38
 
38
- if settings.respond_to?(:dynamic_instrumentation) && settings.dynamic_instrumentation.enabled
39
+ # Tracing must register before DI: on a combined RC dispatch,
40
+ # the APM_TRACING handler must run first to call
41
+ # Datadog::DI::Remote.handle_rc_enablement and start the
42
+ # component before the DI receiver processes LIVE_DEBUGGING
43
+ # changes against `component.started?`. Reversing the order
44
+ # silently drops the probe — the remote client only
45
+ # redispatches on content hash changes.
46
+ register_capabilities(Datadog::Tracing::Remote.capabilities)
47
+ register_products(Datadog::Tracing::Remote.products)
48
+ register_receivers(Datadog::Tracing::Remote.receivers(@telemetry))
49
+
50
+ # Skip DI registration entirely when DI is explicitly disabled
51
+ # (DD_DYNAMIC_INSTRUMENTATION_ENABLED=false) or when the runtime
52
+ # cannot run DI (JRuby, Ruby 2.5): in either case no component will
53
+ # run, so advertising bit 38 or the LIVE_DEBUGGING product would
54
+ # invite probe configs and an enable signal the tracer must refuse.
55
+ # When the env var is unset (default) on a supported runtime, DI is
56
+ # registered so RC can enable it.
57
+ if settings.respond_to?(:dynamic_instrumentation) &&
58
+ !Datadog::DI::Remote.explicitly_disabled?(settings) &&
59
+ Datadog::DI.supported_runtime?
39
60
  register_capabilities(Datadog::DI::Remote.capabilities)
40
61
  register_products(Datadog::DI::Remote.products)
41
62
  register_receivers(Datadog::DI::Remote.receivers(@telemetry))
63
+ end
42
64
 
43
- # Symbol Database
44
- if settings.respond_to?(:symbol_database) && settings.symbol_database.enabled
65
+ # Skip symbol database registration on runtimes that cannot run it
66
+ # (JRuby, Ruby < 2.7): DI supports Ruby 2.6 but Symbol Database does
67
+ # not, so advertising LIVE_DEBUGGING_SYMBOL_DB there would invite
68
+ # symbol-upload configs the component can never serve.
69
+ if settings.respond_to?(:symbol_database) && Datadog::SymbolDatabase.supported_runtime?
70
+ # Symbol database follows DI: when unset it advertises whenever DI
71
+ # advertises (mirror the DI branch above, including the unset/default
72
+ # case that RC may enable). An explicit symbol_database.enabled wins.
73
+ di_enabled = settings.respond_to?(:dynamic_instrumentation) &&
74
+ !Datadog::DI::Remote.explicitly_disabled?(settings)
75
+ if Datadog::SymbolDatabase.resolve_enabled(settings.symbol_database.enabled, di_enabled)
45
76
  register_capabilities(Datadog::SymbolDatabase::Remote.capabilities)
46
77
  register_products(Datadog::SymbolDatabase::Remote.products)
47
78
  register_receivers(Datadog::SymbolDatabase::Remote.receivers(@telemetry))
@@ -53,10 +84,6 @@ module Datadog
53
84
  register_products(Datadog::OpenFeature::Remote.products)
54
85
  register_receivers(Datadog::OpenFeature::Remote.receivers(@telemetry))
55
86
  end
56
-
57
- register_capabilities(Datadog::Tracing::Remote.capabilities)
58
- register_products(Datadog::Tracing::Remote.products)
59
- register_receivers(Datadog::Tracing::Remote.receivers(@telemetry))
60
87
  end
61
88
 
62
89
  def register_capabilities(capabilities)
@@ -57,7 +57,14 @@ module Datadog
57
57
  enabled: !!components.profiler,
58
58
  },
59
59
  dynamic_instrumentation: {
60
- enabled: !!components.dynamic_instrumentation,
60
+ # With always-build, the DI component is constructed by
61
+ # default (when the runtime supports DI, RC is available, and
62
+ # DI is not explicitly disabled) regardless of whether the
63
+ # customer turned DI on. Its presence therefore no longer
64
+ # indicates the enabled state. started? reflects whether DI was
65
+ # actually enabled by env var or by implicit enablement via
66
+ # remote config.
67
+ enabled: components.dynamic_instrumentation&.started? || false,
61
68
  }
62
69
  }
63
70
 
@@ -8,6 +8,7 @@
8
8
  # are loaded, and also none of the rest of datadog library which also
9
9
  # has contrib code in other products.
10
10
 
11
+ require_relative 'fatal_exceptions'
11
12
  require_relative 'code_tracker'
12
13
 
13
14
  # Needed since this file can be loaded without core
@@ -53,7 +54,8 @@ module Datadog
53
54
  # Activate code tracking by default because line trace points will not work
54
55
  # without it.
55
56
  Datadog::DI.activate_tracking!
56
- rescue => exc
57
+ rescue Exception => exc # standard:disable Lint/RescueException
58
+ Datadog::DI.reraise_if_fatal(exc)
57
59
  if defined?(Datadog.logger)
58
60
  Datadog.logger.warn { "di: Failed to activate code tracking for DI: #{exc.class}: #{exc.message}" }
59
61
  else
@@ -3,6 +3,7 @@
3
3
  # rubocop:disable Lint/AssignmentInCondition
4
4
 
5
5
  require_relative 'error'
6
+ require_relative 'fatal_exceptions'
6
7
 
7
8
  module Datadog
8
9
  module DI
@@ -120,7 +121,8 @@ module Datadog
120
121
  end
121
122
  end
122
123
  nil
123
- rescue => exc
124
+ rescue Exception => exc # standard:disable Lint/RescueException
125
+ Datadog::DI.reraise_if_fatal(exc)
124
126
  # Backfill is best-effort — if it fails, line probes on
125
127
  # pre-loaded code won't work but everything else is unaffected.
126
128
  if component = DI.current_component
@@ -191,7 +193,8 @@ module Datadog
191
193
  # Since this method normally is called from customer applications,
192
194
  # rescue any exceptions that might not be handled to not break said
193
195
  # customer applications.
194
- rescue => exc
196
+ rescue Exception => exc # standard:disable Lint/RescueException
197
+ Datadog::DI.reraise_if_fatal(exc)
195
198
  # Code tracker may be loaded without the rest of DI,
196
199
  # in which case DI.component will not yet be defined,
197
200
  # but we will have DI.current_component (set to nil).
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'fatal_exceptions'
4
+
3
5
  module Datadog
4
6
  module DI
5
7
  # Component for dynamic instrumentation.
@@ -15,45 +17,51 @@ module Datadog
15
17
  class Component
16
18
  class << self
17
19
  def build(settings, agent_settings, logger, telemetry: nil)
18
- return unless settings.respond_to?(:dynamic_instrumentation) && settings.dynamic_instrumentation.enabled
20
+ return unless settings.respond_to?(:dynamic_instrumentation)
19
21
 
20
- unless settings.respond_to?(:remote) && settings.remote.enabled
21
- 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
+ # Explicit DD_DYNAMIC_INSTRUMENTATION_ENABLED=false: do not build the
23
+ # component at all. This is customer intent, not a failure log at
24
+ # debug and emit no telemetry error. Capabilities#register skips the
25
+ # DI RC block under the same condition, so no enable signal arrives.
26
+ if Remote.explicitly_disabled?(settings)
27
+ logger.debug("di: dynamic instrumentation is explicitly disabled (DD_DYNAMIC_INSTRUMENTATION_ENABLED=false); not building component")
22
28
  return
23
29
  end
24
30
 
25
- return unless environment_supported?(settings, logger)
31
+ reason = DI.unsupported_reason(settings)
32
+ if reason
33
+ # Log level mirrors customer intent: if the customer explicitly
34
+ # opted in via DD_DYNAMIC_INSTRUMENTATION_ENABLED, warn. Otherwise
35
+ # debug — with always-build, this path runs on every tracer boot
36
+ # for every customer, including those who never wanted DI. Spamming
37
+ # warnings to silent users (especially on JRuby or Ruby 2.5) would
38
+ # be noise. Customers who later trigger implicit enablement via the
39
+ # Datadog UI get a symmetric warn from Remote.handle_rc_enablement
40
+ # when the RC enable signal finds no component to start.
41
+ level = explicitly_enabled?(settings) ? :warn : :debug
42
+ logger.public_send(level, "di: dynamic instrumentation is disabled: #{reason}")
43
+ return
44
+ end
26
45
 
27
46
  new(settings, agent_settings, logger, code_tracker: DI.code_tracker, telemetry: telemetry).tap do |component|
28
47
  DI.add_current_component(component)
29
48
  end
30
49
  end
31
50
 
32
- # Checks whether the runtime environment is supported by
33
- # dynamic instrumentation. Currently we only require that, if Rails
34
- # is used, that Rails environment is not development because
35
- # DI does not currently support code unloading and reloading.
36
- def environment_supported?(settings, logger)
37
- # TODO add tests?
38
- unless settings.dynamic_instrumentation.internal.development
39
- if Datadog::Core::Environment::Execution.development?
40
- logger.warn("di: development environment detected; not enabling dynamic instrumentation")
41
- return false
42
- end
43
- end
44
- if RUBY_ENGINE != 'ruby'
45
- logger.warn("di: cannot enable dynamic instrumentation: MRI is required, but running on #{RUBY_ENGINE}")
46
- return false
47
- end
48
- if RubyVersion.is?('< 2.6')
49
- logger.warn("di: cannot enable dynamic instrumentation: Ruby 2.6+ is required, but running on #{RUBY_VERSION}")
50
- return false
51
- end
52
- unless DI.respond_to?(:exception_message)
53
- logger.warn("di: cannot enable dynamic instrumentation: C extension is not available")
54
- return false
55
- end
56
- true
51
+ # True when the customer explicitly set
52
+ # DD_DYNAMIC_INSTRUMENTATION_ENABLED=true (or its equivalent in code).
53
+ # Symmetric to {Remote.explicitly_disabled?}.
54
+ #
55
+ # Uses {Datadog::Core::Configuration::Options::InstanceMethods#using_default?}
56
+ # rather than `options[:enabled].default_precedence?` because the option
57
+ # hash is populated lazily on first access; reading the underlying option
58
+ # before {Component.build} touches the value would NoMethodError on nil.
59
+ #
60
+ # @param settings [Datadog::Core::Configuration::Settings]
61
+ # @return [Boolean]
62
+ def explicitly_enabled?(settings)
63
+ !settings.dynamic_instrumentation.using_default?(:enabled) &&
64
+ settings.dynamic_instrumentation.enabled
57
65
  end
58
66
  end
59
67
 
@@ -80,7 +88,11 @@ module Datadog
80
88
  settings, instrumenter, probe_notification_builder, probe_notifier_worker, logger, probe_repository,
81
89
  telemetry: telemetry,
82
90
  )
83
- probe_notifier_worker.start
91
+ # @started transitions are serialized by @lifecycle_mutex so that
92
+ # concurrent RC callbacks (which run on the remote-config thread)
93
+ # cannot race a foreground start! with a background stop!.
94
+ @lifecycle_mutex = Mutex.new
95
+ @started = false
84
96
  end
85
97
 
86
98
  attr_reader :settings
@@ -96,9 +108,61 @@ module Datadog
96
108
  attr_reader :redactor
97
109
  attr_reader :serializer
98
110
 
99
- # Shuts down dynamic instrumentation.
111
+ # Starts the DI component: begins accepting probes and
112
+ # processing snapshots.
113
+ #
114
+ # Starts the probe notifier worker thread before enabling the
115
+ # definition trace point, so any future status emission from a
116
+ # trace-point-driven installation has a worker to drain it.
117
+ # Today {ProbeManager#reopen} re-hooks without emitting statuses, so
118
+ # the order is defensive rather than load-bearing. No-op if already
119
+ # started. Serialized by @lifecycle_mutex.
120
+ #
121
+ # @return [void]
122
+ def start!
123
+ @lifecycle_mutex.synchronize do
124
+ return if @started
125
+
126
+ probe_notifier_worker.start
127
+ probe_manager.reopen
128
+ @started = true
129
+ end
130
+ end
131
+
132
+ # Stops the DI component: removes all probes and stops
133
+ # background threads.
134
+ #
135
+ # The component remains alive and can be restarted with {#start!}.
136
+ # Does not clear out the code tracker.
137
+ # No-op if already stopped. Serialized by @lifecycle_mutex.
138
+ #
139
+ # @return [void]
140
+ def stop!
141
+ @lifecycle_mutex.synchronize do
142
+ return unless @started
143
+
144
+ probe_manager.stop
145
+ probe_notifier_worker.stop
146
+ @started = false
147
+ end
148
+ end
149
+
150
+ # Whether the component is currently started.
151
+ #
152
+ # Read by remote config dispatch to decide whether to apply probe
153
+ # changes (changes received while stopped are dropped, since the
154
+ # next start! will reconcile from the latest RC state).
155
+ #
156
+ # @return [Boolean] true if start! has been called and stop! has not
157
+ def started?
158
+ @started
159
+ end
160
+
161
+ # Shuts down dynamic instrumentation permanently.
100
162
  #
101
163
  # Removes all code hooks and stops background threads.
164
+ # Called by Components#shutdown! during component destruction.
165
+ # Unlike {#stop!}, this is not reversible.
102
166
  #
103
167
  # Does not clear out the code tracker, because it's only populated
104
168
  # by code when code is compiled and therefore, if the code tracker
@@ -107,21 +171,31 @@ module Datadog
107
171
  def shutdown!(replacement = nil)
108
172
  DI.remove_current_component(self)
109
173
 
110
- probe_manager.clear_hooks
111
- probe_manager.close
112
- probe_notifier_worker.stop
174
+ # Hold the lifecycle mutex so all transitions of @started are
175
+ # serialized — start! / stop! / shutdown! cannot interleave with
176
+ # one another. Without the mutex an in-flight stop! from an RC
177
+ # callback could complete after shutdown!'s probe_manager.close,
178
+ # producing an inconsistent state.
179
+ @lifecycle_mutex.synchronize do
180
+ @started = false
181
+ probe_manager.clear_hooks
182
+ probe_manager.close
183
+ probe_notifier_worker.stop
184
+ end
113
185
  end
114
186
 
115
187
  def parse_probe_spec_and_notify(probe_spec)
116
188
  probe = ProbeBuilder.build_from_remote_config(probe_spec)
117
- rescue => exc
189
+ rescue Exception => exc # standard:disable Lint/RescueException
190
+ Datadog::DI.reraise_if_fatal(exc)
118
191
  begin
119
192
  probe = Struct.new(:id).new(
120
193
  probe_spec['id'],
121
194
  )
122
195
  payload = probe_notification_builder.build_errored(probe, exc)
123
196
  probe_notifier_worker.add_status(payload)
124
- rescue => nested_exc
197
+ rescue Exception => nested_exc # standard:disable Lint/RescueException
198
+ Datadog::DI.reraise_if_fatal(nested_exc)
125
199
  logger.debug { "di: failed to build error notification: #{nested_exc.class}: #{nested_exc.message}" }
126
200
  telemetry&.report(nested_exc, description: 'Error building probe error notification')
127
201
  raise
@@ -18,6 +18,10 @@ module Datadog
18
18
  class MissingLineNumber < Error
19
19
  end
20
20
 
21
+ # Probe does not contain a method name (i.e., is not a method probe).
22
+ class MissingMethodName < Error
23
+ end
24
+
21
25
  # Failed to communicate to the local Datadog agent (e.g. to send
22
26
  # probe status or a snapshot).
23
27
  class AgentCommunicationError < Error
@@ -47,6 +51,12 @@ module Datadog
47
51
  class AlreadyInstrumented < Error
48
52
  end
49
53
 
54
+ # Raised when a probe targets a class or module that the tracer
55
+ # refuses to instrument (for example, anything in the Datadog
56
+ # namespace, which the tracer itself uses internally).
57
+ class ProbeTargetForbidden < Error
58
+ end
59
+
50
60
  # Raised when installing a line probe and multiple files match the
51
61
  # specified path suffix.
52
62
  # A probe must be installed into one file only, since UI only
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Defined under DI rather than Core because the DI preload path
4
+ # (datadog/di/preload -> base) activates code tracking before the rest of the
5
+ # library, including Datadog::Core, is loaded. Catch-all rescues on that path
6
+ # must be able to re-raise fatal exceptions without referencing Core.
7
+
8
+ module Datadog
9
+ module DI
10
+ # Exception classes that catch-all rescues must never swallow: they signal
11
+ # that the process is being torn down or has run out of memory and have to
12
+ # propagate. SignalException covers Interrupt (Ctrl+C) and every other
13
+ # signal delivered as an exception.
14
+ FATAL_EXCEPTION_CLASSES = [SystemExit, SignalException, NoMemoryError].freeze
15
+
16
+ # Re-raise +exc+ when it is fatal (see FATAL_EXCEPTION_CLASSES). Call this as
17
+ # the first statement of a `rescue Exception` handler so that fatal
18
+ # conditions are not accidentally swallowed by a broad rescue.
19
+ #
20
+ # @param exc [Exception] the currently-handled exception
21
+ # @return [nil] when +exc+ is not fatal; otherwise re-raises +exc+
22
+ def self.reraise_if_fatal(exc)
23
+ raise exc if FATAL_EXCEPTION_CLASSES.any? { |klass| exc.is_a?(klass) }
24
+ end
25
+ end
26
+ end