scout_apm 1.4.6 → 1.5.0.pre
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 +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
|