class ScoutApm::LayerConverters::ConverterBase
Constants
- MAX_METRICS
-
To prevent huge traces from being generated, we should stop collecting detailed metrics as we go beyond some reasonably large count.
We should still add up the /all aggregates.
Attributes
Public Class Methods
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 10 def initialize(context, request, layer_finder, store=nil) @context = context @request = request @layer_finder = layer_finder @store = store @root_layer = request.root_layer @backtraces = [] @limited = false end
Public Instance Methods
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 86 def attach_backtraces(metric_hash) @backtraces.each do |meta_with_backtrace| metric_hash.keys.find { |k| k == meta_with_backtrace }.backtrace = meta_with_backtrace.backtrace end metric_hash end
Call this after you finish walking the layers, and want to take the set-aside backtraces and place them into the metas they match
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 113 def limited? !! @limited end
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 122 def make_meta_options(layer) scope_hash = make_meta_options_scope(layer) desc_hash = make_meta_options_desc_hash(layer) scope_hash.merge(desc_hash) end
When we make MetricMeta
records, we need to determine a few things from layer.
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 148 def make_meta_options_desc_hash(layer, max_desc_length=32768) if layer.desc desc_s = layer.desc.to_s trimmed_desc = desc_s[0 .. max_desc_length] {:desc => trimmed_desc} else {} end end
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 129 def make_meta_options_scope(layer) # This layer is scoped under another thing. Typically that means this is a layer under a view. # Like: Controller -> View/users/show -> ActiveRecord/user/find # in that example, the scope is the View/users/show if subscoped?(layer) {:scope => subscope_name} # We don't scope the controller under itself elsif layer == scope_layer {} # This layer is a top level metric ("ActiveRecord", or "HTTP" or # whatever, directly under the controller), so scope to the # Controller else {:scope => scope_layer.legacy_metric_name} end end
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 105 def over_metric_limit?(metric_hash) if metric_hash.size > MAX_METRICS @limited = true else false end end
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 32 def register_hooks(walker) @subscope_layers = [] walker.before do |layer| if layer.subscopable? @subscope_layers.push(layer) end end walker.after do |layer| if layer.subscopable? @subscope_layers.pop end end end
Subscoping
Keep a list of subscopes, but only ever use the front one. The rest get pushed/popped in cases when we have many levels of subscopable layers. This lets us push/pop without otherwise keeping track very closely.
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 21 def scope_layer layer_finder.scope end
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 215 def skip_layer?(layer) return false if layer.annotations.nil? return true if layer.annotations[:ignorable] end
Sometimes we start capturing a layer without knowing if we really want to make an entry for it. See ActiveRecord instrumentation for an example. We start capturing before we know if a query is cached or not, and want to skip any cached queries.
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 192 def store_aggregate_metric(layer, metric_hash, allocation_metric_hash) meta = MetricMeta.new("#{layer.type}/all") metric_hash[meta] ||= MetricStats.new(false) allocation_metric_hash[meta] ||= MetricStats.new(false) # timing stat = metric_hash[meta] stat.update!(layer.total_call_time, layer.total_exclusive_time) # allocations stat = allocation_metric_hash[meta] stat.update!(layer.total_allocations, layer.total_exclusive_allocations) end
Merged Metric - no specifics, just sum up by type (ActiveRecord, View, HTTP, etc)
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 74 def store_backtrace(layer, meta) return unless layer.backtrace bt = ScoutApm::Utils::BacktraceParser.new(layer.backtrace).call if bt.any? meta.backtrace = bt @backtraces << meta end end
Call this as you are processing each layer. It will store off backtraces
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 164 def store_specific_metric(layer, metric_hash, allocation_metric_hash) return false if over_metric_limit?(metric_hash) meta_options = make_meta_options(layer) meta = MetricMeta.new(layer.legacy_metric_name, meta_options) meta.extra.merge!(layer.annotations) if layer.annotations store_backtrace(layer, meta) metric_hash[meta] ||= MetricStats.new(meta_options.has_key?(:scope)) allocation_metric_hash[meta] ||= MetricStats.new(meta_options.has_key?(:scope)) # timing stat = metric_hash[meta] stat.update!(layer.total_call_time, layer.total_exclusive_time) # allocations stat = allocation_metric_hash[meta] stat.update!(layer.total_allocations, layer.total_exclusive_allocations) if LimitedLayer === layer metric_hash[meta].call_count = layer.count allocation_metric_hash[meta].call_count = layer.count end end
This is the detailed metric - type, name, backtrace, annotations, etc.
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 52 def subscope_name @subscope_layers.first.legacy_metric_name end
Source
# File lib/scout_apm/layer_converters/converter_base.rb, line 48 def subscoped?(layer) @subscope_layers.first && layer != @subscope_layers.first # Don't scope under ourself. end