scout_apm 1.1.0.pre1 → 1.2.0.pre1
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 +6 -0
- data/lib/scout_apm/agent/reporting.rb +67 -77
- data/lib/scout_apm/agent.rb +56 -9
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +19 -0
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +60 -0
- data/lib/scout_apm/bucket_name_splitter.rb +2 -2
- data/lib/scout_apm/capacity.rb +2 -1
- data/lib/scout_apm/context.rb +1 -5
- data/lib/scout_apm/environment.rb +16 -1
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +13 -3
- data/lib/scout_apm/instruments/action_controller_rails_3.rb +20 -20
- data/lib/scout_apm/instruments/active_record.rb +5 -8
- data/lib/scout_apm/instruments/delayed_job.rb +56 -0
- data/lib/scout_apm/instruments/middleware.rb +44 -0
- data/lib/scout_apm/instruments/mongoid.rb +1 -1
- data/lib/scout_apm/instruments/moped.rb +2 -2
- data/lib/scout_apm/instruments/net_http.rb +1 -2
- data/lib/scout_apm/instruments/process/process_cpu.rb +5 -1
- data/lib/scout_apm/instruments/process/process_memory.rb +5 -1
- data/lib/scout_apm/instruments/sinatra.rb +14 -2
- data/lib/scout_apm/layaway.rb +33 -79
- data/lib/scout_apm/layaway_file.rb +2 -1
- data/lib/scout_apm/layer.rb +115 -0
- data/lib/scout_apm/layer_converter.rb +196 -0
- data/lib/scout_apm/metric_meta.rb +24 -4
- data/lib/scout_apm/metric_stats.rb +14 -4
- data/lib/scout_apm/request_manager.rb +26 -0
- data/lib/scout_apm/request_queue_time.rb +54 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +8 -1
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +1 -0
- data/lib/scout_apm/slow_transaction.rb +3 -0
- data/lib/scout_apm/store.rb +122 -190
- data/lib/scout_apm/tracer.rb +54 -83
- data/lib/scout_apm/tracked_request.rb +168 -0
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +18 -5
- metadata +11 -2
@@ -1,11 +1,14 @@
|
|
1
1
|
# Stats that are associated with each instrumented method.
|
2
|
-
|
2
|
+
module ScoutApm
|
3
|
+
class MetricStats
|
3
4
|
attr_accessor :call_count
|
4
5
|
attr_accessor :min_call_time
|
5
6
|
attr_accessor :max_call_time
|
6
7
|
attr_accessor :total_call_time
|
7
8
|
attr_accessor :total_exclusive_time
|
8
9
|
attr_accessor :sum_of_squares
|
10
|
+
attr_accessor :queue
|
11
|
+
attr_accessor :latency
|
9
12
|
|
10
13
|
def initialize(scoped = false)
|
11
14
|
@scoped = scoped
|
@@ -17,16 +20,22 @@ class ScoutApm::MetricStats
|
|
17
20
|
self.sum_of_squares = 0.0
|
18
21
|
end
|
19
22
|
|
20
|
-
|
23
|
+
# Note, that you must include exclusive_time if you wish to set
|
24
|
+
# extra_metrics. A two argument use of this method won't do that.
|
25
|
+
def update!(call_time, exclusive_time=call_time, extra_metrics={})
|
21
26
|
# If this metric is scoped inside another, use exclusive time for min/max and sum_of_squares. Non-scoped metrics
|
22
27
|
# (like controller actions) track the total call time.
|
23
28
|
t = (@scoped ? exclusive_time : call_time)
|
24
29
|
self.min_call_time = t if self.call_count == 0 or t < min_call_time
|
25
30
|
self.max_call_time = t if self.call_count == 0 or t > max_call_time
|
26
|
-
self.call_count +=1
|
31
|
+
self.call_count += 1
|
27
32
|
self.total_call_time += call_time
|
28
33
|
self.total_exclusive_time += exclusive_time
|
29
34
|
self.sum_of_squares += (t * t)
|
35
|
+
if extra_metrics
|
36
|
+
self.queue = extra_metrics[:queue] if extra_metrics[:queue]
|
37
|
+
self.latency = extra_metrics[:latency] if extra_metrics[:latency]
|
38
|
+
end
|
30
39
|
self
|
31
40
|
end
|
32
41
|
|
@@ -52,4 +61,5 @@ class ScoutApm::MetricStats
|
|
52
61
|
# uri, context
|
53
62
|
ScoutApm::AttributeArranger.call(self, json_attributes)
|
54
63
|
end
|
55
|
-
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Request manager handles the threadlocal variable that holds the current
|
2
|
+
# request. If there isn't one, then create one
|
3
|
+
|
4
|
+
module ScoutApm
|
5
|
+
class RequestManager
|
6
|
+
def self.lookup
|
7
|
+
find || create
|
8
|
+
end
|
9
|
+
|
10
|
+
# Get the current Thread local, and detecting, and not returning a stale request
|
11
|
+
def self.find
|
12
|
+
req = Thread.current[:scout_request]
|
13
|
+
|
14
|
+
if req && req.recorded?
|
15
|
+
nil
|
16
|
+
else
|
17
|
+
req
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Create a new TrackedRequest object for this thread
|
22
|
+
def self.create
|
23
|
+
Thread.current[:scout_request] = TrackedRequest.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
class RequestQueueTime < LayerConverterBase
|
3
|
+
HEADERS = %w(X-Queue-Start X-Request-Start X-QUEUE-START X-REQUEST-START x-queue-start x-request-start)
|
4
|
+
|
5
|
+
# Headers is a hash of request headers. In Rails, request.headers would be appropriate
|
6
|
+
def initialize(request)
|
7
|
+
super(request)
|
8
|
+
@headers = request.headers
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
return {} unless headers
|
13
|
+
|
14
|
+
raw_start = locate_timestamp
|
15
|
+
return {} unless raw_start
|
16
|
+
|
17
|
+
parsed_start = parse(raw_start)
|
18
|
+
return {} unless parsed_start
|
19
|
+
|
20
|
+
request_start = root_layer.start_time
|
21
|
+
queue_time = (request_start - parsed_start).to_f
|
22
|
+
|
23
|
+
meta = MetricMeta.new("QueueTime/Request", {:scope => scope_layer.legacy_metric_name})
|
24
|
+
stat = MetricStats.new(true)
|
25
|
+
stat.update!(queue_time)
|
26
|
+
|
27
|
+
{ meta => stat }
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :headers
|
33
|
+
|
34
|
+
# Looks through the possible headers with this data, and extracts the raw
|
35
|
+
# value of the header
|
36
|
+
# Returns nil if not found
|
37
|
+
def locate_timestamp
|
38
|
+
return nil unless headers
|
39
|
+
|
40
|
+
header = HEADERS.find { |candidate| headers[candidate] }
|
41
|
+
if header
|
42
|
+
data = headers[header]
|
43
|
+
data.to_s.gsub(/(t=|\.)/, '')
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a timestamp in fractional seconds since epoch
|
50
|
+
def parse(time_string)
|
51
|
+
Time.at("#{time_string[0,10]}.#{time_string[10,13]}".to_f)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -6,7 +6,14 @@ module ScoutApm
|
|
6
6
|
if ScoutApm::Agent.instance.config.value("report_format") == 'json'
|
7
7
|
ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, metrics, slow_transactions)
|
8
8
|
else
|
9
|
-
|
9
|
+
metadata = metadata.dup
|
10
|
+
metadata.default_proc = nil
|
11
|
+
|
12
|
+
metrics = metrics.dup
|
13
|
+
metrics.default_proc = nil
|
14
|
+
Marshal.dump(:metadata => metadata,
|
15
|
+
:metrics => metrics,
|
16
|
+
:slow_transactions => slow_transactions)
|
10
17
|
end
|
11
18
|
end
|
12
19
|
|
@@ -5,6 +5,7 @@ module ScoutApm
|
|
5
5
|
def serialize(metadata, metrics, slow_transactions)
|
6
6
|
rearranged_metrics = rearrange_the_metrics(metrics)
|
7
7
|
rearranged_slow_transactions = rearrange_the_slow_transactions(slow_transactions)
|
8
|
+
metadata.merge!({:payload_version => 2})
|
8
9
|
jsonify_hash({:metadata => metadata, :metrics => rearranged_metrics, :slow_transactions => rearranged_slow_transactions})
|
9
10
|
end
|
10
11
|
|
@@ -16,6 +16,9 @@ module ScoutApm
|
|
16
16
|
attr_reader :prof
|
17
17
|
attr_reader :raw_prof
|
18
18
|
|
19
|
+
# TODO: Move this out of SlowTransaction, it doesn't have much to do w/
|
20
|
+
# slow trans other than being a piece of data that ends up in it.
|
21
|
+
#
|
19
22
|
# Given a call stack, generates a filtered backtrace that:
|
20
23
|
# * Limits to the app/models, app/controllers, or app/views directories
|
21
24
|
# * Limits to 5 total callers
|
data/lib/scout_apm/store.rb
CHANGED
@@ -1,220 +1,152 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
1
|
+
# Stores one or more minute's worth of Metrics/SlowTransactions in local ram.
|
2
|
+
# When informed to by the background worker, it pushes the in-ram metrics off to
|
3
|
+
# the layaway file for cross-process aggregation.
|
4
4
|
module ScoutApm
|
5
5
|
class Store
|
6
|
+
# A hash of reporting periods. { StoreReportingPeriodTimestamp => StoreReportingPeriod }
|
7
|
+
attr_reader :reporting_periods
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
+
def initialize
|
10
|
+
@reporting_periods = Hash.new { |h,k| h[k] = StoreReportingPeriod.new(k) }
|
11
|
+
end
|
9
12
|
|
10
|
-
|
11
|
-
|
13
|
+
def current_timestamp
|
14
|
+
StoreReportingPeriodTimestamp.new
|
15
|
+
end
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
attr_reader :slow_transaction_lock
|
17
|
+
# Save newly collected metrics
|
18
|
+
def track!(metrics, options={})
|
19
|
+
reporting_periods[current_timestamp].merge_metrics!(metrics)
|
20
|
+
end
|
18
21
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@stack = Array.new
|
25
|
-
# ensure background thread doesn't manipulate transaction sample while the store is.
|
26
|
-
@slow_transaction_lock = Mutex.new
|
27
|
-
@slow_transactions = Array.new
|
22
|
+
def track_one!(type, name, value, options={})
|
23
|
+
meta = MetricMeta.new("#{type}/#{name}")
|
24
|
+
stat = MetricStats.new(false)
|
25
|
+
stat.update!(value)
|
26
|
+
track!({meta => stat}, options)
|
28
27
|
end
|
29
28
|
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
Thread::current[:scout_apm_ignore_transaction] = nil
|
34
|
-
Thread::current[:scout_apm_scope_name] = nil
|
35
|
-
@transaction_hash = Hash.new
|
36
|
-
@stack = Array.new
|
29
|
+
# Save a new slow transaction
|
30
|
+
def track_slow_transaction!(slow_transaction)
|
31
|
+
reporting_periods[current_timestamp].merge_slow_transactions!(slow_transaction)
|
37
32
|
end
|
38
33
|
|
39
|
-
|
40
|
-
|
34
|
+
# Take each completed reporting_period, and write it to the layaway passed
|
35
|
+
def write_to_layaway(layaway)
|
36
|
+
reporting_periods.select { |time, rp| time.timestamp < current_timestamp.timestamp}.
|
37
|
+
each { |time, reporting_period|
|
38
|
+
layaway.add_reporting_period(time, reporting_period)
|
39
|
+
reporting_periods.delete(time)
|
40
|
+
}
|
41
41
|
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# A timestamp, normalized to the beginning of a minute. Used as a hash key to
|
45
|
+
# bucket metrics into per-minute groups
|
46
|
+
class StoreReportingPeriodTimestamp
|
47
|
+
attr_reader :timestamp
|
42
48
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
# (2) Adds a StackItem to the stack. This StackItem is returned and later used to validate the item popped off the stack
|
47
|
-
# when an instrumented code block completes.
|
48
|
-
def record(metric_name)
|
49
|
-
item = ScoutApm::StackItem.new(metric_name)
|
50
|
-
stack << item
|
51
|
-
item
|
49
|
+
def initialize(time=Time.now)
|
50
|
+
@raw_time = time.utc # The actual time passed in. Store it so we can to_s it without reparsing a timestamp
|
51
|
+
@timestamp = @raw_time.to_i - @raw_time.sec # The normalized time (integer) to compare by
|
52
52
|
end
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def stop_recording(sanity_check_item, options={})
|
58
|
-
item = stack.pop
|
59
|
-
stack_empty = stack.empty?
|
54
|
+
def to_s
|
55
|
+
@raw_time.iso8601
|
56
|
+
end
|
60
57
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
58
|
+
def eql?(o)
|
59
|
+
timestamp.eql?(o.timestamp)
|
60
|
+
end
|
65
61
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
ScoutApm::Agent.instance.logger.warn "Scope [#{Thread::current[:scout_apm_scope_name]}] Popped off stack: #{item.inspect} Expected: #{sanity_check_item.inspect}. Aborting."
|
70
|
-
ignore_transaction!
|
71
|
-
return
|
72
|
-
end
|
62
|
+
def hash
|
63
|
+
timestamp.hash
|
64
|
+
end
|
73
65
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
66
|
+
def age_in_seconds
|
67
|
+
Time.now.to_i - timestamp
|
68
|
+
end
|
69
|
+
end
|
78
70
|
|
79
|
-
|
80
|
-
|
71
|
+
# One period of Storage. Typically 1 minute
|
72
|
+
class StoreReportingPeriod
|
73
|
+
# A hash of { MetricMeta => MetricStat }
|
74
|
+
# This holds metrics for specific parts of the application.
|
75
|
+
# "Controller/user/index", "ActiveRecord/SQL/users/find", "View/users/_gravatar" and similar.
|
76
|
+
#
|
77
|
+
# If over the course of a minute a metric is called more than once (very likely), it will be
|
78
|
+
# combined with the others of the same type, and summed/calculated. The merging logic is in
|
79
|
+
# MetricStats
|
80
|
+
#
|
81
|
+
# Use the accessor function `metrics_payload` for most uses. It includes the calculated aggregate values
|
82
|
+
attr_reader :metrics
|
81
83
|
|
82
|
-
|
83
|
-
|
84
|
-
meta.extra = {:backtrace => ScoutApm::SlowTransaction.backtrace_parser(caller)}
|
85
|
-
end
|
84
|
+
# An array of SlowTransaction objects
|
85
|
+
attr_reader :slow_transactions
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
# deep duplicate
|
96
|
-
duplicate = aggs.dup
|
97
|
-
duplicate.each_pair do |k,v|
|
98
|
-
duplicate[k.dup] = v.dup
|
99
|
-
end
|
100
|
-
merge_metrics(duplicate.merge({meta.dup => stat.dup})) # aggregrates + controller
|
101
|
-
end
|
87
|
+
# A StoreReportingPeriodTimestamp representing the time that this
|
88
|
+
# collection of metrics is for
|
89
|
+
attr_reader :timestamp
|
90
|
+
|
91
|
+
def initialize(timestamp)
|
92
|
+
@metrics = Hash.new
|
93
|
+
@slow_transactions = Array.new
|
94
|
+
@timestamp = timestamp
|
102
95
|
end
|
103
96
|
|
104
|
-
|
105
|
-
#
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
end
|
111
|
-
|
112
|
-
# Returns the top-level category names used in the +metrics+ hash.
|
113
|
-
def categories(metrics)
|
114
|
-
cats = Set.new
|
115
|
-
metrics.keys.each do |meta|
|
116
|
-
next if meta.scope.nil? # ignore controller
|
117
|
-
if match=meta.metric_name.match(/\A([\w]+)\//)
|
118
|
-
cats << match[1]
|
119
|
-
end
|
120
|
-
end # metrics.each
|
121
|
-
cats
|
122
|
-
end
|
123
|
-
|
124
|
-
# Takes a metric_hash of calls and generates aggregates for ActiveRecord and View calls.
|
125
|
-
def aggregate_calls(metrics,parent_meta)
|
126
|
-
categories = categories(metrics)
|
127
|
-
aggregates = {}
|
128
|
-
categories.each do |cat|
|
129
|
-
agg_meta=ScoutApm::MetricMeta.new("#{cat}/all")
|
130
|
-
agg_meta.scope = parent_meta.metric_name
|
131
|
-
agg_stats = ScoutApm::MetricStats.new
|
132
|
-
metrics.each do |meta,stats|
|
133
|
-
if meta.metric_name =~ /\A#{cat}\//
|
134
|
-
agg_stats.combine!(stats)
|
135
|
-
end
|
136
|
-
end # metrics.each
|
137
|
-
aggregates[agg_meta] = agg_stats unless agg_stats.call_count.zero?
|
138
|
-
end # categories.each
|
139
|
-
aggregates
|
140
|
-
end
|
141
|
-
|
142
|
-
SLOW_TRANSACTION_THRESHOLD = 2
|
143
|
-
|
144
|
-
# Stores slow transactions. This will be sent to the server.
|
145
|
-
def store_slow(uri, transaction_hash, parent_meta, parent_stat, options = {})
|
146
|
-
@slow_transaction_lock.synchronize do
|
147
|
-
if parent_stat.total_call_time >= SLOW_TRANSACTION_THRESHOLD
|
148
|
-
slow_transaction = ScoutApm::SlowTransaction.new(uri,
|
149
|
-
parent_meta.metric_name,
|
150
|
-
parent_stat.total_call_time,
|
151
|
-
transaction_hash.dup,
|
152
|
-
ScoutApm::Context.current,
|
153
|
-
Time.now,
|
154
|
-
Thread::current[:scout_apm_prof])
|
155
|
-
@slow_transactions.push(slow_transaction)
|
156
|
-
ScoutApm::Agent.instance.logger.debug "Slow transaction sample added. [URI: #{uri}] [Context: #{ScoutApm::Context.current.to_hash}] Array Size: #{@slow_transactions.size}"
|
157
|
-
end
|
158
|
-
end
|
97
|
+
#################################
|
98
|
+
# Add metrics as they are recorded
|
99
|
+
#################################
|
100
|
+
def merge_metrics!(metrics)
|
101
|
+
@metrics.merge!(metrics) { |key, old_stat, new_stat| old_stat.combine!(new_stat) }
|
102
|
+
self
|
159
103
|
end
|
160
104
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
# Options:
|
165
|
-
# :scope => If provided, overrides the default scope.
|
166
|
-
# :exclusive_time => Sets the exclusive time for the method. If not provided, uses +call_time+.
|
167
|
-
def track!(metric_name, call_time, options = {})
|
168
|
-
meta = ScoutApm::MetricMeta.new(metric_name)
|
169
|
-
meta.scope = options[:scope] if options.has_key?(:scope)
|
170
|
-
stat = metric_hash[meta] || ScoutApm::MetricStats.new
|
171
|
-
stat.update!(call_time,options[:exclusive_time] || call_time)
|
172
|
-
metric_hash[meta] = stat
|
173
|
-
end
|
174
|
-
|
175
|
-
# Combines old and current data
|
176
|
-
def merge_data(old_data)
|
177
|
-
{
|
178
|
-
:metrics => merge_metrics(old_data[:metrics]),
|
179
|
-
:slow_transactions => merge_slow_transactions(old_data[:slow_transactions])
|
180
|
-
}
|
181
|
-
end
|
182
|
-
|
183
|
-
# Merges old and current data, clears the current in-memory metric hash, and returns
|
184
|
-
# the merged data
|
185
|
-
def merge_data_and_clear(old_data)
|
186
|
-
merged = merge_data(old_data)
|
187
|
-
self.metric_hash = {}
|
188
|
-
# TODO - is this lock needed?
|
189
|
-
@slow_transaction_lock.synchronize do
|
190
|
-
self.slow_transactions = []
|
191
|
-
end
|
192
|
-
merged
|
105
|
+
def merge_slow_transactions!(slow_transactions)
|
106
|
+
@slow_transactions += Array(slow_transactions)
|
107
|
+
self
|
193
108
|
end
|
194
109
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
110
|
+
#################################
|
111
|
+
# Retrieve Metrics for reporting
|
112
|
+
#################################
|
113
|
+
def metrics_payload
|
114
|
+
aggregate_metrics
|
115
|
+
end
|
116
|
+
|
117
|
+
def slow_transactions_payload
|
118
|
+
@slow_transactions
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
# We can't aggregate CPU, Memory, Capacity, or Controller, so pass through these metrics directly
|
124
|
+
# TODO: Figure out a way to not have this duplicate what's in Samplers, and also on server's ingest
|
125
|
+
PASSTHROUGH_METRICS = ["CPU", "Memory", "Instance", "Controller"]
|
126
|
+
|
127
|
+
# Calculate any aggregate metrics necessary.
|
128
|
+
#
|
129
|
+
# A hash of { MetricMeta => MetricStat }
|
130
|
+
# This represents the aggregate metrics over the course of the minute.
|
131
|
+
# "ActiveRecord/all", "View/all", "HTTP/all" and similar
|
132
|
+
def aggregate_metrics
|
133
|
+
hsh = Hash.new {|h,k| h[k] = MetricStats.new }
|
134
|
+
|
135
|
+
@metrics.inject(hsh) do |result, (meta, stat)|
|
136
|
+
if PASSTHROUGH_METRICS.include?(meta.type) # Leave as-is, don't attempt to combine
|
137
|
+
hsh[meta] = stat
|
138
|
+
elsif meta.type == "Errors" # Sadly special cased, we want both raw and aggregate values
|
139
|
+
hsh[meta] = stat
|
140
|
+
agg_meta = MetricMeta.new("Errors/Request", :scope => meta.scope)
|
141
|
+
hsh[agg_meta].combine!(stat)
|
142
|
+
else # Combine down to a single /all key
|
143
|
+
agg_meta = MetricMeta.new("#{meta.type}/all", :scope => meta.scope)
|
144
|
+
hsh[agg_meta].combine!(stat)
|
215
145
|
end
|
146
|
+
|
147
|
+
hsh
|
216
148
|
end
|
217
|
-
self.slow_transactions
|
218
149
|
end
|
219
|
-
end
|
150
|
+
end
|
220
151
|
end
|
152
|
+
|