Class: ScoutApm::Layer

Inherits:
Object
  • Object
show all
Defined in:
lib/scout_apm/layer.rb

Constant Summary collapse

BACKTRACE_CALLER_LIMIT =

maximum number of lines to send thru for backtrace analysis

50

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type, name, start_time = Time.now) ⇒ Layer

Returns a new instance of Layer.



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/scout_apm/layer.rb', line 56

def initialize(type, name, start_time = Time.now)
  @type = type
  @name = name
  @start_time = start_time
  @allocations_start = ScoutApm::Instruments::Allocations.count
  @allocations_stop = 0

  # initialize these only on first use
  @children = nil
  @annotations = nil
  @desc = nil
end

Instance Attribute Details

#allocations_startObject (readonly)

Returns the value of attribute allocations_start.



52
53
54
# File 'lib/scout_apm/layer.rb', line 52

def allocations_start
  @allocations_start
end

#allocations_stopObject (readonly)

Returns the value of attribute allocations_stop.



52
53
54
# File 'lib/scout_apm/layer.rb', line 52

def allocations_stop
  @allocations_stop
end

#annotationsObject (readonly)

As we go through a part of a request, instrumentation can store additional data Known Keys:

:record_count - The number of rows returned by an AR query (From notification instantiation.active_record)
:class_name   - The ActiveRecord class name (From notification instantiation.active_record)

If no annotations are ever set, this will return nil



50
51
52
# File 'lib/scout_apm/layer.rb', line 50

def annotations
  @annotations
end

#backtraceObject

If this layer took longer than a fixed amount of time, store the backtrace of where it occurred.



39
40
41
# File 'lib/scout_apm/layer.rb', line 39

def backtrace
  @backtrace
end

#descObject

The description of this layer. Will contain additional details specific to the type of layer. For an ActiveRecord metric, it will contain the SQL run For an outoing HTTP call, it will contain the remote URL accessed Leave blank if there is nothing to note



35
36
37
# File 'lib/scout_apm/layer.rb', line 35

def desc
  @desc
end

#file_nameObject

The file name associated with the layer. Only used for autoinstruments overhead logging.



42
43
44
# File 'lib/scout_apm/layer.rb', line 42

def file_name
  @file_name
end

#nameObject

Name: a more specific name of this single item

Examples: "Rack::Cache", "User#find", "users/index", "users/index.html.erb"

Accessor, so we can update a layer if multiple pieces of instrumentation work

together at different layers to fill in the full data. See the ActiveRecord
instrumentation for an example of how this is useful


14
15
16
# File 'lib/scout_apm/layer.rb', line 14

def name
  @name
end

#start_timeObject (readonly)

Time objects recording the start & stop times of this layer



29
30
31
# File 'lib/scout_apm/layer.rb', line 29

def start_time
  @start_time
end

#stop_timeObject (readonly)

Time objects recording the start & stop times of this layer



29
30
31
# File 'lib/scout_apm/layer.rb', line 29

def stop_time
  @stop_time
end

#typeObject (readonly)

Type: a general name for the kind of thing being tracked.

Examples: "Middleware", "ActiveRecord", "Controller", "View"


6
7
8
# File 'lib/scout_apm/layer.rb', line 6

def type
  @type
end

Instance Method Details

#add_child(child) ⇒ Object



73
74
75
76
# File 'lib/scout_apm/layer.rb', line 73

def add_child(child)
  @children ||= LayerChildrenSet.new
  @children << child
end

#annotate_layer(hsh) ⇒ Object

This data is internal to ScoutApm, to add custom information, use the Context api.



92
93
94
95
# File 'lib/scout_apm/layer.rb', line 92

def annotate_layer(hsh)
  @annotations ||= {}
  @annotations.merge!(hsh)
end

#caller_arrayObject

In Ruby 2.0+, we can pass the range directly to the caller to reduce the memory footprint.



