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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +9 -0
  3. data/lib/scout_apm/agent/reporting.rb +8 -6
  4. data/lib/scout_apm/agent.rb +10 -6
  5. data/lib/scout_apm/background_job_integrations/sidekiq.rb +23 -11
  6. data/lib/scout_apm/call_set.rb +61 -0
  7. data/lib/scout_apm/config.rb +2 -1
  8. data/lib/scout_apm/environment.rb +12 -7
  9. data/lib/scout_apm/histogram.rb +124 -0
  10. data/lib/scout_apm/instruments/.DS_Store +0 -0
  11. data/lib/scout_apm/instruments/action_controller_rails_2.rb +1 -0
  12. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +1 -0
  13. data/lib/scout_apm/instruments/delayed_job.rb +1 -0
  14. data/lib/scout_apm/instruments/process/process_memory.rb +1 -1
  15. data/lib/scout_apm/instruments/sinatra.rb +1 -1
  16. data/lib/scout_apm/job_record.rb +76 -0
  17. data/lib/scout_apm/layaway.rb +4 -1
  18. data/lib/scout_apm/layaway_file.rb +4 -4
  19. data/lib/scout_apm/layer.rb +14 -4
  20. data/lib/scout_apm/layer_converters/converter_base.rb +30 -0
  21. data/lib/scout_apm/layer_converters/depth_first_walker.rb +36 -0
  22. data/lib/scout_apm/layer_converters/error_converter.rb +20 -0
  23. data/lib/scout_apm/layer_converters/job_converter.rb +84 -0
  24. data/lib/scout_apm/layer_converters/metric_converter.rb +45 -0
  25. data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +60 -0
  26. data/lib/scout_apm/layer_converters/slow_job_converter.rb +88 -0
  27. data/lib/scout_apm/layer_converters/slow_request_converter.rb +111 -0
  28. data/lib/scout_apm/metric_meta.rb +9 -0
  29. data/lib/scout_apm/metric_set.rb +44 -0
  30. data/lib/scout_apm/reporter.rb +12 -5
  31. data/lib/scout_apm/serializers/jobs_serializer_to_json.rb +28 -0
  32. data/lib/scout_apm/serializers/metrics_to_json_serializer.rb +54 -0
  33. data/lib/scout_apm/serializers/payload_serializer.rb +5 -3
  34. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +9 -4
  35. data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +29 -0
  36. data/lib/scout_apm/slow_item_set.rb +80 -0
  37. data/lib/scout_apm/slow_job_policy.rb +29 -0
  38. data/lib/scout_apm/slow_job_record.rb +33 -0
  39. data/lib/scout_apm/slow_transaction.rb +0 -22
  40. data/lib/scout_apm/stackprof_tree_collapser.rb +7 -8
  41. data/lib/scout_apm/store.rb +55 -35
  42. data/lib/scout_apm/tracked_request.rb +67 -10
  43. data/lib/scout_apm/utils/active_record_metric_name.rb +13 -0
  44. data/lib/scout_apm/utils/backtrace_parser.rb +31 -0
  45. data/lib/scout_apm/utils/fake_stack_prof.rb +1 -1
  46. data/lib/scout_apm/utils/sql_sanitizer.rb +6 -0
  47. data/lib/scout_apm/version.rb +1 -1
  48. data/lib/scout_apm.rb +25 -5
  49. data/test/unit/histogram_test.rb +93 -0
  50. data/test/unit/serializers/payload_serializer_test.rb +5 -5
  51. data/test/unit/{slow_transaction_set_test.rb → slow_item_set_test.rb} +8 -8
  52. data/test/unit/slow_job_policy_test.rb +55 -0
  53. metadata +30 -9
  54. data/lib/scout_apm/layer_converter.rb +0 -222
  55. data/lib/scout_apm/request_queue_time.rb +0 -57
  56. 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/slow_transaction_set'
3
+ require 'scout_apm/slow_item_set'
4
4
  require 'scout_apm/slow_transaction'
5
5
 
6
- class SlowTransactionSetTest < Minitest::Test
6
+ class SlowItemSetTest < Minitest::Test
7
7
  def test_adding_to_empty_set
8
- set = ScoutApm::SlowTransactionSet.new(3, 1)
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::SlowTransactionSet.new(3, 1)
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::SlowTransactionSet.new(max_size, 1)
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::SlowTransactionSet.new(max_size, 1)
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::SlowTransactionSet.new(max_size, 1)
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::SlowTransactionSet.new(max_size, fair)
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.6
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-04 00:00:00.000000000 Z
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/layer_converter.rb
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/slow_transaction_set_test.rb
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: '0'
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/slow_transaction_set_test.rb
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