Class: Datadog::Tracer

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

Overview

A \Tracer keeps track of the time spent by an application processing a single operation. For example, a trace can be used to track the entire time spent processing a complicated web request. Even though the request may require multiple resources and machines to handle the request, all of these function calls and sub-requests would be encapsulated within a single trace. rubocop:disable Metrics/ClassLength

Defined Under Namespace

Classes: TraceCompleted

Constant Summary collapse

SERVICES_DEPRECATION_WARN_ONLY_ONCE =
Datadog::Utils::OnlyOnce.new
SET_SERVICE_INFO_DEPRECATION_WARN_ONLY_ONCE =
Datadog::Utils::OnlyOnce.new
ALLOWED_SPAN_OPTIONS =
[:service, :resource, :span_type].freeze
DEFAULT_ON_ERROR =
proc { |span, error| span.set_error(error) unless span.nil? }

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Tracer

Initialize a new \Tracer used to create, sample and submit spans that measure the time of sections of code. Available +options+ are:

  • +enabled+: set if the tracer submits or not spans to the local agent. It's enabled by default.


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/ddtrace/tracer.rb', line 76

def initialize(options = {})
  # Configurable options
  @context_flush = if options[:context_flush]
                     options[:context_flush]
                   elsif options[:partial_flush]
                     Datadog::ContextFlush::Partial.new(options)
                   else
                     Datadog::ContextFlush::Finished.new
                   end

  @default_service = options[:default_service]
  @enabled = options.fetch(:enabled, true)
  @provider = options[:context_provider] || Datadog::DefaultContextProvider.new
  @sampler = options.fetch(:sampler, Datadog::AllSampler.new)
  @tags = options.fetch(:tags, {})
  @writer = options.fetch(:writer) { Datadog::Writer.new }

  # Instance variables
  @mutex = Mutex.new

  # Enable priority sampling by default
  activate_priority_sampling!(@sampler)
end

Instance Attribute Details

#context_flushObject (readonly)

Returns the value of attribute context_flush.



28
29
30
# File 'lib/ddtrace/tracer.rb', line 28

def context_flush
  @context_flush
end

#default_serviceObject

A default value for service. One should really override this one for non-root spans which have a parent. However, root spans without a service would be invalid and rejected.



151
152
153
# File 'lib/ddtrace/tracer.rb', line 151

def default_service
  @default_service ||= Datadog::Ext::Environment::FALLBACK_SERVICE_NAME
end

#enabledObject

Returns the value of attribute enabled.



29
30
31
# File 'lib/ddtrace/tracer.rb', line 29

def enabled
  @enabled
end

#providerObject (readonly)

Returns the value of attribute provider.



28
29
30
# File 'lib/ddtrace/tracer.rb', line 28

def provider
  @provider
end

#samplerObject (readonly)

Returns the value of attribute sampler.



28
29
30
# File 'lib/ddtrace/tracer.rb', line 28

def sampler
  @sampler
end

#tagsObject (readonly)

Returns the value of attribute tags.



28
29
30
# File 'lib/ddtrace/tracer.rb', line 28

def tags
  @tags
end

#writerObject

Returns the value of attribute writer.



29
30
31
# File 'lib/ddtrace/tracer.rb', line 29

def writer
  @writer
end

Instance Method Details

#active_correlation(key = nil) ⇒ Object

Return a CorrelationIdentifier for active span



353
354
355
# File 'lib/ddtrace/tracer.rb', line 353

def active_correlation(key = nil)
  Datadog::Correlation.identifier_from_context(call_context(key))
end

#active_root_span(key = nil) ⇒ Object

Return the current active root span or +nil+.



348
349
350
# File 'lib/ddtrace/tracer.rb', line 348

def active_root_span(key = nil)
  call_context(key).current_root_span
end

#active_span(key = nil) ⇒ Object

Return the current active span or +nil+.



343
344
345
# File 'lib/ddtrace/tracer.rb', line 343

def active_span(key = nil)
  call_context(key).current_span
end

#call_context(key = nil) ⇒ Object

Return the current active \Context for this traced execution. This method is automatically called when calling Tracer.trace or Tracer.start_span, but it can be used in the application code during manual instrumentation.

This method makes use of a \ContextProvider that is automatically set during the tracer initialization, or while using a library instrumentation.



