Class: Datadog::Configuration::Components

Inherits:
Object
  • Object
show all
Defined in:
lib/ddtrace/configuration/components.rb

Overview

Global components for the trace library. rubocop:disable Metrics/ClassLength rubocop:disable Layout/LineLength

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(settings) ⇒ Components

Returns a new instance of Components.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/ddtrace/configuration/components.rb', line 216

def initialize(settings)
  # Logger
  @logger = self.class.build_logger(settings)

  agent_settings = AgentSettingsResolver.call(settings, logger: @logger)

  # Tracer
  @tracer = self.class.build_tracer(settings, agent_settings)

  # Profiler
  @profiler = self.class.build_profiler(settings, agent_settings, @tracer)

  # Runtime metrics
  @runtime_metrics = self.class.build_runtime_metrics_worker(settings)

  # Health metrics
  @health_metrics = self.class.build_health_metrics(settings)
end

Instance Attribute Details

#health_metricsObject (readonly)

Returns the value of attribute health_metrics.



209
210
211
# File 'lib/ddtrace/configuration/components.rb', line 209

def health_metrics
  @health_metrics
end

#loggerObject (readonly)

Returns the value of attribute logger.



209
210
211
# File 'lib/ddtrace/configuration/components.rb', line 209

def logger
  @logger
end

#profilerObject (readonly)

Returns the value of attribute profiler.



209
210
211
# File 'lib/ddtrace/configuration/components.rb', line 209

def profiler
  @profiler
end

#runtime_metricsObject (readonly)

Returns the value of attribute runtime_metrics.



209
210
211
# File 'lib/ddtrace/configuration/components.rb', line 209

def runtime_metrics
  @runtime_metrics
end

#tracerObject (readonly)

Returns the value of attribute tracer.



209
210
211
# File 'lib/ddtrace/configuration/components.rb', line 209

def tracer
  @tracer
end

Class Method Details

.build_health_metrics(settings) ⇒ Object



18
19
20
21
22
23
24
# File 'lib/ddtrace/configuration/components.rb', line 18

def build_health_metrics(settings)
  settings = settings.diagnostics.health_metrics
  options = { enabled: settings.enabled }
  options[:statsd] = settings.statsd unless settings.statsd.nil?

  Datadog::Diagnostics::Health::Metrics.new(**options)
end

.build_logger(settings) ⇒ Object



26
27
28
29
30
31
# File 'lib/ddtrace/configuration/components.rb', line 26

def build_logger(settings)
  logger = settings.logger.instance || Datadog::Logger.new($stdout)
  logger.level = settings.diagnostics.debug ? ::Logger::DEBUG : settings.logger.level

  logger
end

.build_profiler(settings, agent_settings, tracer) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/ddtrace/configuration/components.rb', line 81

def build_profiler(settings, agent_settings, tracer)
  return unless Datadog::Profiling.supported? && settings.profiling.enabled

  unless defined?(Datadog::Profiling::Tasks::Setup)
    # In #1545 a user reported a NameError due to this constant being uninitialized
    # I've documented my suspicion on why that happened in
    # https://github.com/DataDog/dd-trace-rb/issues/1545#issuecomment-856049025
    #
    # > Thanks for the info! It seems to feed into my theory: there's two moments in the code where we check if
    # > profiler is "supported": 1) when loading ddtrace (inside preload) and 2) when starting the profile
    # > after Datadog.configure gets run.
    # > The problem is that the code assumes that both checks 1) and 2) will always reach the same conclusion:
    # > either profiler is supported, or profiler is not supported.
    # > In the problematic case, it looks like in your case check 1 decides that profiler is not
    # > supported => doesn't load it, and then check 2 decides that it is => assumes it is loaded and tries to
    # > start it.
    #
    # I was never able to validate if this was the issue or why exactly .supported? would change its mind BUT
    # just in case it happens again, I've left this check which avoids breaking the user's application AND
    # would instead direct them to report it to us instead, so that we can investigate what's wrong.
    #
    # TODO: As of June 2021, most checks in .supported? are related to the google-protobuf gem; so it's
    # very likely that it was the origin of the issue we saw. Thus, if, as planned we end up moving away from
    # protobuf OR enough time has passed and no users saw the issue again, we can remove this check altogether.
    Datadog.logger.error(
      'Profiling was marked as supported and enabled, but setup task was not loaded properly. ' \
      'Please report this at https://github.com/DataDog/dd-trace-rb/blob/master/CONTRIBUTING.md#found-a-bug'
    )

    return
  end

  # Load extensions needed to support some of the Profiling features
  Datadog::Profiling::Tasks::Setup.new.run

  # NOTE: Please update the Initialization section of ProfilingDevelopment.md with any changes to this method

  trace_identifiers_helper = Datadog::Profiling::TraceIdentifiers::Helper.new(
    tracer: tracer,
    endpoint_collection_enabled: settings.profiling.advanced.endpoint.collection.enabled
  )

  recorder = build_profiler_recorder(settings)
  collectors = build_profiler_collectors(settings, recorder, trace_identifiers_helper)
  exporters = build_profiler_exporters(settings, agent_settings)
  scheduler = build_profiler_scheduler(settings, recorder, exporters)

  Datadog::Profiler.new(collectors, scheduler)
