Class: Datadog::DI::Component Private

Inherits:
Object
  • Object
show all
Defined in:
lib/datadog/di/component.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Component for dynamic instrumentation.

Only one instance of the Component should ever be active; if configuration is changed, the old distance should be shut down prior to the new instance being created.

The Component instance stores all state related to DI, for example which probes have been retrieved via remote config, intalled tracepoints and so on. Component will clean up all resources and installed tracepoints upon shutdown.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(settings, agent_settings, logger, code_tracker: nil, telemetry: nil) ⇒ Component

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Component.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/datadog/di/component.rb', line 68

def initialize(settings, agent_settings, logger, code_tracker: nil, telemetry: nil)
  @settings = settings
  @agent_settings = agent_settings
  logger = DI::Logger.new(settings, logger)
  @logger = logger
  @telemetry = telemetry
  @code_tracker = code_tracker
  @redactor = Redactor.new(settings)
  @serializer = Serializer.new(settings, redactor, telemetry: telemetry)
  @instrumenter = Instrumenter.new(settings, serializer, logger, code_tracker: code_tracker, telemetry: telemetry)
  @probe_repository = ProbeRepository.new
  @probe_notification_builder = ProbeNotificationBuilder.new(settings, serializer, logger, telemetry: telemetry)
  @probe_notifier_worker = ProbeNotifierWorker.new(
    settings, logger,
    agent_settings: agent_settings,
    probe_repository: probe_repository,
    probe_notification_builder: probe_notification_builder,
    telemetry: telemetry,
  )
  @probe_manager = ProbeManager.new(
    settings, instrumenter, probe_notification_builder, probe_notifier_worker, logger, probe_repository,
    telemetry: telemetry,
  )
  # @started transitions are serialized by @lifecycle_mutex so that
  # concurrent RC callbacks (which run on the remote-config thread)
  # cannot race a foreground start! with a background stop!.
  @lifecycle_mutex = Mutex.new
  @started = false
end

Instance Attribute Details

#agent_settingsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



99
100
101
# File 'lib/datadog/di/component.rb', line 99

def agent_settings
  @agent_settings
end

#code_trackerObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



102
103
104
# File 'lib/datadog/di/component.rb', line 102

def code_tracker
  @code_tracker
end

#instrumenterObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



103
104
105
# File 'lib/datadog/di/component.rb', line 103

def instrumenter
  @instrumenter
end

#loggerObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



100
101
102
# File 'lib/datadog/di/component.rb', line 100

def logger
  @logger
end

#probe_managerObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



107
108
109
# File 'lib/datadog/di/component.rb', line 107

def probe_manager
  @probe_manager
end

#probe_notification_builderObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



106
107
108
# File 'lib/datadog/di/component.rb', line 106

def probe_notification_builder
  @probe_notification_builder
end

#probe_notifier_workerObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



105
106
107
# File 'lib/datadog/di/component.rb', line 105

def probe_notifier_worker
  @probe_notifier_worker
end

#probe_repositoryObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



104
105
106
# File 'lib/datadog/di/component.rb', line 104

def probe_repository
  @probe_repository
end

#redactorObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



108
109
110
# File 'lib/datadog/di/component.rb', line 108

def redactor
  @redactor
end

#serializerObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



109
110
111
# File 'lib/datadog/di/component.rb', line 109

def serializer
  @serializer
end

#settingsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



98
99
100
# File 'lib/datadog/di/component.rb', line 98

def settings
  @settings
end

#telemetryObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



101
102
103
# File 'lib/datadog/di/component.rb', line 101

def telemetry
  @telemetry
end

Class Method Details

