class Datadog::Span

Represents a logical unit of work in the system. Each trace consists of one or more spans. Each span consists of a start time and a duration. For example, a span can describe the time spent on a distributed call on a separate machine, or the time spent in a small component within a larger operation. Spans can be nested within each other, and in those instances will have a parent-child relationship.

rubocop:disable Metrics/ClassLength

Constants

EXTERNAL_MAX_ID

While we only generate 63-bit integers due to limitations in other languages, we support parsing 64-bit integers for distributed tracing since an upstream system may generate one

MAX_ID

The max value for a Span identifier. Span and trace identifiers should be strictly positive and strictly inferior to this limit.

Limited to 63-bit positive integers, as some other languages might be limited to this, and IDs need to be easy to port across various languages and platforms.

NUMERIC_TAG_SIZE_RANGE

This limit is for numeric tags because uint64 could end up rounded.

Attributes

context[RW]
end_time[RW]
name[RW]
parent[R]
parent_id[RW]
resource[RW]
sampled[RW]
service[RW]
span_id[RW]
span_type[RW]
start_time[RW]
status[RW]
trace_id[RW]
tracer[RW]

Public Class Methods

new(tracer, name, options = {}) click to toggle source

Create a new span linked to the given tracer. Call the Tracer method start_span() and then finish() once the tracer operation is over.

  • 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)

  • parent_id: the identifier of the parent span

  • trace_id: the identifier of the root span for this trace

  • context: the context of the span

# File lib/ddtrace/span.rb, line 54
def initialize(tracer, name, options = {})
  @tracer = tracer

  @name = name
  @service = options.fetch(:service, nil)
  @resource = options.fetch(:resource, name)
  @span_type = options.fetch(:span_type, nil)

  @span_id = Datadog::Utils.next_id
  @parent_id = options.fetch(:parent_id, 0)
  @trace_id = options.fetch(:trace_id, Datadog::Utils.next_id)

  @context = options.fetch(:context, nil)

  @meta = {}
  @metrics = {}
  @status = 0

  @parent = nil
  @sampled = true

  @start_time = nil # set by Tracer.start_span
  @end_time = nil # set by Span.finish

  @allocation_count_start = now_allocations
  @allocation_count_finish = @allocation_count_start
end

Public Instance Methods

allocations() click to toggle source
# File lib/ddtrace/span.rb, line 229
def allocations
  @allocation_count_finish - @allocation_count_start
end
clear_metric(key) click to toggle source