end

.build_runtime_metrics(settings) ⇒ Object



33
34
35
36
37
38
39
# File 'lib/ddtrace/configuration/components.rb', line 33

def build_runtime_metrics(settings)
  options = { enabled: settings.runtime_metrics.enabled }
  options[:statsd] = settings.runtime_metrics.statsd unless settings.runtime_metrics.statsd.nil?
  options[:services] = [settings.service] unless settings.service.nil?

  Datadog::Runtime::Metrics.new(**options)
end

.build_runtime_metrics_worker(settings) ⇒ Object



41
42
43
44
45
46
47
48
49
# File 'lib/ddtrace/configuration/components.rb', line 41

def build_runtime_metrics_worker(settings)
  # NOTE: Should we just ignore building the worker if its not enabled?
  options = settings.runtime_metrics.opts.merge(
    enabled: settings.runtime_metrics.enabled,
    metrics: build_runtime_metrics(settings)
  )

  Datadog::Workers::RuntimeMetrics.new(options)
end

.build_tracer(settings, agent_settings) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/ddtrace/configuration/components.rb', line 51

def build_tracer(settings, agent_settings)
  # If a custom tracer has been provided, use it instead.
  # Ignore all other options (they should already be configured.)
  tracer = settings.tracer.instance
  return tracer unless tracer.nil?

  tracer = Tracer.new(
    default_service: settings.service,
    enabled: settings.tracer.enabled,
    partial_flush: settings.tracer.partial_flush.enabled,
    tags: build_tracer_tags(settings),
    sampler: PrioritySampler.new(
      base_sampler: AllSampler.new,
      post_sampler: Sampling::RuleSampler.new(
        rate_limit: settings.sampling.rate_limit,
        default_sample_rate: settings.sampling.default_rate
      )
    )
  )

  # TODO: We reconfigure the tracer here because it has way too many
  #       options it allows to mutate, and it's overwhelming to rewrite
  #       tracer initialization for now. Just reconfigure using the
  #       existing mutable #configure function. Remove when these components
  #       are extracted.
  tracer.configure(agent_settings: agent_settings, **build_tracer_options(settings, agent_settings))

  tracer
end

Instance Method Details

#shutdown!(replacement = nil) ⇒ Object

Shuts down all the components in use. If it has another instance to compare to, it will compare and avoid tearing down parts still in use.



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/ddtrace/configuration/components.rb', line 254

def shutdown!(replacement = nil)
  # Shutdown the old tracer, unless it's still being used.
  # (e.g. a custom tracer instance passed in.)
  tracer.shutdown! unless replacement && tracer == replacement.tracer

  # Shutdown old profiler
  profiler.shutdown! unless profiler.nil?

  # Shutdown workers
  runtime_metrics.stop(true, close_metrics: false)

  # Shutdown the old metrics, unless they are still being used.
  # (e.g. custom Statsd instances.)
  #
  # TODO: This violates the encapsulation created by Runtime::Metrics and
  # Health::Metrics, by directly manipulating `statsd` and changing
  # it's lifecycle management.
  # If we need to directly have ownership of `statsd` lifecycle, we should
  # have direct ownership of it.
  old_statsd = [
    runtime_metrics.metrics.statsd,
    health_metrics.statsd
  ].compact.uniq

  new_statsd =  if replacement
                  [
                    replacement.runtime_metrics.metrics.statsd,
                    replacement.health_metrics.statsd
                  ].compact.uniq
                else
                  []
                end

  unused_statsd = (old_statsd - (old_statsd & new_statsd))
  unused_statsd.each(&:close)
end

#startup!(settings) ⇒ Object

Starts up components



236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/ddtrace/configuration/components.rb', line 236

def startup!(settings)
  if settings.profiling.enabled
    if profiler
      @logger.debug('Profiling started')
      profiler.start
    else
      # Display a warning for users who expected profiling to be enabled
      unsupported_reason = Datadog::Profiling.unsupported_reason
      logger.warn("Profiling was requested but is not supported, profiling disabled: #{unsupported_reason}")
    end
  else
    @logger.debug('Profiling is disabled')
  end
end