scout_apm 1.5.5 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +8 -0
- data/lib/scout_apm.rb +3 -0
- data/lib/scout_apm/agent.rb +23 -25
- data/lib/scout_apm/agent/reporting.rb +8 -3
- data/lib/scout_apm/attribute_arranger.rb +4 -0
- data/lib/scout_apm/bucket_name_splitter.rb +3 -3
- data/lib/scout_apm/config.rb +4 -2
- data/lib/scout_apm/histogram.rb +20 -0
- data/lib/scout_apm/instruments/percentile_sampler.rb +37 -0
- data/lib/scout_apm/instruments/process/process_cpu.rb +12 -0
- data/lib/scout_apm/instruments/process/process_memory.rb +12 -0
- data/lib/scout_apm/layer_converters/converter_base.rb +6 -4
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +21 -13
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +28 -22
- data/lib/scout_apm/metric_meta.rb +5 -1
- data/lib/scout_apm/metric_set.rb +1 -1
- data/lib/scout_apm/reporter.rb +3 -1
- data/lib/scout_apm/request_histograms.rb +46 -0
- data/lib/scout_apm/scored_item_set.rb +79 -0
- data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +2 -0
- data/lib/scout_apm/slow_job_policy.rb +89 -19
- data/lib/scout_apm/slow_job_record.rb +20 -1
- data/lib/scout_apm/slow_request_policy.rb +80 -12
- data/lib/scout_apm/slow_transaction.rb +19 -2
- data/lib/scout_apm/store.rb +45 -15
- data/lib/scout_apm/tracked_request.rb +33 -10
- data/lib/scout_apm/version.rb +1 -1
- data/test/test_helper.rb +4 -3
- data/test/unit/layaway_test.rb +5 -8
- data/test/unit/scored_item_set_test.rb +65 -0
- data/test/unit/serializers/payload_serializer_test.rb +2 -1
- data/test/unit/slow_item_set_test.rb +2 -1
- data/test/unit/slow_request_policy_test.rb +42 -0
- metadata +9 -2
data/lib/scout_apm/store.rb
CHANGED
@@ -6,9 +6,13 @@ module ScoutApm
|
|
6
6
|
# A hash of reporting periods. { StoreReportingPeriodTimestamp => StoreReportingPeriod }
|
7
7
|
attr_reader :reporting_periods
|
8
8
|
|
9
|
+
# Used to pull metrics into each reporting period, as that reporting period is finished.
|
10
|
+
attr_reader :samplers
|
11
|
+
|
9
12
|
def initialize
|
10
13
|
@mutex = Mutex.new
|
11
14
|
@reporting_periods = Hash.new { |h,k| h[k] = StoreReportingPeriod.new(k) }
|
15
|
+
@samplers = []
|
12
16
|
end
|
13
17
|
|
14
18
|
def current_timestamp
|
@@ -66,12 +70,32 @@ module ScoutApm
|
|
66
70
|
@mutex.synchronize {
|
67
71
|
reporting_periods.select { |time, rp| force || time.timestamp < current_timestamp.timestamp}.
|
68
72
|
each { |time, rp|
|
73
|
+
collect_samplers(rp)
|
69
74
|
layaway.add_reporting_period(time, rp)
|
70
75
|
reporting_periods.delete(time)
|
71
76
|
}
|
72
77
|
}
|
73
78
|
ScoutApm::Agent.instance.logger.debug("Finished writing to layaway")
|
74
79
|
end
|
80
|
+
|
81
|
+
######################################
|
82
|
+
# Sampler support
|
83
|
+
def add_sampler(sampler)
|
84
|
+
@samplers << sampler
|
85
|
+
end
|
86
|
+
|
87
|
+
def collect_samplers(rp)
|
88
|
+
@samplers.each do |sampler|
|
89
|
+
begin
|
90
|
+
metrics = sampler.metrics(rp.timestamp)
|
91
|
+
rp.absorb_metrics!(metrics)
|
92
|
+
rescue => e
|
93
|
+
ScoutApm::Agent.instance.logger.info "Error reading #{sampler.human_name} for period: #{rp}"
|
94
|
+
ScoutApm::Agent.instance.logger.debug e.message
|
95
|
+
ScoutApm::Agent.instance.logger.debug e.backtrace.join("\n")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
75
99
|
end
|
76
100
|
|
77
101
|
# A timestamp, normalized to the beginning of a minute. Used as a hash key to
|
@@ -107,11 +131,12 @@ module ScoutApm
|
|
107
131
|
|
108
132
|
# One period of Storage. Typically 1 minute
|
109
133
|
class StoreReportingPeriod
|
110
|
-
# A SlowItemSet to store slow transactions in
|
111
|
-
attr_reader :slow_transactions
|
112
134
|
|
113
|
-
# A
|
114
|
-
attr_reader :
|
135
|
+
# A ScoredItemSet holding the "best" traces for the period
|
136
|
+
attr_reader :request_traces
|
137
|
+
|
138
|
+
# A ScoredItemSet holding the "best" traces for the period
|
139
|
+
attr_reader :job_traces
|
115
140
|
|
116
141
|
# A StoreReportingPeriodTimestamp representing the time that this
|
117
142
|
# collection of metrics is for
|
@@ -122,19 +147,21 @@ module ScoutApm
|
|
122
147
|
def initialize(timestamp)
|
123
148
|
@timestamp = timestamp
|
124
149
|
|
125
|
-
@
|
126
|
-
@
|
150
|
+
@request_traces = ScoredItemSet.new
|
151
|
+
@job_traces = ScoredItemSet.new
|
127
152
|
|
128
153
|
@metric_set = MetricSet.new
|
129
154
|
@jobs = Hash.new
|
130
155
|
end
|
131
156
|
|
132
157
|
# Merges another StoreReportingPeriod into this one
|
133
|
-
def merge(
|
158
|
+
def merge(other)
|
134
159
|
self.
|
135
|
-
merge_metrics!(
|
136
|
-
merge_slow_transactions!(
|
137
|
-
merge_jobs!(
|
160
|
+
merge_metrics!(other.metric_set).
|
161
|
+
merge_slow_transactions!(other.slow_transactions_payload).
|
162
|
+
merge_jobs!(other.jobs).
|
163
|
+
merge_slow_jobs!(other.slow_jobs_payload)
|
164
|
+
self
|
138
165
|
end
|
139
166
|
|
140
167
|
#################################
|
@@ -148,6 +175,7 @@ module ScoutApm
|
|
148
175
|
end
|
149
176
|
|
150
177
|
# For merging when you have another metric_set object
|
178
|
+
# Makes sure that you don't duplicate error count records
|
151
179
|
def merge_metrics!(other_metric_set)
|
152
180
|
metric_set.combine!(other_metric_set)
|
153
181
|
self
|
@@ -155,14 +183,14 @@ module ScoutApm
|
|
155
183
|
|
156
184
|
def merge_slow_transactions!(new_transactions)
|
157
185
|
Array(new_transactions).each do |one_transaction|
|
158
|
-
|
186
|
+
request_traces << one_transaction
|
159
187
|
end
|
160
188
|
|
161
189
|
self
|
162
190
|
end
|
163
191
|
|
164
192
|
def merge_jobs!(jobs)
|
165
|
-
jobs.each do |job|
|
193
|
+
Array(jobs).each do |job|
|
166
194
|
if @jobs.has_key?(job)
|
167
195
|
@jobs[job].combine!(job)
|
168
196
|
else
|
@@ -175,8 +203,10 @@ module ScoutApm
|
|
175
203
|
|
176
204
|
def merge_slow_jobs!(new_jobs)
|
177
205
|
Array(new_jobs).each do |job|
|
178
|
-
|
206
|
+
job_traces << job
|
179
207
|
end
|
208
|
+
|
209
|
+
self
|
180
210
|
end
|
181
211
|
|
182
212
|
#################################
|
@@ -187,7 +217,7 @@ module ScoutApm
|
|
187
217
|
end
|
188
218
|
|
189
219
|
def slow_transactions_payload
|
190
|
-
|
220
|
+
request_traces.to_a
|
191
221
|
end
|
192
222
|
|
193
223
|
def jobs
|
@@ -195,7 +225,7 @@ module ScoutApm
|
|
195
225
|
end
|
196
226
|
|
197
227
|
def slow_jobs_payload
|
198
|
-
|
228
|
+
job_traces.to_a
|
199
229
|
end
|
200
230
|
|
201
231
|
#################################
|
@@ -186,24 +186,47 @@ module ScoutApm
|
|
186
186
|
def record!
|
187
187
|
@recorded = true
|
188
188
|
|
189
|
+
# Update immediate and long-term histograms for both job and web requests
|
190
|
+
if unique_name != :unknown
|
191
|
+
ScoutApm::Agent.instance.request_histograms.add(unique_name, root_layer.total_call_time)
|
192
|
+
ScoutApm::Agent.instance.request_histograms_by_time[ScoutApm::Agent.instance.store.current_timestamp].
|
193
|
+
add(unique_name, root_layer.total_call_time)
|
194
|
+
end
|
195
|
+
|
189
196
|
metrics = LayerConverters::MetricConverter.new(self).call
|
190
197
|
ScoutApm::Agent.instance.store.track!(metrics)
|
191
198
|
|
192
|
-
slow, slow_metrics = LayerConverters::SlowRequestConverter.new(self).call
|
193
|
-
ScoutApm::Agent.instance.store.track_slow_transaction!(slow)
|
194
|
-
ScoutApm::Agent.instance.store.track!(slow_metrics)
|
195
|
-
|
196
199
|
error_metrics = LayerConverters::ErrorConverter.new(self).call
|
197
200
|
ScoutApm::Agent.instance.store.track!(error_metrics)
|
198
201
|
|
199
|
-
|
200
|
-
|
202
|
+
if web?
|
203
|
+
# Don't #call this - that's the job of the ScoredItemSet later.
|
204
|
+
slow_converter = LayerConverters::SlowRequestConverter.new(self)
|
205
|
+
ScoutApm::Agent.instance.store.track_slow_transaction!(slow_converter)
|
206
|
+
|
207
|
+
queue_time_metrics = LayerConverters::RequestQueueTimeConverter.new(self).call
|
208
|
+
ScoutApm::Agent.instance.store.track!(queue_time_metrics)
|
209
|
+
end
|
210
|
+
|
211
|
+
if job?
|
212
|
+
job_metrics = LayerConverters::JobConverter.new(self).call
|
213
|
+
ScoutApm::Agent.instance.store.track_job!(job_metrics)
|
201
214
|
|
202
|
-
|
203
|
-
|
215
|
+
job_converter = LayerConverters::SlowJobConverter.new(self)
|
216
|
+
ScoutApm::Agent.instance.store.track_slow_job!(job_converter)
|
217
|
+
end
|
218
|
+
end
|
204
219
|
|
205
|
-
|
206
|
-
|
220
|
+
# Only call this after the request is complete
|
221
|
+
def unique_name
|
222
|
+
@unique_name ||= begin
|
223
|
+
scope_layer = LayerConverters::ConverterBase.new(self).scope_layer
|
224
|
+
if scope_layer
|
225
|
+
scope_layer.legacy_metric_name
|
226
|
+
else
|
227
|
+
:unknown
|
228
|
+
end
|
229
|
+
end
|
207
230
|
end
|
208
231
|
|
209
232
|
# Have we already persisted this request?
|
data/lib/scout_apm/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
+
# Load & Start simplecov before loading scout_apm
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start
|
4
|
+
|
1
5
|
require 'minitest/autorun'
|
2
6
|
require 'minitest/unit'
|
3
7
|
require 'minitest/pride'
|
4
8
|
require 'pry'
|
5
9
|
|
6
|
-
# Load & Start simplecov before loading scout_apm
|
7
|
-
require 'simplecov'
|
8
|
-
SimpleCov.start
|
9
10
|
|
10
11
|
require 'scout_apm'
|
11
12
|
|
data/test/unit/layaway_test.rb
CHANGED
@@ -18,15 +18,12 @@ class LayawayTest < Minitest::Test
|
|
18
18
|
|
19
19
|
def test_merge_reporting_period
|
20
20
|
File.open(DATA_FILE_PATH, 'w') { |file| file.write(Marshal.dump(NEW_FORMAT)) }
|
21
|
-
ScoutApm::
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
data.add_reporting_period(TIMESTAMP,ScoutApm::StoreReportingPeriod.new(TIMESTAMP))
|
26
|
-
assert_equal [TIMESTAMP], Marshal.load(File.read(DATA_FILE_PATH)).keys
|
27
|
-
# TODO - add tests to verify metrics+slow transactions are merged
|
21
|
+
layaway = ScoutApm::Layaway.new
|
22
|
+
layaway.add_reporting_period(TIMESTAMP, ScoutApm::StoreReportingPeriod.new(TIMESTAMP))
|
23
|
+
unmarshalled = Marshal.load(File.read(DATA_FILE_PATH))
|
24
|
+
assert_equal [TIMESTAMP], unmarshalled.keys
|
28
25
|
end
|
29
26
|
|
30
27
|
TIMESTAMP = ScoutApm::StoreReportingPeriodTimestamp.new(Time.parse("2015-01-01"))
|
31
28
|
NEW_FORMAT = {TIMESTAMP => ScoutApm::StoreReportingPeriod.new(TIMESTAMP)} # Format for 1.2+ agents
|
32
|
-
end
|
29
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'scout_apm/scored_item_set'
|
4
|
+
|
5
|
+
class FakeScoredItem
|
6
|
+
def initialize(name, score)
|
7
|
+
@name = name
|
8
|
+
@score = score
|
9
|
+
end
|
10
|
+
def name; @name; end
|
11
|
+
def score; @score; end
|
12
|
+
def call; "called_#{@score}_#{@name}"; end
|
13
|
+
end
|
14
|
+
|
15
|
+
class ScoredItemSetTest < Minitest::Test
|
16
|
+
def test_empty_set_always_adds_item
|
17
|
+
set = ScoutApm::ScoredItemSet.new
|
18
|
+
set << FakeScoredItem.new("users/index", 10)
|
19
|
+
|
20
|
+
assert_equal set.to_a.first, "called_10_users/index"
|
21
|
+
assert_equal set.count, 1
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_repeated_additions_chooses_most_expensive
|
25
|
+
set = ScoutApm::ScoredItemSet.new
|
26
|
+
|
27
|
+
[ FakeScoredItem.new("users/index", 10),
|
28
|
+
FakeScoredItem.new("users/index", 11),
|
29
|
+
FakeScoredItem.new("users/index", 12)
|
30
|
+
].shuffle.each { |fsi| set << fsi }
|
31
|
+
|
32
|
+
assert_equal set.to_a.first, "called_12_users/index"
|
33
|
+
assert_equal set.count, 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_multiple_items_occupy_different_buckets
|
37
|
+
set = ScoutApm::ScoredItemSet.new
|
38
|
+
|
39
|
+
[ FakeScoredItem.new("users/index", 10),
|
40
|
+
FakeScoredItem.new("users/index", 11),
|
41
|
+
FakeScoredItem.new("users/show", 12),
|
42
|
+
FakeScoredItem.new("users/show", 10)
|
43
|
+
].shuffle.each { |fsi| set << fsi }
|
44
|
+
|
45
|
+
assert_equal set.count, 2
|
46
|
+
assert set.to_a.include?("called_11_users/index")
|
47
|
+
assert set.to_a.include?("called_12_users/show")
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_evicts_at_capacity
|
51
|
+
set = ScoutApm::ScoredItemSet.new(3) # Force max_size to 3
|
52
|
+
|
53
|
+
[ FakeScoredItem.new("users/index", 10),
|
54
|
+
FakeScoredItem.new("users/show", 11),
|
55
|
+
FakeScoredItem.new("posts/index", 12),
|
56
|
+
FakeScoredItem.new("posts/move", 13)
|
57
|
+
].shuffle.each { |fsi| set << fsi }
|
58
|
+
|
59
|
+
assert_equal set.count, 3
|
60
|
+
assert !set.to_a.include?("called_10_users/index"), "Did not Expect to see users/index in #{set.to_a.inspect}"
|
61
|
+
assert set.to_a.include?("called_11_users/show"), "Expected to see users/show in #{set.to_a.inspect}"
|
62
|
+
assert set.to_a.include?("called_12_posts/index"), "Expected to see posts/index in #{set.to_a.inspect}"
|
63
|
+
assert set.to_a.include?("called_13_posts/move"), "Expected to see posts/move in #{set.to_a.inspect}"
|
64
|
+
end
|
65
|
+
end
|
@@ -145,7 +145,7 @@ class PayloadSerializerTest < Minitest::Test
|
|
145
145
|
context = ScoutApm::Context.new
|
146
146
|
context.add({"this" => "that"})
|
147
147
|
context.add_user({"hello" => "goodbye"})
|
148
|
-
slow_t = ScoutApm::SlowTransaction.new("http://example.com/blabla", "Buckethead/something/else", 1.23, slow_transaction_metrics, context, Time.at(1448198788), [])
|
148
|
+
slow_t = ScoutApm::SlowTransaction.new("http://example.com/blabla", "Buckethead/something/else", 1.23, slow_transaction_metrics, context, Time.at(1448198788), [], 10)
|
149
149
|
payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize({}, {}, [slow_t], [], [])
|
150
150
|
formatted_slow_transactions = [
|
151
151
|
{
|
@@ -158,6 +158,7 @@ class PayloadSerializerTest < Minitest::Test
|
|
158
158
|
"uri" => "http://example.com/blabla",
|
159
159
|
"context" => {"this"=>"that", "user"=>{"hello"=>"goodbye"}},
|
160
160
|
"prof" => [],
|
161
|
+
"score" => 10,
|
161
162
|
"metrics" => [
|
162
163
|
{
|
163
164
|
"key" => {
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'scout_apm/slow_request_policy'
|
4
|
+
require 'scout_apm/layer'
|
5
|
+
|
6
|
+
class FakeRequest
|
7
|
+
def initialize(name)
|
8
|
+
@name = name
|
9
|
+
@root_layer = ScoutApm::Layer.new("Controller", name)
|
10
|
+
@root_layer.instance_variable_set("@stop_time", Time.now)
|
11
|
+
end
|
12
|
+
def unique_name; "Controller/foo/bar"; end
|
13
|
+
def root_layer; @root_layer; end
|
14
|
+
def set_duration(seconds)
|
15
|
+
@root_layer.instance_variable_set("@start_time", Time.now - seconds)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class SlowRequestPolicyTest < Minitest::Test
|
20
|
+
def test_stored_records_current_time
|
21
|
+
test_start = Time.now
|
22
|
+
policy = ScoutApm::SlowRequestPolicy.new
|
23
|
+
request = FakeRequest.new("users/index")
|
24
|
+
|
25
|
+
policy.stored!(request)
|
26
|
+
assert policy.last_seen[request.unique_name] > test_start
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_score
|
30
|
+
policy = ScoutApm::SlowRequestPolicy.new
|
31
|
+
request = FakeRequest.new("users/index")
|
32
|
+
|
33
|
+
request.set_duration(10) # 10 seconds
|
34
|
+
policy.last_seen[request.unique_name] = Time.now - 120 # 2 minutes since last seen
|
35
|
+
agent.request_histograms.add(request.unique_name, 1)
|
36
|
+
|
37
|
+
# Actual value I have in console is 1.599
|
38
|
+
assert policy.score(request) > 1.5
|
39
|
+
assert policy.score(request) < 2.0
|
40
|
+
|
41
|
+
end
|
42
|
+
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: 1.
|
4
|
+
version: 1.6.0
|
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-06-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -117,6 +117,7 @@ files:
|
|
117
117
|
- lib/scout_apm/instruments/mongoid.rb
|
118
118
|
- lib/scout_apm/instruments/moped.rb
|
119
119
|
- lib/scout_apm/instruments/net_http.rb
|
120
|
+
- lib/scout_apm/instruments/percentile_sampler.rb
|
120
121
|
- lib/scout_apm/instruments/process/process_cpu.rb
|
121
122
|
- lib/scout_apm/instruments/process/process_memory.rb
|
122
123
|
- lib/scout_apm/instruments/rails_router.rb
|
@@ -142,7 +143,9 @@ files:
|
|
142
143
|
- lib/scout_apm/platform_integrations/heroku.rb
|
143
144
|
- lib/scout_apm/platform_integrations/server.rb
|
144
145
|
- lib/scout_apm/reporter.rb
|
146
|
+
- lib/scout_apm/request_histograms.rb
|
145
147
|
- lib/scout_apm/request_manager.rb
|
148
|
+
- lib/scout_apm/scored_item_set.rb
|
146
149
|
- lib/scout_apm/serializers/app_server_load_serializer.rb
|
147
150
|
- lib/scout_apm/serializers/deploy_serializer.rb
|
148
151
|
- lib/scout_apm/serializers/directive_serializer.rb
|
@@ -189,9 +192,11 @@ files:
|
|
189
192
|
- test/unit/instruments/active_record_instruments_test.rb
|
190
193
|
- test/unit/layaway_test.rb
|
191
194
|
- test/unit/metric_set_test.rb
|
195
|
+
- test/unit/scored_item_set_test.rb
|
192
196
|
- test/unit/serializers/payload_serializer_test.rb
|
193
197
|
- test/unit/slow_item_set_test.rb
|
194
198
|
- test/unit/slow_job_policy_test.rb
|
199
|
+
- test/unit/slow_request_policy_test.rb
|
195
200
|
- test/unit/sql_sanitizer_test.rb
|
196
201
|
- test/unit/utils/active_record_metric_name_test.rb
|
197
202
|
homepage: https://github.com/scoutapp/scout_apm_ruby
|
@@ -228,9 +233,11 @@ test_files:
|
|
228
233
|
- test/unit/instruments/active_record_instruments_test.rb
|
229
234
|
- test/unit/layaway_test.rb
|
230
235
|
- test/unit/metric_set_test.rb
|
236
|
+
- test/unit/scored_item_set_test.rb
|
231
237
|
- test/unit/serializers/payload_serializer_test.rb
|
232
238
|
- test/unit/slow_item_set_test.rb
|
233
239
|
- test/unit/slow_job_policy_test.rb
|
240
|
+
- test/unit/slow_request_policy_test.rb
|
234
241
|
- test/unit/sql_sanitizer_test.rb
|
235
242
|
- test/unit/utils/active_record_metric_name_test.rb
|
236
243
|
has_rdoc:
|