67
68
69
# File 'lib/ddtrace/tracer.rb', line 67

def call_context(key = nil)
  @provider.context(key)
end

#configure(options = {}) ⇒ Object

Updates the current \Tracer instance, so that the tracer can be configured after the initialization. Available +options+ are:

  • +enabled+: set if the tracer submits or not spans to the trace agent
  • +hostname+: change the location of the trace agent
  • +port+: change the port of the trace agent
  • +partial_flush+: enable partial trace flushing

For instance, if the trace agent runs in a different location, just:

tracer.configure(hostname: 'agent.service.consul', port: '8777')



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/ddtrace/tracer.rb', line 112

def configure(options = {})
  enabled = options.fetch(:enabled, nil)

  # Those are rare "power-user" options.
  sampler = options.fetch(:sampler, nil)

  @enabled = enabled unless enabled.nil?
  @sampler = sampler unless sampler.nil?

  configure_writer(options)

  if options.key?(:context_flush) || options.key?(:partial_flush)
    @context_flush = if options[:context_flush]
                       options[:context_flush]
                     elsif options[:partial_flush]
                       Datadog::ContextFlush::Partial.new(options)
                     else
                       Datadog::ContextFlush::Finished.new
                     end
  end
end

#record(context) ⇒ Object

Record the given +context+. For compatibility with previous versions, +context+ can also be a span. It is similar to the +child_of+ argument, method will figure out what to do, submitting a +span+ for recording is like trying to record its +context+.



323
324
325
326
327
328
# File 'lib/ddtrace/tracer.rb', line 323

def record(context)
  context = context.context if context.is_a?(Datadog::Span)
  return if context.nil?

  record_context(context)
end

#servicesObject



35
36
37
38
39
40
41
# File 'lib/ddtrace/tracer.rb', line 35

def services
  SERVICES_DEPRECATION_WARN_ONLY_ONCE.run do
    Datadog.logger.warn('services: Usage of Tracer.services has been deprecated')
  end

  {}
end

#set_service_info(service, app, app_type) ⇒ Object

Set the information about the given service. A valid example is:

tracer.set_service_info('web-application', 'rails', 'web')

set_service_info is deprecated, no service information needs to be tracked



139
140
141
142
143
144
145
146
# File 'lib/ddtrace/tracer.rb', line 139

def set_service_info(service, app, app_type)
  SET_SERVICE_INFO_DEPRECATION_WARN_ONLY_ONCE.run do
    Datadog.logger.warn(%(
      set_service_info: Usage of set_service_info has been deprecated,
      service information no longer needs to be reported to the trace agent.
    ))
  end
end

#set_tags(tags) ⇒ Object

Set the given key / value tag pair at the tracer level. These tags will be appended to each span created by the tracer. Keys and values must be strings. A valid example is:

tracer.set_tags('env' => 'prod', 'component' => 'core')



160
161
162
163
# File 'lib/ddtrace/tracer.rb', line 160

def set_tags(tags)
  string_tags = tags.collect { |k, v| [k.to_s, v] }.to_h
  @tags = @tags.merge(string_tags)
end

#shutdown!Object

Shorthand that calls the shutdown! method of a registered worker. It's useful to ensure that the Trace Buffer is properly flushed before shutting down the application.

For instance:

tracer.trace('operation_name', service='rake_tasks') do |span| span.set_tag('task.name', 'script') end

tracer.shutdown!



55
56
57
58
59
# File 'lib/ddtrace/tracer.rb', line 55

def shutdown!
  return unless @enabled

  @writer.stop unless @writer.nil?
end

#start_span(name, options = {}) ⇒ Object

Return a span that will trace an operation called \name. This method allows parenting passing \child_of as an option. If it's missing, the newly created span is a root span. Available options are:

  • +service+: the service name for this span
  • +resource+: the resource this span refers, or \name if it's missing
  • +span_type+: the type of the span (such as \http, \db and so on)
  • +child_of+: a \Span or a \Context instance representing the parent for this span.
  • +start_time+: when the span actually starts (defaults to \now)
  • +tags+: extra tags which should be added to the span.


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/ddtrace/tracer.rb', line 187

