Class: ScoutApm::LayerConverters::TraceConverter

Inherits:
ConverterBase show all
Defined in:
lib/scout_apm/layer_converters/trace_converter.rb

Constant Summary collapse

MAX_SPANS =

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

1500

Constants inherited from ConverterBase

ConverterBase::MAX_METRICS

Instance Attribute Summary

Attributes inherited from ConverterBase

#context, #layer_finder, #request, #root_layer

Instance Method Summary collapse

Methods inherited from ConverterBase

#attach_backtraces, #initialize, #make_meta_options, #make_meta_options_desc_hash, #make_meta_options_scope, #over_metric_limit?, #register_hooks, #scope_layer, #skip_layer?, #store_aggregate_metric, #store_backtrace, #store_specific_metric, #subscope_name, #subscoped?

Constructor Details

This class inherits a constructor from ScoutApm::LayerConverters::ConverterBase

Instance Method Details

#backtrace_parser(lines) ⇒ Object

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"

, ]



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/scout_apm/layer_converters/trace_converter.rb', line 139

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

#callObject

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.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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
80
81
82
# 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)

  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) ⇒ Object

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



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
# File 'lib/scout_apm/layer_converters/trace_converter.rb', line 86

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?Boolean

Returns:

  • (Boolean)


176
177
178
# File 'lib/scout_apm/layer_converters/trace_converter.rb', line 176

def limited?
  !! @limited
end

#log_over_span_limitObject



170
171
172
173
174
# File 'lib/scout_apm/layer_converters/trace_converter.rb', line 170

def log_over_span_limit
  unless limited?
    context.logger.debug "Not recording additional spans for #{name}. Over the span limit."
  end
end

#nameObject

ScoreItemSet API #



22
# File 'lib/scout_apm/layer_converters/trace_converter.rb', line 22

def name; request.unique_name; end

#over_span_limit?(spans) ⇒ Boolean

Returns:

  • (Boolean)


161
162
163
164
165
166
167
168
# File 'lib/scout_apm/layer_converters/trace_converter.rb', line 161

def over_span_limit?(spans)
  if spans.size > MAX_SPANS
    log_over_span_limit
    @limited = true
  else
    false
  end
end

#record!Object



9
10
11
12
13
14
15
16
17
# 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

#scoreObject



23
# File 'lib/scout_apm/layer_converters/trace_converter.rb', line 23

def score; @points; end