class ScoutApm::LayerConverters::TraceConverter

Constants

MAX_SPANS

To prevent huge traces from being generated, we stop collecting spans as we go beyond some reasonably large count.

Public Instance Methods

backtrace_parser(lines) click to toggle source

Take an array of ruby backtrace lines and split it into an array of hashes like:

“/Users/cschneid/.rvm/rubies/ruby-2.2.7/lib/ruby/2.2.0/irb/workspace.rb:86:in `eval'”, …

turns into:

[ {

"file": "app/controllers/users_controller.rb",
"line": 10,
"function": "index"

}, ]

# File lib/scout_apm/layer_converters/trace_converter.rb, line 142
def backtrace_parser(lines)
  bt = ScoutApm::Utils::BacktraceParser.new(lines).call

  bt.map do |line|
    match = line.match(/(.*):(\d+):in `(.*)'/)
    {
      "file" => match[1],
      "line" => match[2],
      "function" => match[3],
    }
  end

end
call() click to toggle source

Unconditionally attempts to convert this into a DetailedTrace object. Can return nil if the request didn't have any scope_layer or if `timeline_traces` aren't enabled.

# File lib/scout_apm/layer_converters/trace_converter.rb, line 27
def call
  return nil unless scope_layer
  return nil unless context.config.value('timeline_traces')

  # Since this request is being stored, update the needed counters
  context.slow_request_policy.stored!(request)

  # record the change in memory usage
  mem_delta = ScoutApm::Instruments::Process::ProcessMemory.new(context).rss_to_mb(@request.capture_mem_delta!)

  transaction_id = request.transaction_id
  revision = context.environment.git_revision.sha
  start_instant = request.root_layer.start_time
  stop_instant = request.root_layer.stop_time
  type = if request.web?
           "Web"
         elsif request.job?
           "Job"
         else
           "Unknown"
         end

  # Create request tags
  #
  tags = {
    :allocations => request.root_layer.total_allocations,
    :mem_delta => mem_delta,
  }.merge(request.context.to_flat_hash)

  host = context.environment.hostname
  path = request.annotations[:uri] || ""
  code = "" # User#index for instance

  spans = create_spans(request.root_layer)
  if limited?
    tags[:"scout.reached_span_cap"] = true
  end

  DetailedTrace.new(
    transaction_id,
    revision,
    host,
    start_instant,
    stop_instant,
    type,

    path,
    code,

    spans,
    tags

    # total_score = 0,
    # percentile_score = 0,
    # age_score = 0,
    # memory_delta_score = 0,
    # memory_allocations_score = 0
  )
end
create_spans(layer, parent_id = nil) click to toggle source

Returns an array of span objects. Uses recursion to get all children wired up w/ correct parent_ids

# File lib/scout_apm/layer_converters/trace_converter.rb, line 89
def create_spans(layer, parent_id = nil)
  span_id = ScoutApm::Utils::SpanId.new.to_s

  start_instant = layer.start_time
  stop_instant = layer.stop_time
  operation = layer.legacy_metric_name
  tags = {
    :start_allocations => layer.allocations_start,
    :stop_allocations => layer.allocations_stop,
  }
  if layer.desc
    tags[:desc] = layer.desc.to_s
  end
  if layer.annotations && layer.annotations[:record_count]
    tags["db.record_count"] = layer.annotations[:record_count]
  end
  if layer.annotations && layer.annotations[:class_name]
    tags["db.class_name"] = layer.annotations[:class_name]
  end
  if layer.backtrace
    tags[:backtrace] = backtrace_parser(layer.backtrace) rescue nil
  end

  # Collect up self, and all children into result array
  result = []
  result << DetailedTraceSpan.new(
    span_id.to_s,
    parent_id.to_s,
    start_instant,
    stop_instant,
    operation,
    tags)

  layer.children.each do |child|
    # Don't create spans from limited layers. These don't have start/stop times and our processing can't
    # handle these yet.
    unless over_span_limit?(result) || child.is_a?(LimitedLayer)
      result += create_spans(child, span_id)
    end
  end

  return result
end
limited?() click to toggle source
# File lib/scout_apm/layer_converters/trace_converter.rb, line 179
def limited?
  !! @limited
end
log_over_span_limit() click to toggle source
# File lib/scout_apm/layer_converters/trace_converter.rb, line 173
def log_over_span_limit
  unless limited?
    context.logger.debug "Not recording additional spans for #{name}. Over the span limit."
  end
end
name() click to toggle source

ScoreItemSet API #

# File lib/scout_apm/layer_converters/trace_converter.rb, line 22
def name; request.unique_name; end
over_span_limit?(spans) click to toggle source
# File lib/scout_apm/layer_converters/trace_converter.rb, line 164
def over_span_limit?(spans)
  if spans.size > MAX_SPANS
    log_over_span_limit
    @limited = true
  else
    false
  end
end
record!() click to toggle source

Converter API #

# File lib/scout_apm/layer_converters/trace_converter.rb, line 9
def record!
  @points = context.slow_request_policy.score(request)

  # Let the store know we're here, and if it wants our data, it will call
  # back into #call
  @store.track_trace!(self)

  nil # not returning anything in the layer results ... not used
end
score() click to toggle source
# File lib/scout_apm/layer_converters/trace_converter.rb, line 23
def score; @points; end