scout_apm 3.0.0.pre3 → 3.0.0.pre4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +22 -0
- data/lib/scout_apm/agent.rb +7 -14
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -1
- data/lib/scout_apm/instant/middleware.rb +3 -0
- data/lib/scout_apm/instruments/active_record.rb +3 -1
- data/lib/scout_apm/instruments/net_http.rb +7 -2
- data/lib/scout_apm/layer.rb +16 -4
- data/lib/scout_apm/layer_children_set.rb +72 -0
- data/lib/scout_apm/layer_converters/converter_base.rb +8 -4
- data/lib/scout_apm/layer_converters/job_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/metric_converter.rb +1 -1
- data/lib/scout_apm/limited_layer.rb +129 -0
- data/lib/scout_apm/tracked_request.rb +60 -4
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +2 -1
- data/scout_apm.gemspec +1 -0
- data/test/unit/instruments/net_http_test.rb +21 -0
- data/test/unit/layer_children_set_test.rb +88 -0
- data/test/unit/limited_layer_test.rb +53 -0
- metadata +24 -3
- data/lib/scout_apm/instruments/delayed_job.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65e71c8c9518a483f581e8c903ce36fa216fd964
|
4
|
+
data.tar.gz: 693bab7ca8a2189697b0802e2919ce9ebf6a81e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6050bfc0ea85e521707640550eee61cb147f395e7b6610abb1774faf2bdf2009557c93057e886c65f565192c7a46aa5b0ef54b315c8d8711f62d91bb5905157
|
7
|
+
data.tar.gz: d9a50a872883792a85e8cbe7987a4f9e2ace192b09b9474c388d42bb204869f326e8f46bedd41078d0505b1c7d3d0fc40a1dc9ab9fd243743e44e5af61eaa17e
|
data/CHANGELOG.markdown
CHANGED
@@ -6,6 +6,28 @@
|
|
6
6
|
|
7
7
|
* ScoutProf BETA
|
8
8
|
|
9
|
+
# 2.1.15
|
10
|
+
|
11
|
+
* Limit memory usage for very long running requests.
|
12
|
+
|
13
|
+
# 2.1.14
|
14
|
+
|
15
|
+
* Add TrackedRequest#ignore_request! to entirely ignore and stop capturing a
|
16
|
+
certain request. Use in your code by calling:
|
17
|
+
ScoutApm::RequestManager.lookup.ignore_request!
|
18
|
+
|
19
|
+
# 2.1.13
|
20
|
+
|
21
|
+
* Rework Delayed Job instrumentation to not interfere with other instruments.
|
22
|
+
|
23
|
+
# 2.1.12
|
24
|
+
|
25
|
+
* Revert 2.1.11's Delayed Job change - caused issues in a handful of environments
|
26
|
+
|
27
|
+
# 2.1.11
|
28
|
+
|
29
|
+
* Support alternate methods of launching Delayed Job
|
30
|
+
|
9
31
|
# 2.1.10
|
10
32
|
|
11
33
|
* Fix issue getting a default Application Name when it wasn't explicitly set
|
data/lib/scout_apm/agent.rb
CHANGED
@@ -281,20 +281,13 @@ module ScoutApm
|
|
281
281
|
|
282
282
|
# Loads the instrumention logic.
|
283
283
|
def load_instruments
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
when :rails then install_instrument(ScoutApm::Instruments::ActionControllerRails2)
|
292
|
-
when :rails3_or_4 then
|
293
|
-
install_instrument(ScoutApm::Instruments::ActionControllerRails3Rails4)
|
294
|
-
install_instrument(ScoutApm::Instruments::MiddlewareSummary)
|
295
|
-
install_instrument(ScoutApm::Instruments::RailsRouter)
|
296
|
-
# when :sinatra then install_instrument(ScoutApm::Instruments::Sinatra)
|
297
|
-
end
|
284
|
+
case environment.framework
|
285
|
+
when :rails then
|
286
|
+
install_instrument(ScoutApm::Instruments::ActionControllerRails2)
|
287
|
+
when :rails3_or_4 then
|
288
|
+
install_instrument(ScoutApm::Instruments::ActionControllerRails3Rails4)
|
289
|
+
install_instrument(ScoutApm::Instruments::MiddlewareSummary)
|
290
|
+
install_instrument(ScoutApm::Instruments::RailsRouter)
|
298
291
|
end
|
299
292
|
|
300
293
|
install_instrument(ScoutApm::Instruments::ActiveRecord)
|
@@ -50,6 +50,9 @@ module ScoutApm
|
|
50
50
|
if ScoutApm::Agent.instance.config.value('dev_trace')
|
51
51
|
if response.respond_to?(:body)
|
52
52
|
req = ScoutApm::RequestManager.lookup
|
53
|
+
|
54
|
+
return [status, headers, response] if req.ignoring_request?
|
55
|
+
|
53
56
|
slow_converter = LayerConverters::SlowRequestConverter.new(req)
|
54
57
|
trace = slow_converter.call
|
55
58
|
if trace
|
@@ -62,8 +62,10 @@ module ScoutApm
|
|
62
62
|
layer = req.current_layer
|
63
63
|
if layer && layer.type == "ActiveRecord"
|
64
64
|
layer.annotate_layer(payload)
|
65
|
-
|
65
|
+
elsif layer
|
66
66
|
ScoutApm::Agent.instance.logger.debug("Expected layer type: ActiveRecord, got #{layer && layer.type}")
|
67
|
+
else
|
68
|
+
# noop, no layer at all. We're probably ignoring this req.
|
67
69
|
end
|
68
70
|
end
|
69
71
|
end
|
@@ -22,12 +22,17 @@ module ScoutApm
|
|
22
22
|
include ScoutApm::Tracer
|
23
23
|
|
24
24
|
def request_with_scout_instruments(*args,&block)
|
25
|
-
|
26
|
-
self.class.instrument("HTTP", "request", :desc => url) do
|
25
|
+
self.class.instrument("HTTP", "request", :desc => request_scout_description(args.first)) do
|
27
26
|
request_without_scout_instruments(*args, &block)
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
30
|
+
def request_scout_description(req)
|
31
|
+
path = req.path
|
32
|
+
path = path.path if path.respond_to?(:path)
|
33
|
+
(@address + path.split('?').first)[0..99]
|
34
|
+
end
|
35
|
+
|
31
36
|
alias request_without_scout_instruments request
|
32
37
|
alias request request_with_scout_instruments
|
33
38
|
end
|
data/lib/scout_apm/layer.rb
CHANGED
@@ -13,13 +13,17 @@ module ScoutApm
|
|
13
13
|
# instrumentation for an example of how this is useful
|
14
14
|
attr_accessor :name
|
15
15
|
|
16
|
-
# An array of children layers
|
16
|
+
# An array of children layers
|
17
17
|
# For instance, if we are in a middleware, there will likely be only a single
|
18
18
|
# child, which is another middleware. In a Controller, we may have a handful
|
19
19
|
# of children: [ActiveRecord, ActiveRecord, View, HTTP Call].
|
20
20
|
#
|
21
21
|
# This useful to get actual time spent in this layer vs. children time
|
22
|
-
|
22
|
+
#
|
23
|
+
# TODO: Check callers for compatibility w/ nil to avoid making an empty array
|
24
|
+
def children
|
25
|
+
@children || LayerChildrenSet.new
|
26
|
+
end
|
23
27
|
|
24
28
|
# Time objects recording the start & stop times of this layer
|
25
29
|
attr_reader :start_time, :stop_time
|
@@ -38,6 +42,8 @@ module ScoutApm
|
|
38
42
|
# Known Keys:
|
39
43
|
# :record_count - The number of rows returned by an AR query (From notification instantiation.active_record)
|
40
44
|
# :class_name - The ActiveRecord class name (From notification instantiation.active_record)
|
45
|
+
#
|
46
|
+
# If no annotations are ever set, this will return nil
|
41
47
|
attr_reader :annotations
|
42
48
|
|
43
49
|
# ScoutProf - trace_index is an index into the Stack structure in the C
|
@@ -59,11 +65,13 @@ module ScoutApm
|
|
59
65
|
def initialize(type, name, start_time = Time.now)
|
60
66
|
@type = type
|
61
67
|
@name = name
|
62
|
-
@annotations = {}
|
63
68
|
@start_time = start_time
|
64
69
|
@allocations_start = ScoutApm::Instruments::Allocations.count
|
65
70
|
@allocations_stop = 0
|
66
|
-
|
71
|
+
|
72
|
+
# initialize these only on first use
|
73
|
+
@children = nil
|
74
|
+
@annotations = nil
|
67
75
|
@desc = nil
|
68
76
|
|
69
77
|
@traces = ScoutApm::TraceSet.new
|
@@ -73,6 +81,7 @@ module ScoutApm
|
|
73
81
|
end
|
74
82
|
|
75
83
|
def add_child(child)
|
84
|
+
@children ||= LayerChildrenSet.new
|
76
85
|
@children << child
|
77
86
|
end
|
78
87
|
|
@@ -91,6 +100,7 @@ module ScoutApm
|
|
91
100
|
|
92
101
|
# This data is internal to ScoutApm, to add custom information, use the Context api.
|
93
102
|
def annotate_layer(hsh)
|
103
|
+
@annotations ||= {}
|
94
104
|
@annotations.merge!(hsh)
|
95
105
|
end
|
96
106
|
|
@@ -200,6 +210,7 @@ module ScoutApm
|
|
200
210
|
map { |child| child.total_call_time }.
|
201
211
|
inject(0) { |sum, time| sum + time }
|
202
212
|
end
|
213
|
+
private :child_time
|
203
214
|
|
204
215
|
######################################
|
205
216
|
# Allocation Calculations
|
@@ -225,5 +236,6 @@ module ScoutApm
|
|
225
236
|
map { |child| child.total_allocations }.
|
226
237
|
inject(0) { |sum, obj| sum + obj }
|
227
238
|
end
|
239
|
+
private :child_allocations
|
228
240
|
end
|
229
241
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
# A set of children records for any given Layer. This implements some
|
3
|
+
# rate-limiting logic.
|
4
|
+
#
|
5
|
+
# We store the first `unique_cutoff` count of each layer type. So if cutoff
|
6
|
+
# is 1000, we'd store 1000 HTTP layers, and 1000 ActiveRecord calls, and 1000
|
7
|
+
# of each other layer type. After that, make a LimitedLayer object and store
|
8
|
+
# only aggregate counts and times of future layers of that type. (So the
|
9
|
+
# 1001st an onward of ActiveRecord would get only aggregate times, and
|
10
|
+
# counts, without any detail about the SQL called)
|
11
|
+
#
|
12
|
+
# When the set of children is small, keep them unique
|
13
|
+
# When the set of children gets large enough, stop keeping details
|
14
|
+
#
|
15
|
+
# The next optimization, which is not yet implemented:
|
16
|
+
# when the set of children gets larger, attempt to merge them without data loss
|
17
|
+
class LayerChildrenSet
|
18
|
+
include Enumerable
|
19
|
+
|
20
|
+
# By default, how many unique children of a type do we store before
|
21
|
+
# flipping over to storing only aggregate info.
|
22
|
+
DEFAULT_UNIQUE_CUTOFF = 2000
|
23
|
+
attr_reader :unique_cutoff
|
24
|
+
|
25
|
+
# The Set of children objects
|
26
|
+
attr_reader :children
|
27
|
+
private :children
|
28
|
+
|
29
|
+
def initialize(unique_cutoff = DEFAULT_UNIQUE_CUTOFF)
|
30
|
+
@children = Hash.new { |hash, key| hash[key] = Set.new }
|
31
|
+
@limited_layers = nil # populated when needed
|
32
|
+
@unique_cutoff = unique_cutoff
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add a new layer into this set
|
36
|
+
# Only add completed layers - otherwise this will collect up incorrect info
|
37
|
+
# into the created LimitedLayer, since it will "freeze" any current data for
|
38
|
+
# total_call_time and similar methods.
|
39
|
+
def <<(child)
|
40
|
+
metric_type = child.type
|
41
|
+
set = children[metric_type]
|
42
|
+
|
43
|
+
if set.size >= unique_cutoff
|
44
|
+
# find limited_layer
|
45
|
+
@limited_layers || init_limited_layers
|
46
|
+
@limited_layers[metric_type].absorb(child)
|
47
|
+
else
|
48
|
+
# we have space just add it
|
49
|
+
set << child
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def each
|
54
|
+
children.each do |_type, set|
|
55
|
+
set.each do |child_layer|
|
56
|
+
yield child_layer
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if @limited_layers
|
61
|
+
@limited_layers.each do |_type, limited_layer|
|
62
|
+
yield limited_layer
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# hold off initializing this until we know we need it
|
68
|
+
def init_limited_layers
|
69
|
+
@limited_layers ||= Hash.new { |hash, key| hash[key] = LimitedLayer.new(key) }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -177,7 +177,7 @@ module ScoutApm
|
|
177
177
|
meta_options = make_meta_options(layer)
|
178
178
|
|
179
179
|
meta = MetricMeta.new(layer.legacy_metric_name, meta_options)
|
180
|
-
meta.extra.merge!(layer.annotations)
|
180
|
+
meta.extra.merge!(layer.annotations) if layer.annotations
|
181
181
|
|
182
182
|
store_backtrace(layer, meta)
|
183
183
|
|
@@ -187,13 +187,16 @@ module ScoutApm
|
|
187
187
|
# Timing
|
188
188
|
timing_stat = metric_hash[meta]
|
189
189
|
timing_stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
190
|
+
timing_stat.add_traces(layer.traces.as_json)
|
190
191
|
|
191
192
|
# Allocations
|
192
193
|
allocation_stat = allocation_metric_hash[meta]
|
193
194
|
allocation_stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
|
194
195
|
|
195
|
-
|
196
|
-
|
196
|
+
if LimitedLayer === layer
|
197
|
+
metric_hash[meta].call_count = layer.count
|
198
|
+
allocation_metric_hash[meta].call_count = layer.count
|
199
|
+
end
|
197
200
|
end
|
198
201
|
|
199
202
|
# Merged Metric - no specifics, just sum up by type (ActiveRecord, View, HTTP, etc)
|
@@ -221,7 +224,8 @@ module ScoutApm
|
|
221
224
|
# an example. We start capturing before we know if a query is cached
|
222
225
|
# or not, and want to skip any cached queries.
|
223
226
|
def skip_layer?(layer)
|
224
|
-
return
|
227
|
+
return false if layer.annotations.nil?
|
228
|
+
return true if layer.annotations[:ignorable]
|
225
229
|
end
|
226
230
|
|
227
231
|
# Debug logging for scoutprof traces
|
@@ -57,7 +57,7 @@ module ScoutApm
|
|
57
57
|
walker.walk do |layer|
|
58
58
|
next if layer == job_layer
|
59
59
|
next if layer == queue_layer
|
60
|
-
next if layer
|
60
|
+
next if skip_layer?(layer)
|
61
61
|
|
62
62
|
# we don't need to use the full metric name for scoped metrics as we
|
63
63
|
# only display metrics aggregrated by type, just use "ActiveRecord"
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
# A LimitedLayer is a lossy-compression approach to fall back on once we max out
|
3
|
+
# the number of detailed layer objects we store. See LayerChildrenSet for the
|
4
|
+
# logic on when that change over happens
|
5
|
+
#
|
6
|
+
# QUESTION: What do we do if we attempt to merge an item that has children?
|
7
|
+
class LimitedLayer
|
8
|
+
attr_reader :type
|
9
|
+
|
10
|
+
def initialize(type)
|
11
|
+
@type = type
|
12
|
+
|
13
|
+
@total_call_time = 0
|
14
|
+
@total_exclusive_time = 0
|
15
|
+
@total_allocations = 0
|
16
|
+
@total_exclusive_allocations = 0
|
17
|
+
@total_layers = 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def absorb(layer)
|
21
|
+
@total_layers += 1
|
22
|
+
|
23
|
+
@total_call_time += layer.total_call_time
|
24
|
+
@total_exclusive_time += layer.total_exclusive_time
|
25
|
+
|
26
|
+
@total_allocations += layer.total_allocations
|
27
|
+
@total_exclusive_allocations += layer.total_exclusive_allocations
|
28
|
+
end
|
29
|
+
|
30
|
+
def total_call_time
|
31
|
+
@total_call_time
|
32
|
+
end
|
33
|
+
|
34
|
+
def total_exclusive_time
|
35
|
+
@total_exclusive_time
|
36
|
+
end
|
37
|
+
|
38
|
+
def total_allocations
|
39
|
+
@total_allocations
|
40
|
+
end
|
41
|
+
|
42
|
+
def total_exclusive_allocations
|
43
|
+
@total_exclusive_allocations
|
44
|
+
end
|
45
|
+
|
46
|
+
def count
|
47
|
+
@total_layers
|
48
|
+
end
|
49
|
+
|
50
|
+
# This is the old style name. This function is used for now, but should be
|
51
|
+
# removed, and the new type & name split should be enforced through the
|
52
|
+
# app.
|
53
|
+
def legacy_metric_name
|
54
|
+
"#{type}/Limited"
|
55
|
+
end
|
56
|
+
|
57
|
+
def children
|
58
|
+
Set.new
|
59
|
+
end
|
60
|
+
|
61
|
+
def annotations
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
"<LimitedLayer type=#{type} count=#{count}>"
|
67
|
+
end
|
68
|
+
|
69
|
+
######################################################
|
70
|
+
# Stub out some methods with static default values #
|
71
|
+
######################################################
|
72
|
+
def subscopable?
|
73
|
+
false
|
74
|
+
end
|
75
|
+
|
76
|
+
def desc
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def backtrace
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_traces(*)
|
85
|
+
# noop
|
86
|
+
end
|
87
|
+
|
88
|
+
def traces
|
89
|
+
ScoutApm::TraceSet.new
|
90
|
+
end
|
91
|
+
|
92
|
+
#######################################################################
|
93
|
+
# Many methods don't make any sense on a limited layer. Raise errors #
|
94
|
+
# aggressively for now to detect mistaken calls #
|
95
|
+
#######################################################################
|
96
|
+
|
97
|
+
def add_child
|
98
|
+
raise "Should never call add_child on a limited_layer"
|
99
|
+
end
|
100
|
+
|
101
|
+
def record_stop_time!(*)
|
102
|
+
raise "Should never call record_stop_time! on a limited_layer"
|
103
|
+
end
|
104
|
+
|
105
|
+
def record_allocations!
|
106
|
+
raise "Should never call record_allocations! on a limited_layer"
|
107
|
+
end
|
108
|
+
|
109
|
+
def desc=(*)
|
110
|
+
raise "Should never call desc on a limited_layer"
|
111
|
+
end
|
112
|
+
|
113
|
+
def annotate_layer(*)
|
114
|
+
raise "Should never call annotate_layer on a limited_layer"
|
115
|
+
end
|
116
|
+
|
117
|
+
def subscopable!
|
118
|
+
raise "Should never call subscopable! on a limited_layer"
|
119
|
+
end
|
120
|
+
|
121
|
+
def capture_backtrace!
|
122
|
+
raise "Should never call capture_backtrace on a limited_layer"
|
123
|
+
end
|
124
|
+
|
125
|
+
def caller_array
|
126
|
+
raise "Should never call caller_array on a limited_layer"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -57,20 +57,21 @@ module ScoutApm
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def start_layer(layer)
|
60
|
-
if ignoring_children?
|
61
|
-
|
62
|
-
|
60
|
+
return if ignoring_children?
|
61
|
+
|
62
|
+
return ignoring_start_layer if ignoring_request?
|
63
63
|
|
64
64
|
layer.start_sampling
|
65
65
|
|
66
66
|
start_request(layer) unless @root_layer
|
67
|
-
@layers[-1].add_child(layer) if @layers.any?
|
68
67
|
@layers.push(layer)
|
69
68
|
end
|
70
69
|
|
71
70
|
def stop_layer
|
72
71
|
return if ignoring_children?
|
73
72
|
|
73
|
+
return ignoring_stop_layer if ignoring_request?
|
74
|
+
|
74
75
|
layer = @layers.pop
|
75
76
|
|
76
77
|
# Safeguard against a mismatch in the layer tracking in an instrument.
|
@@ -87,6 +88,8 @@ module ScoutApm
|
|
87
88
|
layer.record_stop_time!
|
88
89
|
layer.record_allocations!
|
89
90
|
|
91
|
+
@layers[-1].add_child(layer) if @layers.any?
|
92
|
+
|
90
93
|
# This must be called before checking if a backtrace should be collected as the call count influences our capture logic.
|
91
94
|
# We call `#update_call_counts in stop layer to ensure the layer has a final desc. Layer#desc is updated during the AR instrumentation flow.
|
92
95
|
update_call_counts!(layer)
|
@@ -115,6 +118,8 @@ module ScoutApm
|
|
115
118
|
|
116
119
|
BACKTRACE_BLACKLIST = ["Controller", "Job"]
|
117
120
|
def capture_backtrace?(layer)
|
121
|
+
return if ignoring_request?
|
122
|
+
|
118
123
|
# Never capture backtraces for this kind of layer. The backtrace will
|
119
124
|
# always be 100% framework code.
|
120
125
|
return false if BACKTRACE_BLACKLIST.include?(layer.type)
|
@@ -236,6 +241,8 @@ module ScoutApm
|
|
236
241
|
end
|
237
242
|
|
238
243
|
def instant?
|
244
|
+
return false if ignoring_request?
|
245
|
+
|
239
246
|
instant_key
|
240
247
|
end
|
241
248
|
|
@@ -248,6 +255,8 @@ module ScoutApm
|
|
248
255
|
def record!
|
249
256
|
@recorded = true
|
250
257
|
|
258
|
+
return if ignoring_request?
|
259
|
+
|
251
260
|
# Bail out early if the user asked us to ignore this uri
|
252
261
|
return if ScoutApm::Agent.instance.ignored_uris.ignore?(annotations[:uri])
|
253
262
|
|
@@ -297,6 +306,8 @@ module ScoutApm
|
|
297
306
|
|
298
307
|
# Only call this after the request is complete
|
299
308
|
def unique_name
|
309
|
+
return nil if ignoring_request?
|
310
|
+
|
300
311
|
@unique_name ||= begin
|
301
312
|
scope_layer = LayerConverters::ConverterBase.new(self).scope_layer
|
302
313
|
if scope_layer
|
@@ -311,6 +322,8 @@ module ScoutApm
|
|
311
322
|
# Used to know when we should just create a new one (don't attempt to add
|
312
323
|
# data to an already-recorded request). See RequestManager
|
313
324
|
def recorded?
|
325
|
+
return ignoring_recorded? if ignoring_request?
|
326
|
+
|
314
327
|
@recorded
|
315
328
|
end
|
316
329
|
|
@@ -359,5 +372,48 @@ module ScoutApm
|
|
359
372
|
def backtrace_threshold
|
360
373
|
dev_trace ? 0.05 : 0.5 # the minimum threshold in seconds to record the backtrace for a metric.
|
361
374
|
end
|
375
|
+
|
376
|
+
################################################################################
|
377
|
+
# Ignoring the rest of a request
|
378
|
+
################################################################################
|
379
|
+
|
380
|
+
# At any point in the request, calling code or instrumentation can call
|
381
|
+
# `ignore_request!` to immediately stop recording any information about new
|
382
|
+
# layers, and delete any existing layer info. This class will still exist,
|
383
|
+
# and respond to methods as normal, but `record!` won't be called, and no
|
384
|
+
# data will be recorded.
|
385
|
+
|
386
|
+
def ignore_request!
|
387
|
+
return if @ignoring_request
|
388
|
+
|
389
|
+
# Set instance variable
|
390
|
+
@ignoring_request = true
|
391
|
+
|
392
|
+
# Store data we'll need
|
393
|
+
@ignoring_depth = @layers.length
|
394
|
+
|
395
|
+
# Clear data
|
396
|
+
@layers = []
|
397
|
+
@root_layer = nil
|
398
|
+
@call_set = nil
|
399
|
+
@annotations = {}
|
400
|
+
@instant_key = nil
|
401
|
+
end
|
402
|
+
|
403
|
+
def ignoring_request?
|
404
|
+
@ignoring_request
|
405
|
+
end
|
406
|
+
|
407
|
+
def ignoring_start_layer
|
408
|
+
@ignoring_depth += 1
|
409
|
+
end
|
410
|
+
|
411
|
+
def ignoring_stop_layer
|
412
|
+
@ignoring_depth -= 1
|
413
|
+
end
|
414
|
+
|
415
|
+
def ignoring_recorded?
|
416
|
+
@ignoring_depth <= 0
|
417
|
+
end
|
362
418
|
end
|
363
419
|
end
|
data/lib/scout_apm/version.rb
CHANGED
data/lib/scout_apm.rb
CHANGED
@@ -28,6 +28,8 @@ require 'scout_apm/version'
|
|
28
28
|
|
29
29
|
require 'scout_apm/tracked_request'
|
30
30
|
require 'scout_apm/layer'
|
31
|
+
require 'scout_apm/limited_layer'
|
32
|
+
require 'scout_apm/layer_children_set'
|
31
33
|
require 'scout_apm/request_manager'
|
32
34
|
require 'scout_apm/call_set'
|
33
35
|
|
@@ -70,7 +72,6 @@ require 'scout_apm/instruments/mongoid'
|
|
70
72
|
require 'scout_apm/instruments/redis'
|
71
73
|
require 'scout_apm/instruments/influxdb'
|
72
74
|
require 'scout_apm/instruments/elasticsearch'
|
73
|
-
require 'scout_apm/instruments/delayed_job'
|
74
75
|
require 'scout_apm/instruments/active_record'
|
75
76
|
require 'scout_apm/instruments/action_controller_rails_2'
|
76
77
|
require 'scout_apm/instruments/action_controller_rails_3_rails4'
|
data/scout_apm.gemspec
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'scout_apm/instruments/net_http'
|
4
|
+
|
5
|
+
require 'addressable'
|
6
|
+
|
7
|
+
class NetHttpTest < Minitest::Test
|
8
|
+
def setup
|
9
|
+
ScoutApm::Instruments::NetHttp.new.install
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_request_scout_description_for_uri
|
13
|
+
req = Net::HTTP::Get.new(URI('http://example.org/here'))
|
14
|
+
assert_equal '/here', Net::HTTP.new('').request_scout_description(req)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_request_scout_description_for_addressable
|
18
|
+
req = Net::HTTP::Get.new(Addressable::URI.parse('http://example.org/here'))
|
19
|
+
assert_equal '/here', Net::HTTP.new('').request_scout_description(req)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'scout_apm/layer_children_set'
|
3
|
+
|
4
|
+
class LayerChildrenSetTest < Minitest::Test
|
5
|
+
SET = ScoutApm::LayerChildrenSet
|
6
|
+
|
7
|
+
def test_limit_default
|
8
|
+
assert_equal SET::DEFAULT_UNIQUE_CUTOFF, SET.new.unique_cutoff
|
9
|
+
end
|
10
|
+
|
11
|
+
# Add 5, make sure they're all in the children list we get back.
|
12
|
+
def test_add_layer_before_limit
|
13
|
+
s = SET.new(5)
|
14
|
+
|
15
|
+
5.times do
|
16
|
+
s << make_layer("LayerType", "LayerName")
|
17
|
+
end
|
18
|
+
|
19
|
+
children = s.to_a
|
20
|
+
assert_equal 5, children.size
|
21
|
+
|
22
|
+
# Don't care about order
|
23
|
+
(0..4).each do |i|
|
24
|
+
assert children.include?(lookup_layer(i))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_add_layer_after_limit
|
29
|
+
s = SET.new(5)
|
30
|
+
|
31
|
+
10.times do
|
32
|
+
s << make_layer("LayerType", "LayerName")
|
33
|
+
end
|
34
|
+
|
35
|
+
children = s.to_a
|
36
|
+
# 6 = 5 real ones + 1 merged.
|
37
|
+
assert_equal 6, children.size
|
38
|
+
|
39
|
+
# Don't care about order
|
40
|
+
(0..4).each do |i|
|
41
|
+
assert children.include?(lookup_layer(i))
|
42
|
+
end
|
43
|
+
|
44
|
+
# Don't care about order
|
45
|
+
(5..9).each do |i|
|
46
|
+
assert ! children.include?(lookup_layer(i))
|
47
|
+
end
|
48
|
+
|
49
|
+
limited_layer = children.last
|
50
|
+
assert_equal ScoutApm::LimitedLayer, limited_layer.class
|
51
|
+
assert_equal 5, limited_layer.count
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_add_layer_with_different_type_after_limit
|
55
|
+
s = SET.new(5)
|
56
|
+
|
57
|
+
# Add 20 items
|
58
|
+
10.times do
|
59
|
+
s << make_layer("LayerType", "LayerName")
|
60
|
+
s << make_layer("DifferentLayerType", "LayerName")
|
61
|
+
end
|
62
|
+
|
63
|
+
children = s.to_a
|
64
|
+
|
65
|
+
# Tyo types, so 2 distinct limitdlayer objects
|
66
|
+
limited_layers = children.select{ |l| ScoutApm::LimitedLayer === l }
|
67
|
+
assert_equal 2, limited_layers.length
|
68
|
+
|
69
|
+
# 5 unchanged children each for the two layer types, plus the 2 limitd when each overran their limit
|
70
|
+
assert_equal 12, children.length
|
71
|
+
limited_layers.each { |ml| assert_equal 5, ml.count }
|
72
|
+
end
|
73
|
+
|
74
|
+
#############
|
75
|
+
# Helpers #
|
76
|
+
#############
|
77
|
+
|
78
|
+
def make_layer(type, name)
|
79
|
+
@made_layers ||= []
|
80
|
+
l = ScoutApm::Layer.new(type, name)
|
81
|
+
@made_layers << l
|
82
|
+
l
|
83
|
+
end
|
84
|
+
|
85
|
+
def lookup_layer(i)
|
86
|
+
@made_layers[i]
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
class LimitedLayerTest < Minitest::Test
|
5
|
+
|
6
|
+
def test_counts_while_absorbing
|
7
|
+
ll = ScoutApm::LimitedLayer.new("ActiveRecord")
|
8
|
+
assert_equal 0, ll.count
|
9
|
+
|
10
|
+
ll.absorb faux_layer("ActiveRecord", "User#Find", 2, 1, 200, 100)
|
11
|
+
assert_equal 1, ll.count
|
12
|
+
|
13
|
+
ll.absorb faux_layer("ActiveRecord", "User#Find", 2, 1, 200, 100)
|
14
|
+
assert_equal 2, ll.count
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_sums_values_while_absorbing
|
18
|
+
ll = ScoutApm::LimitedLayer.new("ActiveRecord")
|
19
|
+
|
20
|
+
ll.absorb faux_layer("ActiveRecord", "User#Find", 2, 1, 200, 100)
|
21
|
+
assert_equal 1, ll.total_exclusive_time
|
22
|
+
assert_equal 2, ll.total_call_time
|
23
|
+
assert_equal 100, ll.total_exclusive_allocations
|
24
|
+
assert_equal 200, ll.total_allocations
|
25
|
+
|
26
|
+
|
27
|
+
ll.absorb faux_layer("ActiveRecord", "User#Find", 4, 3, 400, 300)
|
28
|
+
assert_equal 4, ll.total_exclusive_time # 3 + 1
|
29
|
+
assert_equal 6, ll.total_call_time # 4 + 2
|
30
|
+
assert_equal 400, ll.total_exclusive_allocations # 300 + 100
|
31
|
+
assert_equal 600, ll.total_allocations # 400 + 200
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_the_name
|
35
|
+
ll = ScoutApm::LimitedLayer.new("ActiveRecord")
|
36
|
+
assert_equal "ActiveRecord/Limited", ll.legacy_metric_name
|
37
|
+
end
|
38
|
+
|
39
|
+
#############
|
40
|
+
# Helpers #
|
41
|
+
#############
|
42
|
+
|
43
|
+
def faux_layer(type, name, tct, tet, a_tct, a_tet)
|
44
|
+
OpenStruct.new(
|
45
|
+
:type => type,
|
46
|
+
:name => name,
|
47
|
+
:total_call_time => tct,
|
48
|
+
:total_exclusive_time => tet,
|
49
|
+
:total_allocations => a_tct,
|
50
|
+
:total_exclusive_allocations => a_tet,
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.
|
4
|
+
version: 3.0.0.pre4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derek Haynes
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-11-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -95,6 +95,20 @@ dependencies:
|
|
95
95
|
- - ">="
|
96
96
|
- !ruby/object:Gem::Version
|
97
97
|
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: addressable
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
98
112
|
description: Monitors Ruby apps and reports detailed metrics on performance to Scout.
|
99
113
|
email:
|
100
114
|
- support@scoutapp.com
|
@@ -151,7 +165,6 @@ files:
|
|
151
165
|
- lib/scout_apm/instruments/action_controller_rails_2.rb
|
152
166
|
- lib/scout_apm/instruments/action_controller_rails_3_rails4.rb
|
153
167
|
- lib/scout_apm/instruments/active_record.rb
|
154
|
-
- lib/scout_apm/instruments/delayed_job.rb
|
155
168
|
- lib/scout_apm/instruments/elasticsearch.rb
|
156
169
|
- lib/scout_apm/instruments/grape.rb
|
157
170
|
- lib/scout_apm/instruments/http_client.rb
|
@@ -171,6 +184,7 @@ files:
|
|
171
184
|
- lib/scout_apm/layaway.rb
|
172
185
|
- lib/scout_apm/layaway_file.rb
|
173
186
|
- lib/scout_apm/layer.rb
|
187
|
+
- lib/scout_apm/layer_children_set.rb
|
174
188
|
- lib/scout_apm/layer_converters/allocation_metric_converter.rb
|
175
189
|
- lib/scout_apm/layer_converters/converter_base.rb
|
176
190
|
- lib/scout_apm/layer_converters/depth_first_walker.rb
|
@@ -180,6 +194,7 @@ files:
|
|
180
194
|
- lib/scout_apm/layer_converters/request_queue_time_converter.rb
|
181
195
|
- lib/scout_apm/layer_converters/slow_job_converter.rb
|
182
196
|
- lib/scout_apm/layer_converters/slow_request_converter.rb
|
197
|
+
- lib/scout_apm/limited_layer.rb
|
183
198
|
- lib/scout_apm/metric_meta.rb
|
184
199
|
- lib/scout_apm/metric_set.rb
|
185
200
|
- lib/scout_apm/metric_stats.rb
|
@@ -241,8 +256,11 @@ files:
|
|
241
256
|
- test/unit/histogram_test.rb
|
242
257
|
- test/unit/ignored_uris_test.rb
|
243
258
|
- test/unit/instruments/active_record_instruments_test.rb
|
259
|
+
- test/unit/instruments/net_http_test.rb
|
244
260
|
- test/unit/instruments/percentile_sampler_test.rb
|
245
261
|
- test/unit/layaway_test.rb
|
262
|
+
- test/unit/layer_children_set_test.rb
|
263
|
+
- test/unit/limited_layer_test.rb
|
246
264
|
- test/unit/metric_set_test.rb
|
247
265
|
- test/unit/scored_item_set_test.rb
|
248
266
|
- test/unit/serializers/payload_serializer_test.rb
|
@@ -291,8 +309,11 @@ test_files:
|
|
291
309
|
- test/unit/histogram_test.rb
|
292
310
|
- test/unit/ignored_uris_test.rb
|
293
311
|
- test/unit/instruments/active_record_instruments_test.rb
|
312
|
+
- test/unit/instruments/net_http_test.rb
|
294
313
|
- test/unit/instruments/percentile_sampler_test.rb
|
295
314
|
- test/unit/layaway_test.rb
|
315
|
+
- test/unit/layer_children_set_test.rb
|
316
|
+
- test/unit/limited_layer_test.rb
|
296
317
|
- test/unit/metric_set_test.rb
|
297
318
|
- test/unit/scored_item_set_test.rb
|
298
319
|
- test/unit/serializers/payload_serializer_test.rb
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module ScoutApm
|
2
|
-
module Instruments
|
3
|
-
class DelayedJob
|
4
|
-
attr_reader :logger
|
5
|
-
|
6
|
-
def initialize(logger=ScoutApm::Agent.instance.logger)
|
7
|
-
@logger = logger
|
8
|
-
@installed = false
|
9
|
-
end
|
10
|
-
|
11
|
-
def installed?
|
12
|
-
@installed
|
13
|
-
end
|
14
|
-
|
15
|
-
def install
|
16
|
-
@installed = true
|
17
|
-
if defined?(::Delayed::Worker)
|
18
|
-
::Delayed::Worker.class_eval do
|
19
|
-
include ScoutApm::Tracer
|
20
|
-
include ScoutApm::Instruments::DelayedJobInstruments
|
21
|
-
alias run_without_scout_instruments run
|
22
|
-
alias run run_with_scout_instruments
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
module DelayedJobInstruments
|
29
|
-
def run_with_scout_instruments(job)
|
30
|
-
scout_method_name = method_from_handler(job.handler)
|
31
|
-
queue = job.queue
|
32
|
-
latency = (Time.now.to_f - job.created_at.to_f) * 1000
|
33
|
-
|
34
|
-
ScoutApm::Agent.instance.store.track_one!("Queue", queue, 0, {:extra_metrics => {:latency => latency}})
|
35
|
-
req = ScoutApm::RequestManager.lookup
|
36
|
-
req.job!
|
37
|
-
req.start_layer( ScoutApm::Layer.new("Job", scout_method_name) )
|
38
|
-
|
39
|
-
begin
|
40
|
-
run_without_scout_instruments(job)
|
41
|
-
rescue
|
42
|
-
req.error!
|
43
|
-
raise
|
44
|
-
ensure
|
45
|
-
req.stop_layer
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def method_from_handler(handler)
|
50
|
-
job_handler = YAML.load(handler)
|
51
|
-
klass = job_handler.object.name
|
52
|
-
method = job_handler.method_name
|
53
|
-
"#{klass}##{method}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|