scout_apm 2.2.0.pre3 → 2.3.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/CHANGELOG.markdown +147 -2
- data/Guardfile +43 -0
- data/Rakefile +2 -2
- data/ext/allocations/allocations.c +6 -0
- data/ext/allocations/extconf.rb +1 -0
- data/ext/rusage/README.md +26 -0
- data/ext/rusage/extconf.rb +5 -0
- data/ext/rusage/rusage.c +52 -0
- data/lib/scout_apm.rb +28 -15
- data/lib/scout_apm/agent.rb +89 -37
- data/lib/scout_apm/agent/logging.rb +6 -1
- data/lib/scout_apm/agent/reporting.rb +9 -6
- data/lib/scout_apm/app_server_load.rb +21 -10
- data/lib/scout_apm/attribute_arranger.rb +6 -3
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +71 -1
- data/lib/scout_apm/background_job_integrations/resque.rb +85 -0
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +22 -20
- data/lib/scout_apm/background_recorder.rb +43 -0
- data/lib/scout_apm/background_worker.rb +19 -15
- data/lib/scout_apm/config.rb +138 -28
- data/lib/scout_apm/db_query_metric_set.rb +80 -0
- data/lib/scout_apm/db_query_metric_stats.rb +102 -0
- data/lib/scout_apm/debug.rb +37 -0
- data/lib/scout_apm/environment.rb +22 -15
- data/lib/scout_apm/git_revision.rb +51 -0
- data/lib/scout_apm/histogram.rb +11 -2
- data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
- data/lib/scout_apm/instant/middleware.rb +196 -54
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +89 -68
- data/lib/scout_apm/instruments/action_view.rb +49 -0
- data/lib/scout_apm/instruments/active_record.rb +127 -3
- data/lib/scout_apm/instruments/grape.rb +4 -3
- data/lib/scout_apm/instruments/middleware_detailed.rb +4 -6
- data/lib/scout_apm/instruments/mongoid.rb +24 -3
- data/lib/scout_apm/instruments/net_http.rb +7 -2
- data/lib/scout_apm/instruments/percentile_sampler.rb +36 -19
- data/lib/scout_apm/instruments/process/process_cpu.rb +3 -2
- data/lib/scout_apm/instruments/process/process_memory.rb +3 -3
- data/lib/scout_apm/instruments/resque.rb +40 -0
- data/lib/scout_apm/layaway.rb +67 -28
- data/lib/scout_apm/layer.rb +19 -59
- data/lib/scout_apm/layer_children_set.rb +77 -0
- data/lib/scout_apm/layer_converters/allocation_metric_converter.rb +5 -6
- data/lib/scout_apm/layer_converters/converter_base.rb +201 -14
- data/lib/scout_apm/layer_converters/database_converter.rb +55 -0
- data/lib/scout_apm/layer_converters/depth_first_walker.rb +22 -10
- data/lib/scout_apm/layer_converters/error_converter.rb +5 -7
- data/lib/scout_apm/layer_converters/find_layer_by_type.rb +34 -0
- data/lib/scout_apm/layer_converters/histograms.rb +14 -0
- data/lib/scout_apm/layer_converters/job_converter.rb +36 -50
- data/lib/scout_apm/layer_converters/metric_converter.rb +17 -19
- data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +10 -12
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +41 -115
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +33 -117
- data/lib/scout_apm/limited_layer.rb +126 -0
- data/lib/scout_apm/metric_meta.rb +0 -5
- data/lib/scout_apm/metric_set.rb +9 -1
- data/lib/scout_apm/metric_stats.rb +7 -8
- data/lib/scout_apm/rack.rb +26 -0
- data/lib/scout_apm/remote/message.rb +23 -0
- data/lib/scout_apm/remote/recorder.rb +57 -0
- data/lib/scout_apm/remote/router.rb +49 -0
- data/lib/scout_apm/remote/server.rb +58 -0
- data/lib/scout_apm/reporter.rb +51 -15
- data/lib/scout_apm/request_histograms.rb +4 -0
- data/lib/scout_apm/request_manager.rb +2 -1
- data/lib/scout_apm/scored_item_set.rb +7 -0
- data/lib/scout_apm/serializers/db_query_serializer_to_json.rb +15 -0
- data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +21 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +10 -3
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +6 -6
- data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +2 -1
- data/lib/scout_apm/server_integrations/puma.rb +5 -2
- data/lib/scout_apm/slow_job_policy.rb +1 -10
- data/lib/scout_apm/slow_job_record.rb +6 -1
- data/lib/scout_apm/slow_request_policy.rb +1 -10
- data/lib/scout_apm/slow_transaction.rb +20 -2
- data/lib/scout_apm/store.rb +66 -12
- data/lib/scout_apm/synchronous_recorder.rb +26 -0
- data/lib/scout_apm/tracked_request.rb +136 -71
- data/lib/scout_apm/utils/active_record_metric_name.rb +8 -4
- data/lib/scout_apm/utils/backtrace_parser.rb +3 -3
- data/lib/scout_apm/utils/gzip_helper.rb +24 -0
- data/lib/scout_apm/utils/numbers.rb +14 -0
- data/lib/scout_apm/utils/scm.rb +14 -0
- data/lib/scout_apm/version.rb +1 -1
- data/scout_apm.gemspec +5 -4
- data/test/test_helper.rb +18 -0
- data/test/unit/config_test.rb +59 -8
- data/test/unit/db_query_metric_set_test.rb +56 -0
- data/test/unit/db_query_metric_stats_test.rb +113 -0
- data/test/unit/git_revision_test.rb +15 -0
- data/test/unit/histogram_test.rb +14 -0
- data/test/unit/instruments/net_http_test.rb +21 -0
- data/test/unit/instruments/percentile_sampler_test.rb +137 -0
- data/test/unit/layaway_test.rb +20 -0
- data/test/unit/layer_children_set_test.rb +88 -0
- data/test/unit/layer_converters/depth_first_walker_test.rb +66 -0
- data/test/unit/layer_converters/metric_converter_test.rb +22 -0
- data/test/unit/layer_converters/stubs.rb +33 -0
- data/test/unit/limited_layer_test.rb +53 -0
- data/test/unit/remote/test_message.rb +13 -0
- data/test/unit/remote/test_router.rb +33 -0
- data/test/unit/remote/test_server.rb +15 -0
- data/test/unit/serializers/payload_serializer_test.rb +3 -12
- data/test/unit/store_test.rb +66 -0
- data/test/unit/test_tracked_request.rb +87 -0
- data/test/unit/utils/active_record_metric_name_test.rb +8 -0
- data/test/unit/utils/backtrace_parser_test.rb +5 -0
- data/test/unit/utils/numbers_test.rb +15 -0
- data/test/unit/utils/scm.rb +17 -0
- metadata +125 -30
- data/ext/stacks/extconf.rb +0 -37
- data/ext/stacks/scout_atomics.h +0 -86
- data/ext/stacks/stacks.c +0 -811
- data/lib/scout_apm/capacity.rb +0 -57
- data/lib/scout_apm/deploy_integrations/capistrano_2.cap +0 -12
- data/lib/scout_apm/deploy_integrations/capistrano_2.rb +0 -83
- data/lib/scout_apm/deploy_integrations/capistrano_3.cap +0 -12
- data/lib/scout_apm/deploy_integrations/capistrano_3.rb +0 -88
- data/lib/scout_apm/instruments/delayed_job.rb +0 -57
- data/lib/scout_apm/serializers/deploy_serializer.rb +0 -16
- data/lib/scout_apm/trace_compactor.rb +0 -312
- data/lib/scout_apm/utils/fake_stacks.rb +0 -87
- data/tester.rb +0 -53
@@ -0,0 +1,77 @@
|
|
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
|
31
|
+
@limited_layers = nil # populated when needed
|
32
|
+
@unique_cutoff = unique_cutoff
|
33
|
+
end
|
34
|
+
|
35
|
+
def child_set(metric_type)
|
36
|
+
children[metric_type] = Set.new if !children.has_key?(metric_type)
|
37
|
+
children[metric_type]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Add a new layer into this set
|
41
|
+
# Only add completed layers - otherwise this will collect up incorrect info
|
42
|
+
# into the created LimitedLayer, since it will "freeze" any current data for
|
43
|
+
# total_call_time and similar methods.
|
44
|
+
def <<(child)
|
45
|
+
metric_type = child.type
|
46
|
+
set = child_set(metric_type)
|
47
|
+
|
48
|
+
if set.size >= unique_cutoff
|
49
|
+
# find limited_layer
|
50
|
+
@limited_layers || init_limited_layers
|
51
|
+
@limited_layers[metric_type].absorb(child)
|
52
|
+
else
|
53
|
+
# we have space just add it
|
54
|
+
set << child
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def each
|
59
|
+
children.each do |_type, set|
|
60
|
+
set.each do |child_layer|
|
61
|
+
yield child_layer
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
if @limited_layers
|
66
|
+
@limited_layers.each do |_type, limited_layer|
|
67
|
+
yield limited_layer
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# hold off initializing this until we know we need it
|
73
|
+
def init_limited_layers
|
74
|
+
@limited_layers ||= Hash.new { |hash, key| hash[key] = LimitedLayer.new(key) }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -1,16 +1,15 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
module LayerConverters
|
3
3
|
class AllocationMetricConverter < ConverterBase
|
4
|
-
def
|
5
|
-
|
6
|
-
return
|
7
|
-
return {} unless ScoutApm::Instruments::Allocations::ENABLED
|
4
|
+
def record!
|
5
|
+
return unless scope_layer
|
6
|
+
return unless ScoutApm::Instruments::Allocations::ENABLED
|
8
7
|
|
9
|
-
meta = MetricMeta.new("ObjectAllocations", {:scope =>
|
8
|
+
meta = MetricMeta.new("ObjectAllocations", {:scope => scope_layer.legacy_metric_name})
|
10
9
|
stat = MetricStats.new
|
11
10
|
stat.update!(root_layer.total_allocations)
|
12
11
|
|
13
|
-
{ meta => stat }
|
12
|
+
@store.track!({ meta => stat })
|
14
13
|
end
|
15
14
|
end
|
16
15
|
end
|
@@ -1,31 +1,218 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
module LayerConverters
|
3
3
|
class ConverterBase
|
4
|
-
|
4
|
+
|
5
5
|
attr_reader :request
|
6
6
|
attr_reader :root_layer
|
7
|
+
attr_reader :layer_finder
|
7
8
|
|
8
|
-
def initialize(request)
|
9
|
+
def initialize(request, layer_finder, store=nil)
|
9
10
|
@request = request
|
11
|
+
@layer_finder = layer_finder
|
12
|
+
@store = store
|
13
|
+
|
10
14
|
@root_layer = request.root_layer
|
11
|
-
@
|
15
|
+
@backtraces = []
|
16
|
+
@limited = false
|
12
17
|
end
|
13
18
|
|
14
|
-
# Scope is determined by the first Controller we hit. Most of the time
|
15
|
-
# there will only be 1 anyway. But if you have a controller that calls
|
16
|
-
# another controller method, we may pick that up:
|
17
|
-
# def update
|
18
|
-
# show
|
19
|
-
# render :update
|
20
|
-
# end
|
21
19
|
def scope_layer
|
22
|
-
|
20
|
+
layer_finder.scope
|
21
|
+
end
|
22
|
+
|
23
|
+
################################################################################
|
24
|
+
# Subscoping
|
25
|
+
################################################################################
|
26
|
+
#
|
27
|
+
# Keep a list of subscopes, but only ever use the front one. The rest
|
28
|
+
# get pushed/popped in cases when we have many levels of subscopable
|
29
|
+
# layers. This lets us push/pop without otherwise keeping track very closely.
|
30
|
+
def register_hooks(walker)
|
31
|
+
@subscope_layers = []
|
32
|
+
|
33
|
+
walker.before do |layer|
|
34
|
+
if layer.subscopable?
|
35
|
+
@subscope_layers.push(layer)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
walker.after do |layer|
|
40
|
+
if layer.subscopable?
|
41
|
+
@subscope_layers.pop
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def subscoped?(layer)
|
47
|
+
@subscope_layers.first && layer != @subscope_layers.first # Don't scope under ourself.
|
48
|
+
end
|
49
|
+
|
50
|
+
def subscope_name
|
51
|
+
@subscope_layers.first.legacy_metric_name
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
################################################################################
|
56
|
+
# Backtrace Handling
|
57
|
+
################################################################################
|
58
|
+
#
|
59
|
+
# Because we get several layers for the same thing if you call an
|
60
|
+
# instrumented thing repeatedly, and only some of them may have
|
61
|
+
# backtraces captured, we store the backtraces off into another spot
|
62
|
+
# during processing, then at the end, we loop over those saved
|
63
|
+
# backtraces, putting them back into the metrics hash.
|
64
|
+
#
|
65
|
+
# This comes up most often when capturing n+1 backtraces. Because the
|
66
|
+
# query may be fast enough to evade our time-limit based backtrace
|
67
|
+
# capture, only the Nth item (see TrackedRequest for more detail) has a
|
68
|
+
# backtrack captured. This sequence makes sure that we report up that
|
69
|
+
# backtrace in the aggregated set of metrics around that call.
|
70
|
+
|
71
|
+
# Call this as you are processing each layer. It will store off backtraces
|
72
|
+
def store_backtrace(layer, meta)
|
73
|
+
return unless layer.backtrace
|
74
|
+
|
75
|
+
bt = ScoutApm::Utils::BacktraceParser.new(layer.backtrace).call
|
76
|
+
if bt.any?
|
77
|
+
meta.backtrace = bt
|
78
|
+
@backtraces << meta
|
79
|
+
end
|
23
80
|
end
|
24
81
|
|
25
|
-
|
26
|
-
|
27
|
-
|
82
|
+
# Call this after you finish walking the layers, and want to take the
|
83
|
+
# set-aside backtraces and place them into the metas they match
|
84
|
+
def attach_backtraces(metric_hash)
|
85
|
+
@backtraces.each do |meta_with_backtrace|
|
86
|
+
metric_hash.keys.find { |k| k == meta_with_backtrace }.backtrace = meta_with_backtrace.backtrace
|
28
87
|
end
|
88
|
+
metric_hash
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
################################################################################
|
93
|
+
# Limit Handling
|
94
|
+
################################################################################
|
95
|
+
|
96
|
+
# To prevent huge traces from being generated, we should stop collecting
|
97
|
+
# detailed metrics as we go beyond some reasonably large count.
|
98
|
+
#
|
99
|
+
# We should still add up the /all aggregates.
|
100
|
+
|
101
|
+
MAX_METRICS = 500
|
102
|
+
|
103
|
+
def over_metric_limit?(metric_hash)
|
104
|
+
if metric_hash.size > MAX_METRICS
|
105
|
+
@limited = true
|
106
|
+
else
|
107
|
+
false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def limited?
|
112
|
+
!! @limited
|
113
|
+
end
|
114
|
+
|
115
|
+
################################################################################
|
116
|
+
# Meta Scope
|
117
|
+
################################################################################
|
118
|
+
|
119
|
+
# When we make MetricMeta records, we need to determine a few things from layer.
|
120
|
+
def make_meta_options(layer)
|
121
|
+
scope_hash = make_meta_options_scope(layer)
|
122
|
+
desc_hash = make_meta_options_desc_hash(layer)
|
123
|
+
|
124
|
+
scope_hash.merge(desc_hash)
|
125
|
+
end
|
126
|
+
|
127
|
+
def make_meta_options_scope(layer)
|
128
|
+
# This layer is scoped under another thing. Typically that means this is a layer under a view.
|
129
|
+
# Like: Controller -> View/users/show -> ActiveRecord/user/find
|
130
|
+
# in that example, the scope is the View/users/show
|
131
|
+
if subscoped?(layer)
|
132
|
+
{:scope => subscope_name}
|
133
|
+
|
134
|
+
# We don't scope the controller under itself
|
135
|
+
elsif layer == scope_layer
|
136
|
+
{}
|
137
|
+
|
138
|
+
# This layer is a top level metric ("ActiveRecord", or "HTTP" or
|
139
|
+
# whatever, directly under the controller), so scope to the
|
140
|
+
# Controller
|
141
|
+
else
|
142
|
+
{:scope => scope_layer.legacy_metric_name}
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def make_meta_options_desc_hash(layer, max_desc_length=1000)
|
147
|
+
if layer.desc
|
148
|
+
desc_s = layer.desc.to_s
|
149
|
+
trimmed_desc = desc_s[0 .. max_desc_length]
|
150
|
+
{:desc => trimmed_desc}
|
151
|
+
else
|
152
|
+
{}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
################################################################################
|
158
|
+
# Storing metrics into the hashes
|
159
|
+
################################################################################
|
160
|
+
|
161
|
+
# This is the detailed metric - type, name, backtrace, annotations, etc.
|
162
|
+
def store_specific_metric(layer, metric_hash, allocation_metric_hash)
|
163
|
+
return false if over_metric_limit?(metric_hash)
|
164
|
+
|
165
|
+
meta_options = make_meta_options(layer)
|
166
|
+
|
167
|
+
meta = MetricMeta.new(layer.legacy_metric_name, meta_options)
|
168
|
+
meta.extra.merge!(layer.annotations) if layer.annotations
|
169
|
+
|
170
|
+
store_backtrace(layer, meta)
|
171
|
+
|
172
|
+
metric_hash[meta] ||= MetricStats.new(meta_options.has_key?(:scope))
|
173
|
+
allocation_metric_hash[meta] ||= MetricStats.new(meta_options.has_key?(:scope))
|
174
|
+
|
175
|
+
# timing
|
176
|
+
stat = metric_hash[meta]
|
177
|
+
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
178
|
+
|
179
|
+
# allocations
|
180
|
+
stat = allocation_metric_hash[meta]
|
181
|
+
stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
|
182
|
+
|
183
|
+
if LimitedLayer === layer
|
184
|
+
metric_hash[meta].call_count = layer.count
|
185
|
+
allocation_metric_hash[meta].call_count = layer.count
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Merged Metric - no specifics, just sum up by type (ActiveRecord, View, HTTP, etc)
|
190
|
+
def store_aggregate_metric(layer, metric_hash, allocation_metric_hash)
|
191
|
+
meta = MetricMeta.new("#{layer.type}/all")
|
192
|
+
|
193
|
+
metric_hash[meta] ||= MetricStats.new(false)
|
194
|
+
allocation_metric_hash[meta] ||= MetricStats.new(false)
|
195
|
+
|
196
|
+
# timing
|
197
|
+
stat = metric_hash[meta]
|
198
|
+
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
199
|
+
|
200
|
+
# allocations
|
201
|
+
stat = allocation_metric_hash[meta]
|
202
|
+
stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
|
203
|
+
end
|
204
|
+
|
205
|
+
################################################################################
|
206
|
+
# Misc Helpers
|
207
|
+
################################################################################
|
208
|
+
|
209
|
+
# Sometimes we start capturing a layer without knowing if we really
|
210
|
+
# want to make an entry for it. See ActiveRecord instrumentation for
|
211
|
+
# an example. We start capturing before we know if a query is cached
|
212
|
+
# or not, and want to skip any cached queries.
|
213
|
+
def skip_layer?(layer)
|
214
|
+
return false if layer.annotations.nil?
|
215
|
+
return true if layer.annotations[:ignorable]
|
29
216
|
end
|
30
217
|
end
|
31
218
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module LayerConverters
|
3
|
+
class DatabaseConverter < ConverterBase
|
4
|
+
def initialize(*)
|
5
|
+
super
|
6
|
+
@db_query_metric_set = DbQueryMetricSet.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def register_hooks(walker)
|
10
|
+
super
|
11
|
+
|
12
|
+
return unless scope_layer
|
13
|
+
|
14
|
+
walker.on do |layer|
|
15
|
+
next if skip_layer?(layer)
|
16
|
+
|
17
|
+
stat = DbQueryMetricStats.new(
|
18
|
+
layer.name.model,
|
19
|
+
layer.name.normalized_operation,
|
20
|
+
scope_layer.legacy_metric_name, # controller_scope
|
21
|
+
1, # count, this is a single query, so 1
|
22
|
+
layer.total_call_time,
|
23
|
+
records_returned(layer)
|
24
|
+
)
|
25
|
+
@db_query_metric_set << stat
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def skip_layer?(layer)
|
30
|
+
layer.type != 'ActiveRecord' ||
|
31
|
+
layer.limited? ||
|
32
|
+
( ! layer.name.respond_to?(:model)) ||
|
33
|
+
( ! layer.name.respond_to?(:normalized_operation)) ||
|
34
|
+
layer.name.model.nil? ||
|
35
|
+
layer.name.normalized_operation.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
def record!
|
39
|
+
# Everything in the metric set here is from a single transaction, which
|
40
|
+
# we want to keep track of. (One web call did a User#find 10 times, but
|
41
|
+
# only due to 1 http request)
|
42
|
+
@db_query_metric_set.increment_transaction_count!
|
43
|
+
@store.track_db_query_metrics!(@db_query_metric_set)
|
44
|
+
end
|
45
|
+
|
46
|
+
def records_returned(layer)
|
47
|
+
if layer.annotations
|
48
|
+
layer.annotations.fetch(:record_count, 0)
|
49
|
+
else
|
50
|
+
0
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -5,30 +5,42 @@ module ScoutApm
|
|
5
5
|
|
6
6
|
def initialize(root_layer)
|
7
7
|
@root_layer = root_layer
|
8
|
+
|
9
|
+
@on_blocks = []
|
10
|
+
@before_blocks = []
|
11
|
+
@after_blocks = []
|
8
12
|
end
|
9
13
|
|
10
14
|
def before(&block)
|
11
|
-
@
|
15
|
+
@before_blocks << block
|
12
16
|
end
|
13
17
|
|
14
18
|
def after(&block)
|
15
|
-
@
|
19
|
+
@after_blocks << block
|
20
|
+
end
|
21
|
+
|
22
|
+
def on(&block)
|
23
|
+
@on_blocks << block
|
16
24
|
end
|
17
25
|
|
18
|
-
def walk(layer=root_layer
|
26
|
+
def walk(layer=root_layer)
|
19
27
|
# Need to run this for the root layer the first time through.
|
20
28
|
if layer == root_layer
|
21
|
-
@
|
22
|
-
|
23
|
-
@after_block.call(layer) if @after_block
|
29
|
+
@before_blocks.each{|b| b.call(layer) }
|
30
|
+
@on_blocks.each{|b| b.call(layer) }
|
24
31
|
end
|
25
32
|
|
26
33
|
layer.children.each do |child|
|
27
|
-
@
|
28
|
-
|
29
|
-
walk(child
|
30
|
-
@
|
34
|
+
@before_blocks.each{|b| b.call(child) }
|
35
|
+
@on_blocks.each{|b| b.call(child) }
|
36
|
+
walk(child)
|
37
|
+
@after_blocks.each{|b| b.call(child) }
|
31
38
|
end
|
39
|
+
|
40
|
+
if layer == root_layer
|
41
|
+
@after_blocks.each{|b| b.call(layer) }
|
42
|
+
end
|
43
|
+
|
32
44
|
nil
|
33
45
|
end
|
34
46
|
end
|
@@ -1,19 +1,17 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
module LayerConverters
|
3
3
|
class ErrorConverter < ConverterBase
|
4
|
-
def
|
5
|
-
scope = scope_layer
|
6
|
-
|
4
|
+
def record!
|
7
5
|
# Should we mark a request as errored out if a middleware raises?
|
8
6
|
# How does that interact w/ a tool like Sentry or Honeybadger?
|
9
|
-
return
|
10
|
-
return
|
7
|
+
return unless scope_layer
|
8
|
+
return unless request.error?
|
11
9
|
|
12
|
-
meta = MetricMeta.new("Errors/#{
|
10
|
+
meta = MetricMeta.new("Errors/#{scope_layer.legacy_metric_name}", {})
|
13
11
|
stat = MetricStats.new
|
14
12
|
stat.update!(1)
|
15
13
|
|
16
|
-
{ meta => stat }
|
14
|
+
@store.track!({ meta => stat })
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|