117
118
119
120
121
122
123
124
# File 'lib/scout_apm/layer.rb', line 117

def caller_array
  # omits the first several callers which are in the ScoutAPM stack.
  if ScoutApm::Agent.instance.context.environment.ruby_2?
    caller(3...BACKTRACE_CALLER_LIMIT)
  else
    caller[3...BACKTRACE_CALLER_LIMIT]
  end
end

#capture_backtrace!Object



112
113
114
# File 'lib/scout_apm/layer.rb', line 112

def capture_backtrace!
  @backtrace = caller_array
end

#childrenObject

An array of children layers For instance, if we are in a middleware, there will likely be only a single child, which is another middleware. In a Controller, we may have a handful of children: [ActiveRecord, ActiveRecord, View, HTTP Call].

This useful to get actual time spent in this layer vs. children time

TODO: Check callers for compatibility w/ nil to avoid making an empty array



24
25
26
# File 'lib/scout_apm/layer.rb', line 24

def children
  @children || LayerChildrenSet.new
end

#legacy_metric_nameObject

This is the old style name. This function is used for now, but should be removed, and the new type & name split should be enforced through the app.



108
109
110
# File 'lib/scout_apm/layer.rb', line 108

def legacy_metric_name
  "#{type}/#{name}"
end

#limited?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/scout_apm/layer.rb', line 69

def limited?
  false
end

#record_allocations!Object

Fetch the current number of allocated objects. This will always increment - we fetch when initializing and when stopping the layer.



83
84
85
# File 'lib/scout_apm/layer.rb', line 83

def record_allocations!
  @allocations_stop = ScoutApm::Instruments::Allocations.count
end

#record_stop_time!(stop_time = Time.now) ⇒ Object



78
79
80
# File 'lib/scout_apm/layer.rb', line 78

def record_stop_time!(stop_time = Time.now)
  @stop_time = stop_time
end

#subscopable!Object



97
98
99
# File 'lib/scout_apm/layer.rb', line 97

def subscopable!
  @subscopable = true
end

#subscopable?Boolean

Returns:

  • (Boolean)


101
102
103
# File 'lib/scout_apm/layer.rb', line 101

def subscopable?
  @subscopable
end

#to_sObject

May not be safe to call in every rails app, relies on Time#iso8601



131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/scout_apm/layer.rb', line 131

def to_s
  name_clause = "#{type}/#{name}"

  total_string = total_call_time == 0 ? nil : "Total: #{total_call_time}"
  self_string = total_exclusive_time == 0 ? nil : "Self: #{total_exclusive_time}"
  timing_string = [total_string, self_string].compact.join(", ")

  time_clause = "(Start: #{start_time.iso8601} / Stop: #{stop_time.try(:iso8601)} [#{timing_string}])"
  desc_clause = "Description: #{desc.inspect}"
  children_clause = "Children: #{children.length}"

  "<Layer: #{name_clause} #{time_clause} #{desc_clause} #{children_clause}>"
end

#total_allocationsObject

These are almost identical to the timing metrics.



174
175
176
177
178
179
180
181
# File 'lib/scout_apm/layer.rb', line 174

def total_allocations
  if @allocations_stop > 0
    allocations = (@allocations_stop - @allocations_start)
  else
    allocations = (ScoutApm::Instruments::Allocations.count - @allocations_start)
  end
  allocations < 0 ? 0 : allocations
end

#total_call_timeObject

Time Calculations



149
150
151
152
153
154
155
# File 'lib/scout_apm/layer.rb', line 149

def total_call_time
  if stop_time
    stop_time - start_time
  else
    Time.now - start_time
  end
end

#total_exclusive_allocationsObject



183
184
185
# File 'lib/scout_apm/layer.rb', line 183

def total_exclusive_allocations
  total_allocations - child_allocations
end

#total_exclusive_timeObject



157
158
159
# File 'lib/scout_apm/layer.rb', line 157

def total_exclusive_time
  total_call_time - child_time
end