This method removes a metric for the given key. It acts like {#remove_tag}.

# File lib/ddtrace/span.rb, line 144
def clear_metric(key)
  @metrics.delete(key)
end
clear_tag(key) click to toggle source

This method removes a tag for the given key.

# File lib/ddtrace/span.rb, line 121
def clear_tag(key)
  @meta.delete(key)
end
finish(finish_time = nil) click to toggle source

Mark the span finished at the current time and submit it.

# File lib/ddtrace/span.rb, line 164
def finish(finish_time = nil)
  # A span should not be finished twice. Note that this is not thread-safe,
  # finish is called from multiple threads, a given span might be finished
  # several times. Again, one should not do this, so this test is more a
  # fallback to avoid very bad things and protect you in most common cases.
  return if finished?

  @allocation_count_finish = now_allocations

  # Provide a default start_time if unset, but this should have been set by start_span.
  # Using now here causes 0-duration spans, still, this is expected, as we never
  # explicitely say when it started.
  @start_time ||= Time.now.utc

  @end_time = finish_time.nil? ? Time.now.utc : finish_time # finish this

  # Finish does not really do anything if the span is not bound to a tracer and a context.
  return self if @tracer.nil? || @context.nil?

  # spans without a service would be dropped, so here we provide a default.
  # This should really never happen with integrations in contrib, as a default
  # service is always set. It's only for custom instrumentation.
  @service ||= (@tracer && @tracer.default_service)

  begin
    @context.close_span(self)
    @tracer.record(self)
  rescue StandardError => e
    Datadog.logger.debug("error recording finished trace: #{e}")
    Datadog.health_metrics.error_span_finish(1, tags: ["error:#{e.class.name}"])
  end
  self
end
finished?() click to toggle source

Return whether the span is finished or not.

# File lib/ddtrace/span.rb, line 199
def finished?
  !@end_time.nil?
end
get_metric(key) click to toggle source

Return the metric with the given key, nil if it doesn't exist.

# File lib/ddtrace/span.rb, line 149
def get_metric(key)
  @metrics[key] || @meta[key]
end
get_tag(key) click to toggle source

Return the tag with the given key, nil if it doesn't exist.

# File lib/ddtrace/span.rb, line 126
def get_tag(key)
  @meta[key] || @metrics[key]
end
parent=(parent) click to toggle source

Set this span's parent, inheriting any properties not explicitly set. If the parent is nil, set the span zero values.

# File lib/ddtrace/span.rb, line 215
def parent=(parent)
  @parent = parent

  if parent.nil?
    @trace_id = @span_id
    @parent_id = 0
  else
    @trace_id = parent.trace_id
    @parent_id = parent.span_id
    @service ||= parent.service
    @sampled = parent.sampled
  end
end
pretty_print(q) click to toggle source

Return a human readable version of the span

# File lib/ddtrace/span.rb, line 258
def pretty_print(q)
  start_time = (@start_time.to_f * 1e9).to_i rescue '-'
  end_time = (@end_time.to_f * 1e9).to_i rescue '-'
  duration = ((@end_time - @start_time) * 1e9).to_i rescue 0
  q.group 0 do
    q.breakable
    q.text "Name: #{@name}\n"
    q.text "Span ID: #{@span_id}\n"
    q.text "Parent ID: #{@parent_id}\n"
    q.text "Trace ID: #{@trace_id}\n"
    q.text "Type: #{@span_type}\n"
    q.text "Service: #{@service}\n"
    q.text "Resource: #{@resource}\n"
    q.text "Error: #{@status}\n"
    q.text "Start: #{start_time}\n"
    q.text "End: #{end_time}\n"
    q.text "Duration: #{duration}\n"
    q.text "Allocations: #{allocations}\n"
    q.group(2, 'Tags: [', "]\n") do
      q.breakable
      q.seplist @meta.each do |key, value|
        q.text "#{key} => #{value}"
      end
    end
    q.group(2, 'Metrics: [', ']') do
      q.breakable
      q.seplist @metrics.each do |key, value|
        q.text "#{key} => #{value}"
      end
    end
  end
end
set_error(e) click to toggle source

Mark the span with the given error.

# File lib/ddtrace/span.rb, line 154
def set_error(e)
  e = Error.build_from(e)

  @status = Ext::Errors::STATUS
  set_tag(Ext::Errors::TYPE, e.type) unless e.type.empty?
  set_tag(Ext::Errors::MSG, e.message) unless e.message.empty?
  set_tag(Ext::Errors::STACK, e.backtrace) unless e.backtrace.empty?
end
set_metric(key, value) click to toggle source

This method sets a tag with a floating point value for the given key. It acts like `set_tag()` and it simply add a tag without further processing.

# File lib/ddtrace/span.rb, line 132
def set_metric(key, value)
  # Keys must be unique between tags and metrics
  @meta.delete(key)

  # enforce that the value is a floating point number
  value = Float(value)
  @metrics[key] = value
rescue StandardError => e
  Datadog.logger.debug("Unable to set the metric #{key}, ignoring it. Caused by: #{e}")
end
set_parent(parent) click to toggle source

DEPRECATED: remove this function in the next release, replaced by “parent=“

# File lib/ddtrace/span.rb, line 209
def set_parent(parent)
  self.parent = parent
end
set_tag(key, value = nil) click to toggle source

Set the given key / value tag pair on the span. Keys and values must be strings. A valid example is:

span.set_tag('http.method', request.method)
# File lib/ddtrace/span.rb, line 86
def set_tag(key, value = nil)
  # Keys must be unique between tags and metrics
  @metrics.delete(key)

  # Ensure `http.status_code` is always a string so it is added to
  #   @meta instead of @metrics
  # DEV: This is necessary because the agent looks to `meta['http.status_code']` for
  #   tagging necessary metrics
  value = value.to_s if key == Ext::HTTP::STATUS_CODE

  # NOTE: Adding numeric tags as metrics is stop-gap support
  #       for numeric typed tags. Eventually they will become
  #       tags again.
  # Any numeric that is not an integer greater than max size is logged as a metric.
  # Everything else gets logged as a tag.
  if value.is_a?(Numeric) && !(value.is_a?(Integer) && !NUMERIC_TAG_SIZE_RANGE.cover?(value))
    set_metric(key, value)
  else
    @meta[key] = value.to_s
  end
rescue StandardError => e
  Datadog.logger.debug("Unable to set the tag #{key}, ignoring it. Caused by: #{e}")
end
set_tags(tags) click to toggle source

Sets tags from given hash, for each key in hash it sets the tag with that key and associated value from the hash. It is shortcut for `set_tag`. Keys and values of the hash must be strings. Note that nested hashes are not supported. A valid example is:

span.set_tags({ "http.method" => "GET", "user.id" => "234" })
# File lib/ddtrace/span.rb, line 116
def set_tags(tags)
  tags.each { |k, v| set_tag(k, v) }
end
to_hash() click to toggle source

Return the hash representation of the current span.

# File lib/ddtrace/span.rb, line 234
def to_hash
  h = {
    span_id: @span_id,
    parent_id: @parent_id,
    trace_id: @trace_id,
    name: @name,
    service: @service,
    resource: @resource,
    type: @span_type,
    meta: @meta,
    metrics: @metrics,
    allocations: allocations,
    error: @status
  }

  if !@start_time.nil? && !@end_time.nil?
    h[:start] = (@start_time.to_f * 1e9).to_i
    h[:duration] = ((@end_time - @start_time) * 1e9).to_i
  end

  h
end
to_s() click to toggle source

Return a string representation of the span.

# File lib/ddtrace/span.rb, line 204
def to_s
  "Span(name:#{@name},sid:#{@span_id},tid:#{@trace_id},pid:#{@parent_id})"
end

Private Instance Methods

now_allocations() click to toggle source
# File lib/ddtrace/span.rb, line 294
def now_allocations
  0
end