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

Constant Summary collapse

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.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ddtrace/tracer.rb', line 72

def initialize(options = {})
  @enabled = options.fetch(:enabled, true)
  @writer = options.fetch(:writer, Datadog::Writer.new)
  @sampler = options.fetch(:sampler, Datadog::AllSampler.new)

  @provider = options.fetch(:context_provider, Datadog::DefaultContextProvider.new)
  @provider ||= Datadog::DefaultContextProvider.new # @provider should never be nil

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

  @mutex = Mutex.new
  @tags = options.fetch(:tags, Datadog.configuration.tags)
  @default_service = options.fetch(:default_service, Datadog.configuration.service)

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

Instance Attribute Details

#context_flushObject (readonly)

Returns the value of attribute context_flush.



23
24
25
# File 'lib/ddtrace/tracer.rb', line 23

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.



142
143
144
145
146
147
148
149
150
151
# File 'lib/ddtrace/tracer.rb', line 142

def default_service
  return @default_service if instance_variable_defined?(:@default_service) && @default_service
  begin
    @default_service = File.basename($PROGRAM_NAME, '.*')
  rescue StandardError => e
    Datadog::Logger.log.error("unable to guess default service: #{e}")
    @default_service = 'ruby'.freeze
  end
  @default_service
end

#enabledObject

Returns the value of attribute enabled.



24
25
26
# File 'lib/ddtrace/tracer.rb', line 24

def enabled
  @enabled
end

#providerObject (readonly)

Returns the value of attribute provider.



23
24
25
# File 'lib/ddtrace/tracer.rb', line 23

def provider
  @provider
end

#samplerObject (readonly)

Returns the value of attribute sampler.



23
24
25
# File 'lib/ddtrace/tracer.rb', line 23

def sampler
  @sampler
end

#tagsObject (readonly)

Returns the value of attribute tags.



23
24
25
# File 'lib/ddtrace/tracer.rb', line 23

def tags
  @tags
end

#writerObject

Returns the value of attribute writer.



24
25
26
# File 'lib/ddtrace/tracer.rb', line 24

def writer
  @writer
end

Instance Method Details

#active_correlationObject

Return a CorrelationIdentifier for active span



328
329
330
# File 'lib/ddtrace/tracer.rb', line 328

def active_correlation
  Datadog::Correlation.identifier_from_context(call_context)
end

#active_root_spanObject

Return the current active root span or nil.



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

def active_root_span
  call_context.current_root_span
end

#active_spanObject

Return the current active span or nil.



318
319
320
# File 'lib/ddtrace/tracer.rb', line 318

def active_span
  call_context.current_span
end

#call_contextObject

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.



63
64
65
# File 'lib/ddtrace/tracer.rb', line 63

def call_context
  @provider.context
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')


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/ddtrace/tracer.rb', line 106

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)

  @context_flush = if options[:partial_flush]
                     Datadog::ContextFlush::Partial.new(options)
                   else
                     Datadog::ContextFlush::Finished.new
                   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.



298
299
300
301
302
303
# File 'lib/ddtrace/tracer.rb', line 298

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

  record_context(context)
end

#servicesObject



30
31
32
33
34
35
36
37
# File 'lib/ddtrace/tracer.rb', line 30

def services
  # Only log each deprecation warning once (safeguard against log spam)
  Datadog::Patcher.do_once('Tracer#set_service_info') do
    Datadog::Logger.log.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



129
130
131
132
133
134
135
136
137
# File 'lib/ddtrace/tracer.rb', line 129

def set_service_info(service, app, app_type)
  # Only log each deprecation warning once (safeguard against log spam)
  Datadog::Patcher.do_once('Tracer#set_service_info') do
    Datadog::Logger.log.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')


158
159
160
161
# File 'lib/ddtrace/tracer.rb', line 158

def set_tags(tags)
  string_tags = Hash[tags.collect { |k, v| [k.to_s, v] }]
  @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!


51
52
53
54
55
# File 'lib/ddtrace/tracer.rb', line 51

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.



185
186
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
# File 'lib/ddtrace/tracer.rb', line 185

def start_span(name, options = {})
  start_time = options.fetch(:start_time, Time.now.utc)

  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('system.pid', Process.pid)

    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
  @tags.each { |k, v| span.set_tag(k, v) } unless @tags.empty?
  tags.each { |k, v| span.set_tag(k, v) } unless tags.empty?
  span.start_time = 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)

  • tags: extra tags which should be added to the span.



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
290
291
292
# File 'lib/ddtrace/tracer.rb', line 256

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)
      # rubocop:disable Lint/UselessAssignment
      rescue StandardError => e
        Datadog::Logger.log.debug('Failed to start span: #{e}')
      ensure
        return_value = yield(span)
      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.
    rescue Exception => e
      (options[:on_error] || DEFAULT_ON_ERROR).call(span, e)
      raise e
    ensure
      span.finish unless span.nil?
    end

    return_value
  else
    start_span(name, options)
  end
end