scout_apm 3.0.0.pre3 → 3.0.0.pre4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|