.build(settings, agent_settings, logger, telemetry: nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/datadog/di/component.rb', line 19

def build(settings, agent_settings, logger, telemetry: nil)
  return unless settings.respond_to?(:dynamic_instrumentation)

  # Explicit DD_DYNAMIC_INSTRUMENTATION_ENABLED=false: do not build the
  # component at all. This is customer intent, not a failure — log at
  # debug and emit no telemetry error. Capabilities#register skips the
  # DI RC block under the same condition, so no enable signal arrives.
  if Remote.explicitly_disabled?(settings)
    logger.debug("di: dynamic instrumentation is explicitly disabled (DD_DYNAMIC_INSTRUMENTATION_ENABLED=false); not building component")
    return
  end

  reason = DI.unsupported_reason(settings)
  if reason
    # Log level mirrors customer intent: if the customer explicitly
    # opted in via DD_DYNAMIC_INSTRUMENTATION_ENABLED, warn. Otherwise
    # debug — with always-build, this path runs on every tracer boot
    # for every customer, including those who never wanted DI. Spamming
    # warnings to silent users (especially on JRuby or Ruby 2.5) would
    # be noise. Customers who later trigger implicit enablement via the
    # Datadog UI get a symmetric warn from Remote.handle_rc_enablement
    # when the RC enable signal finds no component to start.
    level = explicitly_enabled?(settings) ? :warn : :debug
    logger.public_send(level, "di: dynamic instrumentation is disabled: #{reason}")
    return
  end

  new(settings, agent_settings, logger, code_tracker: DI.code_tracker, telemetry: telemetry).tap do |component|
    DI.add_current_component(component)
  end
end

.explicitly_enabled?(settings) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

True when the customer explicitly set DD_DYNAMIC_INSTRUMENTATION_ENABLED=true (or its equivalent in code). Symmetric to Remote.explicitly_disabled?.

Uses Core::Configuration::Options::InstanceMethods#using_default? rather than options[:enabled].default_precedence? because the option hash is populated lazily on first access; reading the underlying option before build touches the value would NoMethodError on nil.

Parameters:

Returns:

  • (Boolean)


62
63
64
65
# File 'lib/datadog/di/component.rb', line 62

def explicitly_enabled?(settings)
  !settings.dynamic_instrumentation.using_default?(:enabled) &&
    settings.dynamic_instrumentation.enabled
end

Instance Method Details

#parse_probe_spec_and_notify(probe_spec) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/datadog/di/component.rb', line 195

def parse_probe_spec_and_notify(probe_spec)
  probe = ProbeBuilder.build_from_remote_config(probe_spec, logger: logger)
rescue Exception => exc # standard:disable Lint/RescueException
  Datadog::DI.reraise_if_fatal(exc)
  begin
    probe = Struct.new(:id).new(
      probe_spec["id"],
    )
    payload = probe_notification_builder.build_errored(probe, exc)
    probe_notifier_worker.add_status(payload)
  rescue Exception => nested_exc # standard:disable Lint/RescueException
    Datadog::DI.reraise_if_fatal(nested_exc)
    logger.debug { "di: failed to build error notification: #{nested_exc.class}: #{nested_exc.message}" }
    telemetry&.report(nested_exc, description: "Error building probe error notification")
    raise
  end

  raise
else
  payload = probe_notification_builder.build_received(probe)
  probe_notifier_worker.add_status(payload, probe: probe)
  probe
end

#shutdown!(replacement = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Shuts down dynamic instrumentation permanently.

Removes all code hooks and stops background threads. Called by Components#shutdown! during component destruction. Unlike #stop!, this is not reversible.

Does not clear out the code tracker, because it's only populated by code when code is compiled and therefore, if the code tracker was replaced by a new instance, the new instance of it wouldn't have any of the already loaded code tracked.



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/datadog/di/component.rb', line 179

def shutdown!(replacement = nil)
  DI.remove_current_component(self)

  # Hold the lifecycle mutex so all transitions of @started are
  # serialized — start! / stop! / shutdown! cannot interleave with
  # one another. Without the mutex an in-flight stop! from an RC
  # callback could complete after shutdown!'s probe_manager.close,
  # producing an inconsistent state.
  @lifecycle_mutex.synchronize do
    @started = false
    probe_manager.clear_hooks
    probe_manager.close
    probe_notifier_worker.stop
  end
end

#start!void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Starts the DI component: begins accepting probes and processing snapshots.

Starts the probe notifier worker thread before enabling the definition trace point, so any future status emission from a trace-point-driven installation has a worker to drain it. Today ProbeManager#reopen re-hooks without emitting statuses, so the order is defensive rather than load-bearing. No-op if already started. Serialized by @lifecycle_mutex.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/datadog/di/component.rb', line 122

def start!
  @lifecycle_mutex.synchronize do
    return if @started

    # DI.activate_tracking creates the global code tracker lazily. When DI
    # is enabled after boot via remote configuration (in-product
    # enablement), this component and its instrumenter were built with a
    # nil tracker; adopt the now-current global tracker so line probes
    # can be targeted.
    @code_tracker = DI.code_tracker
    instrumenter.code_tracker = @code_tracker

    probe_notifier_worker.start
    probe_manager.reopen
    @started = true
  end
end

#started?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Whether the component is currently started.

Read by remote config dispatch to decide whether to apply probe changes (changes received while stopped are dropped, since the next start! will reconcile from the latest RC state).

Returns:

  • (Boolean)

    true if start! has been called and stop! has not



165
166
167
# File 'lib/datadog/di/component.rb', line 165

def started?
  @started
end

#stop!void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Stops the DI component: removes all probes and stops background threads.

The component remains alive and can be restarted with #start!. Does not clear out the code tracker. No-op if already stopped. Serialized by @lifecycle_mutex.



148
149
150
151
152
153
154
155
156
# File 'lib/datadog/di/component.rb', line 148

def stop!
  @lifecycle_mutex.synchronize do
    return unless @started

    probe_manager.stop
    probe_notifier_worker.stop
    @started = false
  end
end