def start_span(name, options = {})
  start_time = options[:start_time]
  tags = options.fetch(:tags, {})

  span_options = options.select do |k, _v|
    # Filter options, we want no side effects with unexpected args.
    ALLOWED_SPAN_OPTIONS.include?(k)
  end

  ctx, parent = guess_context_and_parent(options[:child_of])
  span_options[:context] = ctx unless ctx.nil?

  span = Span.new(self, name, span_options)
  if parent.nil?
    # root span
    @sampler.sample!(span)
    span.set_tag(Datadog::Ext::Runtime::TAG_PID, Process.pid)
    span.set_tag(Datadog::Ext::Runtime::TAG_ID, Datadog::Core::Environment::Identity.id)

    if ctx && ctx.trace_id
      span.trace_id = ctx.trace_id
      span.parent_id = ctx.span_id unless ctx.span_id.nil?
    end
  else
    # child span
    span.parent = parent # sets service, trace_id, parent_id, sampled
  end

  span.set_tags(@tags) unless @tags.empty?
  span.set_tags(tags) unless tags.empty?
  span.start(start_time)

  # this could at some point be optional (start_active_span vs start_manual_span)
  ctx.add_span(span) unless ctx.nil?

  span
end

#trace(name, options = {}) ⇒ Object

Return a +span+ that will trace an operation called +name+. You could trace your code using a do-block like:

tracer.trace('web.request') do |span| span.service = 'my-web-site' span.resource = '/' span.set_tag('http.method', request.request_method) do_something() end

The tracer.trace() method can also be used without a block in this way:

span = tracer.trace('web.request', service: 'my-web-site') do_something() span.finish()

Remember that in this case, calling span.finish() is mandatory.

When a Trace is started, trace() will store the created span; subsequent spans will become it's children and will inherit some properties:

parent = tracer.trace('parent') # has no parent span child = tracer.trace('child') # is a child of 'parent' child.finish() parent.finish() parent2 = tracer.trace('parent2') # has no parent span parent2.finish()

Available options are:

  • +service+: the service name for this span
  • +resource+: the resource this span refers, or \name if it's missing
  • +span_type+: the type of the span (such as \http, \db and so on)
  • +child_of+: a \Span or a \Context instance representing the parent for this span. If not set, defaults to Tracer.call_context
  • +tags+: extra tags which should be added to the span.


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
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/ddtrace/tracer.rb', line 261

def trace(name, options = {})
  options[:child_of] ||= call_context

  # call the finish only if a block is given; this ensures
  # that a call to tracer.trace() without a block, returns
  # a span that should be manually finished.
  if block_given?
    span = nil
    return_value = nil

    begin
      begin
        span = start_span(name, options)
      rescue StandardError => e
        Datadog.logger.debug("Failed to start span: #{e}")
      ensure
        # We should yield to the provided block when possible, as this
        # block is application code that we don't want to hinder. We call:
        # * `yield(span)` during normal execution.
        # * `yield(nil)` if `start_span` fails with a runtime error.
        # * We don't yield during a fatal error, as the application is likely trying to
        #   end its execution (either due to a system error or graceful shutdown).
        return_value = yield(span) if span || e.is_a?(StandardError)
      end
    # rubocop:disable Lint/RescueException
    # Here we really want to catch *any* exception, not only StandardError,
    # as we really have no clue of what is in the block,
    # and it is user code which should be executed no matter what.
    # It's not a problem since we re-raise it afterwards so for example a
    # SignalException::Interrupt would still bubble up.
    # rubocop:disable Metrics/BlockNesting
    rescue Exception => e
      if (on_error_handler = options[:on_error]) && on_error_handler.respond_to?(:call)
        begin
          on_error_handler.call(span, e)
        rescue
          Datadog.logger.debug('Custom on_error handler failed, falling back to default')
          DEFAULT_ON_ERROR.call(span, e)
        end
      else
        Datadog.logger.debug('Custom on_error handler must be a callable, falling back to default') if on_error_handler
        DEFAULT_ON_ERROR.call(span, e)
      end
      raise e
    ensure
      span.finish unless span.nil?
    end

    return_value
  else
    start_span(name, options)
  end
end

#trace_completedObject



315
316
317
# File 'lib/ddtrace/tracer.rb', line 315

def trace_completed
  @trace_completed ||= TraceCompleted.new
end