scout_apm 1.4.6 → 1.5.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +9 -0
- data/lib/scout_apm/agent/reporting.rb +8 -6
- data/lib/scout_apm/agent.rb +10 -6
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +23 -11
- data/lib/scout_apm/call_set.rb +61 -0
- data/lib/scout_apm/config.rb +2 -1
- data/lib/scout_apm/environment.rb +12 -7
- data/lib/scout_apm/histogram.rb +124 -0
- data/lib/scout_apm/instruments/.DS_Store +0 -0
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +1 -0
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +1 -0
- data/lib/scout_apm/instruments/delayed_job.rb +1 -0
- data/lib/scout_apm/instruments/process/process_memory.rb +1 -1
- data/lib/scout_apm/instruments/sinatra.rb +1 -1
- data/lib/scout_apm/job_record.rb +76 -0
- data/lib/scout_apm/layaway.rb +4 -1
- data/lib/scout_apm/layaway_file.rb +4 -4
- data/lib/scout_apm/layer.rb +14 -4
- data/lib/scout_apm/layer_converters/converter_base.rb +30 -0
- data/lib/scout_apm/layer_converters/depth_first_walker.rb +36 -0
- data/lib/scout_apm/layer_converters/error_converter.rb +20 -0
- data/lib/scout_apm/layer_converters/job_converter.rb +84 -0
- data/lib/scout_apm/layer_converters/metric_converter.rb +45 -0
- data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +60 -0
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +88 -0
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +111 -0
- data/lib/scout_apm/metric_meta.rb +9 -0
- data/lib/scout_apm/metric_set.rb +44 -0
- data/lib/scout_apm/reporter.rb +12 -5
- data/lib/scout_apm/serializers/jobs_serializer_to_json.rb +28 -0
- data/lib/scout_apm/serializers/metrics_to_json_serializer.rb +54 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +5 -3
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +9 -4
- data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +29 -0
- data/lib/scout_apm/slow_item_set.rb +80 -0
- data/lib/scout_apm/slow_job_policy.rb +29 -0
- data/lib/scout_apm/slow_job_record.rb +33 -0
- data/lib/scout_apm/slow_transaction.rb +0 -22
- data/lib/scout_apm/stackprof_tree_collapser.rb +7 -8
- data/lib/scout_apm/store.rb +55 -35
- data/lib/scout_apm/tracked_request.rb +67 -10
- data/lib/scout_apm/utils/active_record_metric_name.rb +13 -0
- data/lib/scout_apm/utils/backtrace_parser.rb +31 -0
- data/lib/scout_apm/utils/fake_stack_prof.rb +1 -1
- data/lib/scout_apm/utils/sql_sanitizer.rb +6 -0
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +25 -5
- data/test/unit/histogram_test.rb +93 -0
- data/test/unit/serializers/payload_serializer_test.rb +5 -5
- data/test/unit/{slow_transaction_set_test.rb → slow_item_set_test.rb} +8 -8
- data/test/unit/slow_job_policy_test.rb +55 -0
- metadata +30 -9
- data/lib/scout_apm/layer_converter.rb +0 -222
- data/lib/scout_apm/request_queue_time.rb +0 -57
- data/lib/scout_apm/slow_transaction_set.rb +0 -67
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'scout_apm/histogram'
|
4
|
+
|
5
|
+
# These tests assume insertion order. Because of the approximate nature of
|
6
|
+
# this data structure, changing the order can slightly change the results,
|
7
|
+
# causing these tests to fail. The overall "shape" of the histogram should be
|
8
|
+
# approximately the same in the case of alternate orders, but I'm punting on
|
9
|
+
# any attempts to automate that.
|
10
|
+
class HistogramTest < Minitest::Test
|
11
|
+
# When we have enough slots, we don't have any fuzz in the data, so exact numbers.
|
12
|
+
def test_histogram_min_and_max_with_big_enough_histogram
|
13
|
+
hist = ScoutApm::NumericHistogram.new(10)
|
14
|
+
|
15
|
+
10.times {
|
16
|
+
(1..10).to_a.each do |i|
|
17
|
+
hist.add(i)
|
18
|
+
end
|
19
|
+
}
|
20
|
+
|
21
|
+
assert_equal 1, hist.quantile(0)
|
22
|
+
assert_equal 10, hist.quantile(100)
|
23
|
+
end
|
24
|
+
|
25
|
+
# When we don't have enough slots, we have to approximate the buckets
|
26
|
+
# In this case, the true range is 1 through 10, and we only have 5 buckets to allocate.
|
27
|
+
# 1 2 3 4 5 6 7 8 9 10 # <-- True Range
|
28
|
+
# x x x x x # <-- Where buckets get adjusted to
|
29
|
+
def test_histogram_min_and_max_with_fewer_buckets
|
30
|
+
hist = ScoutApm::NumericHistogram.new(5)
|
31
|
+
|
32
|
+
10.times {
|
33
|
+
(1..10).to_a.each do |i|
|
34
|
+
hist.add(i)
|
35
|
+
end
|
36
|
+
}
|
37
|
+
|
38
|
+
assert_equal 1.5, hist.quantile(0).round(1)
|
39
|
+
assert_equal 9.5, hist.quantile(100).round(1)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_combine
|
43
|
+
hist1 = ScoutApm::NumericHistogram.new(5)
|
44
|
+
10.times {
|
45
|
+
(1..10).to_a.each do |i|
|
46
|
+
hist1.add(i)
|
47
|
+
end
|
48
|
+
}
|
49
|
+
|
50
|
+
hist2 = ScoutApm::NumericHistogram.new(10)
|
51
|
+
10.times {
|
52
|
+
(1..10).to_a.each do |i|
|
53
|
+
hist2.add(i)
|
54
|
+
end
|
55
|
+
}
|
56
|
+
|
57
|
+
combined = hist1.combine!(hist2)
|
58
|
+
assert_equal 1.5, combined.quantile(0).round(1)
|
59
|
+
assert_equal 9.5, combined.quantile(100).round(1)
|
60
|
+
assert_equal 200, combined.total
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_combine_retains_order
|
64
|
+
hist1 = ScoutApm::NumericHistogram.new(5)
|
65
|
+
10.times {
|
66
|
+
(10..50).to_a.each do |i|
|
67
|
+
hist1.add(i)
|
68
|
+
end
|
69
|
+
}
|
70
|
+
|
71
|
+
hist2 = ScoutApm::NumericHistogram.new(10)
|
72
|
+
10.times {
|
73
|
+
(1..10).to_a.each do |i|
|
74
|
+
hist2.add(i)
|
75
|
+
end
|
76
|
+
}
|
77
|
+
|
78
|
+
combined = hist1.combine!(hist2)
|
79
|
+
assert combined.quantile(0) < combined.quantile(100)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_mean
|
83
|
+
hist = ScoutApm::NumericHistogram.new(5)
|
84
|
+
10.times {
|
85
|
+
(1..10).to_a.each do |i|
|
86
|
+
hist.add(i)
|
87
|
+
end
|
88
|
+
}
|
89
|
+
|
90
|
+
assert_equal 5.5, hist.mean
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
@@ -33,7 +33,7 @@ class PayloadSerializerTest < Minitest::Test
|
|
33
33
|
:unique_id => "unique_idz",
|
34
34
|
:agent_version => 123
|
35
35
|
}
|
36
|
-
payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, {}, {})
|
36
|
+
payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, {}, {}, [], [])
|
37
37
|
|
38
38
|
# symbol keys turn to strings
|
39
39
|
formatted_metadata = {
|
@@ -74,7 +74,7 @@ class PayloadSerializerTest < Minitest::Test
|
|
74
74
|
stats.total_exclusive_time = 0.07813208899999999
|
75
75
|
}
|
76
76
|
}
|
77
|
-
payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize({}, metrics, {})
|
77
|
+
payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize({}, metrics, {}, [], [])
|
78
78
|
formatted_metrics = [
|
79
79
|
{
|
80
80
|
"key" => {
|
@@ -145,8 +145,8 @@ 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),
|
149
|
-
payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize({}, {}, [slow_t])
|
148
|
+
slow_t = ScoutApm::SlowTransaction.new("http://example.com/blabla", "Buckethead/something/else", 1.23, slow_transaction_metrics, context, Time.at(1448198788), StackProf.new)
|
149
|
+
payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize({}, {}, [slow_t], [], [])
|
150
150
|
formatted_slow_transactions = [
|
151
151
|
{
|
152
152
|
"key" => {
|
@@ -204,7 +204,7 @@ class PayloadSerializerTest < Minitest::Test
|
|
204
204
|
:quotie => "here are some \"quotes\"",
|
205
205
|
:payload_version => 2,
|
206
206
|
}
|
207
|
-
payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, {}, {})
|
207
|
+
payload = ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, {}, {}, [], [])
|
208
208
|
|
209
209
|
# symbol keys turn to strings
|
210
210
|
formatted_metadata = {
|
@@ -1,17 +1,17 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
require 'scout_apm/
|
3
|
+
require 'scout_apm/slow_item_set'
|
4
4
|
require 'scout_apm/slow_transaction'
|
5
5
|
|
6
|
-
class
|
6
|
+
class SlowItemSetTest < Minitest::Test
|
7
7
|
def test_adding_to_empty_set
|
8
|
-
set = ScoutApm::
|
8
|
+
set = ScoutApm::SlowItemSet.new(3, 1)
|
9
9
|
set << make_slow("Controller/Foo")
|
10
10
|
assert_equal 1, set.count
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_adding_to_partially_full_set
|
14
|
-
set = ScoutApm::
|
14
|
+
set = ScoutApm::SlowItemSet.new(3, 1)
|
15
15
|
set << make_slow("Controller/Foo")
|
16
16
|
set << make_slow("Controller/Foo")
|
17
17
|
assert_equal 2, set.count
|
@@ -19,7 +19,7 @@ class SlowTransactionSetTest < Minitest::Test
|
|
19
19
|
|
20
20
|
def test_overflow_of_one_type
|
21
21
|
max_size = 3
|
22
|
-
set = ScoutApm::
|
22
|
+
set = ScoutApm::SlowItemSet.new(max_size, 1)
|
23
23
|
set << make_slow("Controller/Foo")
|
24
24
|
set << make_slow("Controller/Foo")
|
25
25
|
set << make_slow("Controller/Foo")
|
@@ -31,7 +31,7 @@ class SlowTransactionSetTest < Minitest::Test
|
|
31
31
|
|
32
32
|
def test_eviction_of_overrepresented
|
33
33
|
max_size = 3
|
34
|
-
set = ScoutApm::
|
34
|
+
set = ScoutApm::SlowItemSet.new(max_size, 1)
|
35
35
|
set << make_slow("Controller/Foo")
|
36
36
|
set << make_slow("Controller/Foo")
|
37
37
|
set << make_slow("Controller/Foo")
|
@@ -49,7 +49,7 @@ class SlowTransactionSetTest < Minitest::Test
|
|
49
49
|
# evicted one was the fastest of the Foos
|
50
50
|
def test_eviction_of_fastest
|
51
51
|
max_size = 3
|
52
|
-
set = ScoutApm::
|
52
|
+
set = ScoutApm::SlowItemSet.new(max_size, 1)
|
53
53
|
|
54
54
|
[1,2,3].shuffle.each do |seconds| # Shuffle to remove any assumptions on order
|
55
55
|
set << make_slow("Controller/Foo", seconds)
|
@@ -63,7 +63,7 @@ class SlowTransactionSetTest < Minitest::Test
|
|
63
63
|
def test_eviction_when_no_overrepresented
|
64
64
|
max_size = 4
|
65
65
|
fair = 2
|
66
|
-
set = ScoutApm::
|
66
|
+
set = ScoutApm::SlowItemSet.new(max_size, fair)
|
67
67
|
|
68
68
|
# Full, but each is at fair level
|
69
69
|
set << make_slow("Controller/Bar")
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'scout_apm/slow_job_policy'
|
4
|
+
|
5
|
+
class SlowJobPolicyTest < Minitest::Test
|
6
|
+
def test_first_call_is_not_slow
|
7
|
+
policy = ScoutApm::SlowJobPolicy.new
|
8
|
+
assert !policy.slow?("TestWorker", 10)
|
9
|
+
end
|
10
|
+
|
11
|
+
# All of these get faster and faster, so none are marked as slow.
|
12
|
+
def test_fast_calls_are_not_slow
|
13
|
+
policy = ScoutApm::SlowJobPolicy.new
|
14
|
+
assert !policy.slow?("TestWorker", 10)
|
15
|
+
assert !policy.slow?("TestWorker", 8)
|
16
|
+
assert !policy.slow?("TestWorker", 6)
|
17
|
+
assert !policy.slow?("TestWorker", 4)
|
18
|
+
assert !policy.slow?("TestWorker", 2)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_slow_calls_are_marked_as_slow
|
22
|
+
policy = ScoutApm::SlowJobPolicy.new
|
23
|
+
policy.slow?("TestWorker", 10) # Prime it with a not-slow
|
24
|
+
|
25
|
+
assert policy.slow?("TestWorker", 12)
|
26
|
+
assert policy.slow?("TestWorker", 14)
|
27
|
+
assert policy.slow?("TestWorker", 16)
|
28
|
+
assert policy.slow?("TestWorker", 18)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_mix_of_fast_and_slow
|
32
|
+
policy = ScoutApm::SlowJobPolicy.new
|
33
|
+
policy.slow?("TestWorker", 10) # Prime it with a not-slow
|
34
|
+
|
35
|
+
assert policy.slow?("TestWorker", 12)
|
36
|
+
assert !policy.slow?("TestWorker", 8)
|
37
|
+
assert policy.slow?("TestWorker", 13)
|
38
|
+
assert !policy.slow?("TestWorker", 6)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_different_workers_dont_interfere
|
42
|
+
policy = ScoutApm::SlowJobPolicy.new
|
43
|
+
policy.slow?("TestWorker", 10) # Prime it with a not-slow
|
44
|
+
policy.slow?("OtherWorker", 1.0) # Prime it with a not-slow
|
45
|
+
|
46
|
+
assert !policy.slow?("TestWorker", 8)
|
47
|
+
assert policy.slow?("OtherWorker", 2)
|
48
|
+
assert !policy.slow?("TestWorker", 1)
|
49
|
+
assert policy.slow?("OtherWorker", 3)
|
50
|
+
assert policy.slow?("TestWorker", 12)
|
51
|
+
assert !policy.slow?("OtherWorker", 1)
|
52
|
+
assert policy.slow?("TestWorker", 12)
|
53
|
+
assert policy.slow?("OtherWorker", 4)
|
54
|
+
end
|
55
|
+
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.5.0.pre
|
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-04-
|
12
|
+
date: 2016-04-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -76,6 +76,7 @@ files:
|
|
76
76
|
- lib/scout_apm/background_job_integrations/sidekiq.rb
|
77
77
|
- lib/scout_apm/background_worker.rb
|
78
78
|
- lib/scout_apm/bucket_name_splitter.rb
|
79
|
+
- lib/scout_apm/call_set.rb
|
79
80
|
- lib/scout_apm/capacity.rb
|
80
81
|
- lib/scout_apm/config.rb
|
81
82
|
- lib/scout_apm/context.rb
|
@@ -88,6 +89,8 @@ files:
|
|
88
89
|
- lib/scout_apm/framework_integrations/rails_3_or_4.rb
|
89
90
|
- lib/scout_apm/framework_integrations/ruby.rb
|
90
91
|
- lib/scout_apm/framework_integrations/sinatra.rb
|
92
|
+
- lib/scout_apm/histogram.rb
|
93
|
+
- lib/scout_apm/instruments/.DS_Store
|
91
94
|
- lib/scout_apm/instruments/action_controller_rails_2.rb
|
92
95
|
- lib/scout_apm/instruments/action_controller_rails_3_rails4.rb
|
93
96
|
- lib/scout_apm/instruments/active_record.rb
|
@@ -105,11 +108,20 @@ files:
|
|
105
108
|
- lib/scout_apm/instruments/rails_router.rb
|
106
109
|
- lib/scout_apm/instruments/redis.rb
|
107
110
|
- lib/scout_apm/instruments/sinatra.rb
|
111
|
+
- lib/scout_apm/job_record.rb
|
108
112
|
- lib/scout_apm/layaway.rb
|
109
113
|
- lib/scout_apm/layaway_file.rb
|
110
114
|
- lib/scout_apm/layer.rb
|
111
|
-
- lib/scout_apm/
|
115
|
+
- lib/scout_apm/layer_converters/converter_base.rb
|
116
|
+
- lib/scout_apm/layer_converters/depth_first_walker.rb
|
117
|
+
- lib/scout_apm/layer_converters/error_converter.rb
|
118
|
+
- lib/scout_apm/layer_converters/job_converter.rb
|
119
|
+
- lib/scout_apm/layer_converters/metric_converter.rb
|
120
|
+
- lib/scout_apm/layer_converters/request_queue_time_converter.rb
|
121
|
+
- lib/scout_apm/layer_converters/slow_job_converter.rb
|
122
|
+
- lib/scout_apm/layer_converters/slow_request_converter.rb
|
112
123
|
- lib/scout_apm/metric_meta.rb
|
124
|
+
- lib/scout_apm/metric_set.rb
|
113
125
|
- lib/scout_apm/metric_stats.rb
|
114
126
|
- lib/scout_apm/middleware.rb
|
115
127
|
- lib/scout_apm/platform_integrations/cloud_foundry.rb
|
@@ -117,12 +129,14 @@ files:
|
|
117
129
|
- lib/scout_apm/platform_integrations/server.rb
|
118
130
|
- lib/scout_apm/reporter.rb
|
119
131
|
- lib/scout_apm/request_manager.rb
|
120
|
-
- lib/scout_apm/request_queue_time.rb
|
121
132
|
- lib/scout_apm/serializers/app_server_load_serializer.rb
|
122
133
|
- lib/scout_apm/serializers/deploy_serializer.rb
|
123
134
|
- lib/scout_apm/serializers/directive_serializer.rb
|
135
|
+
- lib/scout_apm/serializers/jobs_serializer_to_json.rb
|
136
|
+
- lib/scout_apm/serializers/metrics_to_json_serializer.rb
|
124
137
|
- lib/scout_apm/serializers/payload_serializer.rb
|
125
138
|
- lib/scout_apm/serializers/payload_serializer_to_json.rb
|
139
|
+
- lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb
|
126
140
|
- lib/scout_apm/server_integrations/null.rb
|
127
141
|
- lib/scout_apm/server_integrations/passenger.rb
|
128
142
|
- lib/scout_apm/server_integrations/puma.rb
|
@@ -130,15 +144,18 @@ files:
|
|
130
144
|
- lib/scout_apm/server_integrations/thin.rb
|
131
145
|
- lib/scout_apm/server_integrations/unicorn.rb
|
132
146
|
- lib/scout_apm/server_integrations/webrick.rb
|
147
|
+
- lib/scout_apm/slow_item_set.rb
|
148
|
+
- lib/scout_apm/slow_job_policy.rb
|
149
|
+
- lib/scout_apm/slow_job_record.rb
|
133
150
|
- lib/scout_apm/slow_request_policy.rb
|
134
151
|
- lib/scout_apm/slow_transaction.rb
|
135
|
-
- lib/scout_apm/slow_transaction_set.rb
|
136
152
|
- lib/scout_apm/stack_item.rb
|
137
153
|
- lib/scout_apm/stackprof_tree_collapser.rb
|
138
154
|
- lib/scout_apm/store.rb
|
139
155
|
- lib/scout_apm/tracer.rb
|
140
156
|
- lib/scout_apm/tracked_request.rb
|
141
157
|
- lib/scout_apm/utils/active_record_metric_name.rb
|
158
|
+
- lib/scout_apm/utils/backtrace_parser.rb
|
142
159
|
- lib/scout_apm/utils/fake_stack_prof.rb
|
143
160
|
- lib/scout_apm/utils/installed_gems.rb
|
144
161
|
- lib/scout_apm/utils/null_logger.rb
|
@@ -154,10 +171,12 @@ files:
|
|
154
171
|
- test/unit/agent_test.rb
|
155
172
|
- test/unit/config_test.rb
|
156
173
|
- test/unit/environment_test.rb
|
174
|
+
- test/unit/histogram_test.rb
|
157
175
|
- test/unit/instruments/active_record_instruments_test.rb
|
158
176
|
- test/unit/layaway_test.rb
|
159
177
|
- test/unit/serializers/payload_serializer_test.rb
|
160
|
-
- test/unit/
|
178
|
+
- test/unit/slow_item_set_test.rb
|
179
|
+
- test/unit/slow_job_policy_test.rb
|
161
180
|
- test/unit/sql_sanitizer_test.rb
|
162
181
|
- test/unit/utils/active_record_metric_name_test.rb
|
163
182
|
homepage: https://github.com/scoutapp/scout_apm_ruby
|
@@ -175,9 +194,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
175
194
|
version: '0'
|
176
195
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
177
196
|
requirements:
|
178
|
-
- - "
|
197
|
+
- - ">"
|
179
198
|
- !ruby/object:Gem::Version
|
180
|
-
version:
|
199
|
+
version: 1.3.1
|
181
200
|
requirements: []
|
182
201
|
rubyforge_project: scout_apm
|
183
202
|
rubygems_version: 2.6.2
|
@@ -190,10 +209,12 @@ test_files:
|
|
190
209
|
- test/unit/agent_test.rb
|
191
210
|
- test/unit/config_test.rb
|
192
211
|
- test/unit/environment_test.rb
|
212
|
+
- test/unit/histogram_test.rb
|
193
213
|
- test/unit/instruments/active_record_instruments_test.rb
|
194
214
|
- test/unit/layaway_test.rb
|
195
215
|
- test/unit/serializers/payload_serializer_test.rb
|
196
|
-
- test/unit/
|
216
|
+
- test/unit/slow_item_set_test.rb
|
217
|
+
- test/unit/slow_job_policy_test.rb
|
197
218
|
- test/unit/sql_sanitizer_test.rb
|
198
219
|
- test/unit/utils/active_record_metric_name_test.rb
|
199
220
|
has_rdoc:
|
@@ -1,222 +0,0 @@
|
|
1
|
-
module ScoutApm
|
2
|
-
class LayerConverterBase
|
3
|
-
attr_reader :walker
|
4
|
-
attr_reader :request
|
5
|
-
attr_reader :root_layer
|
6
|
-
|
7
|
-
def initialize(request)
|
8
|
-
@request = request
|
9
|
-
@root_layer = request.root_layer
|
10
|
-
@walker = LayerDepthFirstWalker.new(root_layer)
|
11
|
-
end
|
12
|
-
|
13
|
-
# Scope is determined by the first Controller we hit. Most of the time
|
14
|
-
# there will only be 1 anyway. But if you have a controller that calls
|
15
|
-
# another controller method, we may pick that up:
|
16
|
-
# def update
|
17
|
-
# show
|
18
|
-
# render :update
|
19
|
-
# end
|
20
|
-
def scope_layer
|
21
|
-
@scope_layer ||= walker.walk do |layer|
|
22
|
-
if layer.type == "Controller"
|
23
|
-
break layer
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# Take a TrackedRequest and turn it into a hash of:
|
30
|
-
# MetricMeta => MetricStats
|
31
|
-
# This will do some merging of metrics as duplicate calls are seen.
|
32
|
-
class LayerMetricConverter < LayerConverterBase
|
33
|
-
def call
|
34
|
-
scope = scope_layer
|
35
|
-
|
36
|
-
# TODO: Track requests that never reach a Controller (for example, when
|
37
|
-
# Middleware decides to return rather than passing onward)
|
38
|
-
return {} unless scope
|
39
|
-
|
40
|
-
create_metrics
|
41
|
-
end
|
42
|
-
|
43
|
-
# Full metrics from this request. These get aggregated in Store for the
|
44
|
-
# overview metrics, or stored permanently in a SlowTransaction
|
45
|
-
# Some merging of metrics will happen here, so if a request calls the same
|
46
|
-
# ActiveRecord or View repeatedly, it'll get merged.
|
47
|
-
def create_metrics
|
48
|
-
metric_hash = Hash.new
|
49
|
-
|
50
|
-
walker.walk do |layer|
|
51
|
-
meta_options = if layer == scope_layer # We don't scope the controller under itself
|
52
|
-
{}
|
53
|
-
else
|
54
|
-
{:scope => scope_layer.legacy_metric_name}
|
55
|
-
end
|
56
|
-
|
57
|
-
# we don't need to use the full metric name for scoped metrics as we only display metrics aggregrated
|
58
|
-
# by type.
|
59
|
-
metric_name = meta_options.has_key?(:scope) ? layer.type : layer.legacy_metric_name
|
60
|
-
|
61
|
-
meta = MetricMeta.new(metric_name, meta_options)
|
62
|
-
metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
|
63
|
-
|
64
|
-
stat = metric_hash[meta]
|
65
|
-
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
66
|
-
end
|
67
|
-
metric_hash
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
class LayerErrorConverter < LayerConverterBase
|
72
|
-
def call
|
73
|
-
scope = scope_layer
|
74
|
-
|
75
|
-
# Should we mark a request as errored out if a middleware raises?
|
76
|
-
# How does that interact w/ a tool like Sentry or Honeybadger?
|
77
|
-
return {} unless scope
|
78
|
-
return {} unless request.error?
|
79
|
-
|
80
|
-
meta = MetricMeta.new("Errors/#{scope.legacy_metric_name}", {})
|
81
|
-
stat = MetricStats.new
|
82
|
-
stat.update!(1)
|
83
|
-
|
84
|
-
{ meta => stat }
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Take a TrackedRequest and turn it into a slow transaction if needed
|
89
|
-
# return a 2 element array, [ Slow Transaction or Nil , Hash of metrics to store ]
|
90
|
-
class LayerSlowTransactionConverter < LayerConverterBase
|
91
|
-
def call
|
92
|
-
scope = scope_layer
|
93
|
-
return [nil, {}] unless scope
|
94
|
-
|
95
|
-
policy = ScoutApm::Agent.instance.slow_request_policy.capture_type(root_layer.total_call_time)
|
96
|
-
if policy == ScoutApm::SlowRequestPolicy::CAPTURE_NONE
|
97
|
-
return [nil, {}]
|
98
|
-
end
|
99
|
-
|
100
|
-
# increment the slow transaction count if this is a slow transaction.
|
101
|
-
meta = MetricMeta.new("SlowTransaction/#{scope.legacy_metric_name}")
|
102
|
-
stat = MetricStats.new
|
103
|
-
stat.update!(1)
|
104
|
-
|
105
|
-
uri = request.annotations[:uri] || ""
|
106
|
-
|
107
|
-
metrics = create_metrics
|
108
|
-
# Disable stackprof output for now
|
109
|
-
stackprof = [] # request.stackprof
|
110
|
-
|
111
|
-
[
|
112
|
-
SlowTransaction.new(uri,
|
113
|
-
scope.legacy_metric_name,
|
114
|
-
root_layer.total_call_time,
|
115
|
-
metrics,
|
116
|
-
request.context,
|
117
|
-
root_layer.stop_time,
|
118
|
-
stackprof),
|
119
|
-
{ meta => stat }
|
120
|
-
]
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
# Full metrics from this request. These get aggregated in Store for the
|
125
|
-
# overview metrics, or stored permanently in a SlowTransaction
|
126
|
-
# Some merging of metrics will happen here, so if a request calls the same
|
127
|
-
# ActiveRecord or View repeatedly, it'll get merged.
|
128
|
-
def create_metrics
|
129
|
-
metric_hash = Hash.new
|
130
|
-
|
131
|
-
# Keep a list of subscopes, but only ever use the front one. The rest
|
132
|
-
# get pushed/popped in cases when we have many levels of subscopable
|
133
|
-
# layers. This lets us push/pop without otherwise keeping track very closely.
|
134
|
-
subscope_layers = []
|
135
|
-
|
136
|
-
walker.before do |layer|
|
137
|
-
if layer.subscopable?
|
138
|
-
subscope_layers.push(layer)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
walker.after do |layer|
|
143
|
-
if layer.subscopable?
|
144
|
-
subscope_layers.pop
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
walker.walk do |layer|
|
149
|
-
meta_options = if subscope_layers.first && layer != subscope_layers.first # Don't scope under ourself.
|
150
|
-
subscope_name = subscope_layers.first.legacy_metric_name
|
151
|
-
{:scope => subscope_name}
|
152
|
-
elsif layer == scope_layer # We don't scope the controller under itself
|
153
|
-
{}
|
154
|
-
else
|
155
|
-
{:scope => scope_layer.legacy_metric_name}
|
156
|
-
end
|
157
|
-
|
158
|
-
# Specific Metric
|
159
|
-
meta_options.merge!(:desc => layer.desc.to_s) if layer.desc
|
160
|
-
meta = MetricMeta.new(layer.legacy_metric_name, meta_options)
|
161
|
-
meta.extra.merge!(:backtrace => ScoutApm::SlowTransaction.backtrace_parser(layer.backtrace)) if layer.backtrace
|
162
|
-
metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
|
163
|
-
stat = metric_hash[meta]
|
164
|
-
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
165
|
-
|
166
|
-
# Merged Metric (no specifics, just sum up by type)
|
167
|
-
meta = MetricMeta.new("#{layer.type}/all")
|
168
|
-
metric_hash[meta] ||= MetricStats.new(false)
|
169
|
-
stat = metric_hash[meta]
|
170
|
-
stat.update!(layer.total_call_time, layer.total_exclusive_time)
|
171
|
-
end
|
172
|
-
|
173
|
-
metric_hash
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
class LayerDepthFirstWalker
|
178
|
-
attr_reader :root_layer
|
179
|
-
|
180
|
-
def initialize(root_layer)
|
181
|
-
@root_layer = root_layer
|
182
|
-
end
|
183
|
-
|
184
|
-
def before(&block)
|
185
|
-
@before_block = block
|
186
|
-
end
|
187
|
-
|
188
|
-
def after(&block)
|
189
|
-
@after_block = block
|
190
|
-
end
|
191
|
-
|
192
|
-
def walk(layer=root_layer, &block)
|
193
|
-
# Need to run this for the root layer the first time through.
|
194
|
-
if layer == root_layer
|
195
|
-
@before_block.call(layer) if @before_block
|
196
|
-
yield layer
|
197
|
-
@after_block.call(layer) if @after_block
|
198
|
-
end
|
199
|
-
|
200
|
-
layer.children.each do |child|
|
201
|
-
@before_block.call(child) if @before_block
|
202
|
-
yield child
|
203
|
-
walk(child, &block)
|
204
|
-
@after_block.call(child) if @after_block
|
205
|
-
end
|
206
|
-
nil
|
207
|
-
end
|
208
|
-
|
209
|
-
# Do this w/o using recursion, since it's prone to stack overflows
|
210
|
-
# Takes a block to run over each layer
|
211
|
-
# def walk
|
212
|
-
# layer_stack = [root_layer]
|
213
|
-
|
214
|
-
# while layer_stack.any?
|
215
|
-
# current_layer = layer_stack.pop
|
216
|
-
# current_layer.children.reverse.each { |child| layer_stack.push(child) }
|
217
|
-
|
218
|
-
# yield current_layer
|
219
|
-
# end
|
220
|
-
# end
|
221
|
-
end
|
222
|
-
end
|
@@ -1,57 +0,0 @@
|
|
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
|
-
# If we end up with a negative value, just bail out and don't report anything
|
24
|
-
return {} if queue_time < 0
|
25
|
-
|
26
|
-
meta = MetricMeta.new("QueueTime/Request", {:scope => scope_layer.legacy_metric_name})
|
27
|
-
stat = MetricStats.new(true)
|
28
|
-
stat.update!(queue_time)
|
29
|
-
|
30
|
-
{ meta => stat }
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
attr_reader :headers
|
36
|
-
|
37
|
-
# Looks through the possible headers with this data, and extracts the raw
|
38
|
-
# value of the header
|
39
|
-
# Returns nil if not found
|
40
|
-
def locate_timestamp
|
41
|
-
return nil unless headers
|
42
|
-
|
43
|
-
header = HEADERS.find { |candidate| headers[candidate] }
|
44
|
-
if header
|
45
|
-
data = headers[header]
|
46
|
-
data.to_s.gsub(/(t=|\.)/, '')
|
47
|
-
else
|
48
|
-
nil
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# Returns a timestamp in fractional seconds since epoch
|
53
|
-
def parse(time_string)
|
54
|
-
Time.at("#{time_string[0,10]}.#{time_string[10,13]}".to_f)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|