scout_apm 2.6.10 → 3.0.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (233) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -2
  3. data/.rubocop.yml +3 -11
  4. data/CHANGELOG.markdown +4 -362
  5. data/Gemfile +1 -14
  6. data/README.markdown +7 -52
  7. data/Rakefile +1 -0
  8. data/ext/allocations/allocations.c +1 -7
  9. data/ext/allocations/extconf.rb +0 -1
  10. data/ext/rusage/rusage.c +0 -26
  11. data/ext/stacks/extconf.rb +37 -0
  12. data/ext/stacks/scout_atomics.h +86 -0
  13. data/ext/stacks/stacks.c +811 -0
  14. data/lib/scout_apm/agent/logging.rb +69 -0
  15. data/lib/scout_apm/agent/reporting.rb +126 -0
  16. data/lib/scout_apm/agent.rb +259 -138
  17. data/lib/scout_apm/app_server_load.rb +15 -41
  18. data/lib/scout_apm/attribute_arranger.rb +3 -14
  19. data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -70
  20. data/lib/scout_apm/background_job_integrations/sidekiq.rb +24 -31
  21. data/lib/scout_apm/background_worker.rb +12 -23
  22. data/lib/scout_apm/capacity.rb +57 -0
  23. data/lib/scout_apm/config.rb +37 -206
  24. data/lib/scout_apm/context.rb +4 -20
  25. data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
  26. data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
  27. data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
  28. data/lib/scout_apm/deploy_integrations/capistrano_3.rb +88 -0
  29. data/lib/scout_apm/environment.rb +28 -42
  30. data/lib/scout_apm/fake_store.rb +0 -12
  31. data/lib/scout_apm/framework_integrations/rails_2.rb +1 -2
  32. data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +6 -17
  33. data/lib/scout_apm/framework_integrations/sinatra.rb +1 -1
  34. data/lib/scout_apm/histogram.rb +3 -12
  35. data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
  36. data/lib/scout_apm/instant/middleware.rb +54 -202
  37. data/lib/scout_apm/instant_reporting.rb +7 -7
  38. data/lib/scout_apm/instruments/.DS_Store +0 -0
  39. data/lib/scout_apm/instruments/action_controller_rails_2.rb +9 -15
  40. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +76 -124
  41. data/lib/scout_apm/instruments/active_record.rb +29 -324
  42. data/lib/scout_apm/instruments/delayed_job.rb +57 -0
  43. data/lib/scout_apm/instruments/elasticsearch.rb +6 -10
  44. data/lib/scout_apm/instruments/grape.rb +9 -12
  45. data/lib/scout_apm/instruments/http_client.rb +7 -14
  46. data/lib/scout_apm/instruments/influxdb.rb +6 -10
  47. data/lib/scout_apm/instruments/middleware_detailed.rb +11 -15
  48. data/lib/scout_apm/instruments/middleware_summary.rb +5 -11
  49. data/lib/scout_apm/instruments/mongoid.rb +8 -39
  50. data/lib/scout_apm/instruments/moped.rb +6 -11
  51. data/lib/scout_apm/instruments/net_http.rb +9 -27
  52. data/lib/scout_apm/instruments/percentile_sampler.rb +23 -42
  53. data/lib/scout_apm/instruments/process/process_cpu.rb +6 -11
  54. data/lib/scout_apm/instruments/process/process_memory.rb +12 -17
  55. data/lib/scout_apm/instruments/rails_router.rb +6 -12
  56. data/lib/scout_apm/instruments/redis.rb +6 -10
  57. data/lib/scout_apm/instruments/sinatra.rb +4 -5
  58. data/lib/scout_apm/job_record.rb +2 -4
  59. data/lib/scout_apm/layaway.rb +34 -88
  60. data/lib/scout_apm/layaway_file.rb +3 -13
  61. data/lib/scout_apm/layer.rb +60 -25
  62. data/lib/scout_apm/layer_converters/allocation_metric_converter.rb +6 -7
  63. data/lib/scout_apm/layer_converters/converter_base.rb +14 -203
  64. data/lib/scout_apm/layer_converters/depth_first_walker.rb +10 -22
  65. data/lib/scout_apm/layer_converters/error_converter.rb +8 -8
  66. data/lib/scout_apm/layer_converters/job_converter.rb +50 -37
  67. data/lib/scout_apm/layer_converters/metric_converter.rb +19 -18
  68. data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +13 -13
  69. data/lib/scout_apm/layer_converters/slow_job_converter.rb +116 -52
  70. data/lib/scout_apm/layer_converters/slow_request_converter.rb +120 -51
  71. data/lib/scout_apm/metric_meta.rb +5 -0
  72. data/lib/scout_apm/metric_set.rb +1 -9
  73. data/lib/scout_apm/metric_stats.rb +8 -7
  74. data/lib/scout_apm/middleware.rb +9 -7
  75. data/lib/scout_apm/reporter.rb +24 -71
  76. data/lib/scout_apm/request_histograms.rb +0 -12
  77. data/lib/scout_apm/request_manager.rb +7 -5
  78. data/lib/scout_apm/scored_item_set.rb +0 -7
  79. data/lib/scout_apm/serializers/app_server_load_serializer.rb +0 -4
  80. data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
  81. data/lib/scout_apm/serializers/directive_serializer.rb +0 -4
  82. data/lib/scout_apm/serializers/payload_serializer.rb +4 -11
  83. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +16 -35
  84. data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +1 -2
  85. data/lib/scout_apm/server_integrations/passenger.rb +1 -1
  86. data/lib/scout_apm/server_integrations/puma.rb +2 -5
  87. data/lib/scout_apm/slow_job_policy.rb +13 -25
  88. data/lib/scout_apm/slow_job_record.rb +4 -13
  89. data/lib/scout_apm/slow_request_policy.rb +13 -25
  90. data/lib/scout_apm/slow_transaction.rb +5 -25
  91. data/lib/scout_apm/store.rb +32 -99
  92. data/lib/scout_apm/trace_compactor.rb +312 -0
  93. data/lib/scout_apm/tracer.rb +31 -35
  94. data/lib/scout_apm/tracked_request.rb +95 -262
  95. data/lib/scout_apm/utils/active_record_metric_name.rb +13 -88
  96. data/lib/scout_apm/utils/backtrace_parser.rb +4 -7
  97. data/lib/scout_apm/utils/fake_stacks.rb +87 -0
  98. data/lib/scout_apm/utils/installed_gems.rb +3 -7
  99. data/lib/scout_apm/utils/klass_helper.rb +2 -8
  100. data/lib/scout_apm/utils/null_logger.rb +13 -0
  101. data/lib/scout_apm/utils/sql_sanitizer.rb +5 -16
  102. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +0 -7
  103. data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +0 -6
  104. data/lib/scout_apm/utils/unique_id.rb +0 -27
  105. data/lib/scout_apm/version.rb +2 -1
  106. data/lib/scout_apm.rb +25 -84
  107. data/scout_apm.gemspec +3 -17
  108. data/test/test_helper.rb +3 -57
  109. data/test/unit/agent_test.rb +54 -1
  110. data/test/unit/background_job_integrations/sidekiq_test.rb +3 -0
  111. data/test/unit/config_test.rb +12 -25
  112. data/test/unit/context_test.rb +4 -4
  113. data/test/unit/histogram_test.rb +4 -25
  114. data/test/unit/ignored_uris_test.rb +1 -1
  115. data/test/unit/instruments/active_record_instruments_test.rb +5 -0
  116. data/test/unit/layaway_test.rb +2 -62
  117. data/test/unit/serializers/payload_serializer_test.rb +15 -43
  118. data/test/unit/slow_request_policy_test.rb +6 -15
  119. data/test/unit/sql_sanitizer_test.rb +6 -53
  120. data/test/unit/store_test.rb +4 -73
  121. data/test/unit/utils/active_record_metric_name_test.rb +5 -59
  122. data/test/unit/utils/backtrace_parser_test.rb +1 -6
  123. data/tester.rb +53 -0
  124. metadata +28 -229
  125. data/.travis.yml +0 -26
  126. data/Guardfile +0 -43
  127. data/gems/README.md +0 -28
  128. data/gems/octoshark.gemfile +0 -4
  129. data/gems/rails3.gemfile +0 -5
  130. data/gems/rails4.gemfile +0 -4
  131. data/gems/rails5.gemfile +0 -4
  132. data/gems/rails6.gemfile +0 -4
  133. data/lib/scout_apm/agent/exit_handler.rb +0 -65
  134. data/lib/scout_apm/agent/preconditions.rb +0 -81
  135. data/lib/scout_apm/agent_context.rb +0 -261
  136. data/lib/scout_apm/auto_instrument/instruction_sequence.rb +0 -31
  137. data/lib/scout_apm/auto_instrument/layer.rb +0 -23
  138. data/lib/scout_apm/auto_instrument/parser.rb +0 -27
  139. data/lib/scout_apm/auto_instrument/rails.rb +0 -175
  140. data/lib/scout_apm/auto_instrument.rb +0 -5
  141. data/lib/scout_apm/background_job_integrations/legacy_sneakers.rb +0 -55
  142. data/lib/scout_apm/background_job_integrations/que.rb +0 -134
  143. data/lib/scout_apm/background_job_integrations/resque.rb +0 -88
  144. data/lib/scout_apm/background_job_integrations/shoryuken.rb +0 -124
  145. data/lib/scout_apm/background_job_integrations/sneakers.rb +0 -87
  146. data/lib/scout_apm/background_recorder.rb +0 -48
  147. data/lib/scout_apm/db_query_metric_set.rb +0 -97
  148. data/lib/scout_apm/db_query_metric_stats.rb +0 -102
  149. data/lib/scout_apm/debug.rb +0 -37
  150. data/lib/scout_apm/detailed_trace.rb +0 -217
  151. data/lib/scout_apm/error.rb +0 -27
  152. data/lib/scout_apm/error_service/error_buffer.rb +0 -39
  153. data/lib/scout_apm/error_service/error_record.rb +0 -211
  154. data/lib/scout_apm/error_service/ignored_exceptions.rb +0 -66
  155. data/lib/scout_apm/error_service/middleware.rb +0 -32
  156. data/lib/scout_apm/error_service/notifier.rb +0 -33
  157. data/lib/scout_apm/error_service/payload.rb +0 -47
  158. data/lib/scout_apm/error_service/periodic_work.rb +0 -17
  159. data/lib/scout_apm/error_service/railtie.rb +0 -11
  160. data/lib/scout_apm/error_service/sidekiq.rb +0 -80
  161. data/lib/scout_apm/error_service.rb +0 -32
  162. data/lib/scout_apm/extensions/config.rb +0 -87
  163. data/lib/scout_apm/extensions/transaction_callback_payload.rb +0 -74
  164. data/lib/scout_apm/git_revision.rb +0 -59
  165. data/lib/scout_apm/instrument_manager.rb +0 -88
  166. data/lib/scout_apm/instruments/action_view.rb +0 -141
  167. data/lib/scout_apm/instruments/http.rb +0 -48
  168. data/lib/scout_apm/instruments/memcached.rb +0 -43
  169. data/lib/scout_apm/instruments/resque.rb +0 -39
  170. data/lib/scout_apm/instruments/samplers.rb +0 -11
  171. data/lib/scout_apm/layer_children_set.rb +0 -86
  172. data/lib/scout_apm/layer_converters/database_converter.rb +0 -70
  173. data/lib/scout_apm/layer_converters/find_layer_by_type.rb +0 -38
  174. data/lib/scout_apm/layer_converters/histograms.rb +0 -15
  175. data/lib/scout_apm/layer_converters/trace_converter.rb +0 -184
  176. data/lib/scout_apm/limited_layer.rb +0 -126
  177. data/lib/scout_apm/logger.rb +0 -158
  178. data/lib/scout_apm/periodic_work.rb +0 -47
  179. data/lib/scout_apm/rack.rb +0 -26
  180. data/lib/scout_apm/remote/message.rb +0 -27
  181. data/lib/scout_apm/remote/recorder.rb +0 -57
  182. data/lib/scout_apm/remote/router.rb +0 -49
  183. data/lib/scout_apm/remote/server.rb +0 -60
  184. data/lib/scout_apm/reporting.rb +0 -143
  185. data/lib/scout_apm/serializers/db_query_serializer_to_json.rb +0 -15
  186. data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +0 -21
  187. data/lib/scout_apm/synchronous_recorder.rb +0 -30
  188. data/lib/scout_apm/tasks/doctor.rb +0 -75
  189. data/lib/scout_apm/tasks/support.rb +0 -22
  190. data/lib/scout_apm/transaction.rb +0 -13
  191. data/lib/scout_apm/transaction_time_consumed.rb +0 -51
  192. data/lib/scout_apm/utils/gzip_helper.rb +0 -24
  193. data/lib/scout_apm/utils/marshal_logging.rb +0 -90
  194. data/lib/scout_apm/utils/numbers.rb +0 -14
  195. data/lib/scout_apm/utils/scm.rb +0 -14
  196. data/lib/tasks/doctor.rake +0 -11
  197. data/test/tmp/README.md +0 -17
  198. data/test/unit/agent_context_test.rb +0 -15
  199. data/test/unit/auto_instrument/assignments-instrumented.rb +0 -31
  200. data/test/unit/auto_instrument/assignments.rb +0 -31
  201. data/test/unit/auto_instrument/controller-ast.txt +0 -57
  202. data/test/unit/auto_instrument/controller-instrumented.rb +0 -49
  203. data/test/unit/auto_instrument/controller.rb +0 -49
  204. data/test/unit/auto_instrument/rescue_from-instrumented.rb +0 -13
  205. data/test/unit/auto_instrument/rescue_from.rb +0 -13
  206. data/test/unit/auto_instrument_test.rb +0 -54
  207. data/test/unit/db_query_metric_set_test.rb +0 -67
  208. data/test/unit/db_query_metric_stats_test.rb +0 -113
  209. data/test/unit/error_service/error_buffer_test.rb +0 -25
  210. data/test/unit/error_service/ignored_exceptions_test.rb +0 -49
  211. data/test/unit/extensions/periodic_callbacks_test.rb +0 -58
  212. data/test/unit/extensions/transaction_callbacks_test.rb +0 -58
  213. data/test/unit/fake_store_test.rb +0 -10
  214. data/test/unit/git_revision_test.rb +0 -15
  215. data/test/unit/instruments/active_record_test.rb +0 -40
  216. data/test/unit/instruments/net_http_test.rb +0 -27
  217. data/test/unit/instruments/percentile_sampler_test.rb +0 -133
  218. data/test/unit/layer_children_set_test.rb +0 -97
  219. data/test/unit/layer_converters/depth_first_walker_test.rb +0 -70
  220. data/test/unit/layer_converters/metric_converter_test.rb +0 -22
  221. data/test/unit/layer_converters/stubs.rb +0 -33
  222. data/test/unit/limited_layer_test.rb +0 -53
  223. data/test/unit/logger_test.rb +0 -69
  224. data/test/unit/remote/test_message.rb +0 -13
  225. data/test/unit/remote/test_router.rb +0 -33
  226. data/test/unit/remote/test_server.rb +0 -15
  227. data/test/unit/request_histograms_test.rb +0 -17
  228. data/test/unit/tracer_test.rb +0 -76
  229. data/test/unit/tracked_request_test.rb +0 -71
  230. data/test/unit/transaction_test.rb +0 -14
  231. data/test/unit/transaction_time_consumed_test.rb +0 -46
  232. data/test/unit/utils/numbers_test.rb +0 -15
  233. data/test/unit/utils/scm.rb +0 -17
@@ -5,42 +5,30 @@ module ScoutApm
5
5
 
6
6
  def initialize(root_layer)
7
7
  @root_layer = root_layer
8
-
9
- @on_blocks = []
10
- @before_blocks = []
11
- @after_blocks = []
12
8
  end
13
9
 
14
10
  def before(&block)
15
- @before_blocks << block
11
+ @before_block = block
16
12
  end
17
13
 
18
14
  def after(&block)
19
- @after_blocks << block
20
- end
21
-
22
- def on(&block)
23
- @on_blocks << block
15
+ @after_block = block
24
16
  end
25
17
 
26
- def walk(layer=root_layer)
18
+ def walk(layer=root_layer, &block)
27
19
  # Need to run this for the root layer the first time through.
28
20
  if layer == root_layer
29
- @before_blocks.each{|b| b.call(layer) }
30
- @on_blocks.each{|b| b.call(layer) }
21
+ @before_block.call(layer) if @before_block
22
+ yield layer
23
+ @after_block.call(layer) if @after_block
31
24
  end
32
25
 
33
26
  layer.children.each do |child|
34
- @before_blocks.each{|b| b.call(child) }
35
- @on_blocks.each{|b| b.call(child) }
36
- walk(child)
37
- @after_blocks.each{|b| b.call(child) }
27
+ @before_block.call(child) if @before_block
28
+ yield child
29
+ walk(child, &block)
30
+ @after_block.call(child) if @after_block
38
31
  end
39
-
40
- if layer == root_layer
41
- @after_blocks.each{|b| b.call(layer) }
42
- end
43
-
44
32
  nil
45
33
  end
46
34
  end
@@ -1,19 +1,19 @@
1
1
  module ScoutApm
2
2
  module LayerConverters
3
3
  class ErrorConverter < ConverterBase
4
- def record!
4
+ def call
5
+ scope = scope_layer
6
+
5
7
  # Should we mark a request as errored out if a middleware raises?
6
8
  # How does that interact w/ a tool like Sentry or Honeybadger?
7
- return unless scope_layer
8
- return unless request.error?
9
+ return {} unless scope
10
+ return {} unless request.error?
9
11
 
10
- meta = MetricMeta.new("Errors/#{scope_layer.legacy_metric_name}", {})
12
+ meta = MetricMeta.new("Errors/#{scope.legacy_metric_name}", {})
11
13
  stat = MetricStats.new
12
14
  stat.update!(1)
13
- metrics = { meta => stat }
14
-
15
- @store.track!(metrics)
16
- metrics # this result must be returned so it can be accessed by transaction callback extensions
15
+
16
+ { meta => stat }
17
17
  end
18
18
  end
19
19
  end
@@ -10,20 +10,54 @@
10
10
  module ScoutApm
11
11
  module LayerConverters
12
12
  class JobConverter < ConverterBase
13
- attr_reader :meta_options
14
-
15
- def register_hooks(walker)
13
+ def call
16
14
  return unless request.job?
17
15
 
18
- super
16
+ JobRecord.new(
17
+ queue_layer.name,
18
+ job_layer.name,
19
+ job_layer.total_call_time,
20
+ job_layer.total_exclusive_time,
21
+ errors,
22
+ create_metrics
23
+ )
24
+ end
25
+
26
+ def queue_layer
27
+ @queue_layer ||= find_first_layer_of_type("Queue")
28
+ end
29
+
30
+ def job_layer
31
+ @job_layer ||= find_first_layer_of_type("Job")
32
+ end
33
+
34
+ def errors
35
+ if request.error?
36
+ 1
37
+ else
38
+ 0
39
+ end
40
+ end
41
+
42
+ def find_first_layer_of_type(layer_type)
43
+ walker.walk do |layer|
44
+ return layer if layer.type == layer_type
45
+ end
46
+ end
47
+
48
+ # Full metrics from this request. These get aggregated in Store for the
49
+ # overview metrics, or stored permanently in a SlowTransaction
50
+ # Some merging of metrics will happen here, so if a request calls the same
51
+ # ActiveRecord or View repeatedly, it'll get merged.
52
+ def create_metrics
53
+ metric_hash = Hash.new
19
54
 
20
- @metrics = Hash.new
21
- @meta_options = {:scope => layer_finder.job.legacy_metric_name}
55
+ meta_options = {:scope => job_layer.legacy_metric_name}
22
56
 
23
- walker.on do |layer|
24
- next if layer == layer_finder.job
25
- next if layer == layer_finder.queue
26
- next if skip_layer?(layer)
57
+ walker.walk do |layer|
58
+ next if layer == job_layer
59
+ next if layer == queue_layer
60
+ next if layer.annotations[:ignorable]
27
61
 
28
62
  # we don't need to use the full metric name for scoped metrics as we
29
63
  # only display metrics aggregrated by type, just use "ActiveRecord"
@@ -31,41 +65,20 @@ module ScoutApm
31
65
  metric_name = layer.type
32
66
 
33
67
  meta = MetricMeta.new(metric_name, meta_options)
34
- @metrics[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
68
+ metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
35
69
 
36
- stat = @metrics[meta]
70
+ stat = metric_hash[meta]
37
71
  stat.update!(layer.total_call_time, layer.total_exclusive_time)
38
72
  end
39
73
 
40
- end
41
-
42
- def record!
43
- return unless request.job?
44
-
45
- errors = request.error? ? 1 : 0
46
- add_latency_metric!
47
-
48
- record = JobRecord.new(
49
- layer_finder.queue.name,
50
- layer_finder.job.name,
51
- layer_finder.job.total_call_time,
52
- layer_finder.job.total_exclusive_time,
53
- errors,
54
- @metrics
55
- )
56
-
57
- @store.track_job!(record)
58
- record # this result must be returned so it can be accessed by transaction callback extensions
59
- end
60
-
61
- # This isn't stored as a specific layer, so grabbing it doesn't use the
62
- # walker callbacks
63
- def add_latency_metric!
74
+ # Add the latency metric, which wasn't stored as a distinct layer
64
75
  latency = request.annotations[:queue_latency] || 0
65
76
  meta = MetricMeta.new("Latency", meta_options)
66
77
  stat = MetricStats.new
67
78
  stat.update!(latency)
68
- @metrics[meta] = stat
79
+ metric_hash[meta] = stat
80
+
81
+ metric_hash
69
82
  end
70
83
  end
71
84
  end
@@ -1,22 +1,27 @@
1
1
  # Take a TrackedRequest and turn it into a hash of:
2
2
  # MetricMeta => MetricStats
3
-
4
- # Full metrics from this request. These get aggregated in Store for the
5
- # overview metrics, or stored permanently in a SlowTransaction
6
- # Some merging of metrics will happen here, so if a request calls the same
7
- # ActiveRecord or View repeatedly, it'll get merged.
8
3
  module ScoutApm
9
4
  module LayerConverters
10
5
  class MetricConverter < ConverterBase
11
- def register_hooks(walker)
12
- super
6
+ def call
7
+ scope = scope_layer
8
+
9
+ # TODO: Track requests that never reach a Controller (for example, when
10
+ # Middleware decides to return rather than passing onward)
11
+ return {} unless scope
13
12
 
14
- @metrics = {}
13
+ create_metrics
14
+ end
15
15
 
16
- return unless scope_layer
16
+ # Full metrics from this request. These get aggregated in Store for the
17
+ # overview metrics, or stored permanently in a SlowTransaction
18
+ # Some merging of metrics will happen here, so if a request calls the same
19
+ # ActiveRecord or View repeatedly, it'll get merged.
20
+ def create_metrics
21
+ metric_hash = Hash.new
17
22
 
18
- walker.on do |layer|
19
- next if skip_layer?(layer)
23
+ walker.walk do |layer|
24
+ next if layer.annotations[:ignorable]
20
25
 
21
26
  meta_options = if layer == scope_layer # We don't scope the controller under itself
22
27
  {}
@@ -29,16 +34,12 @@ module ScoutApm
29
34
  metric_name = meta_options.has_key?(:scope) ? layer.type : layer.legacy_metric_name
30
35
 
31
36
  meta = MetricMeta.new(metric_name, meta_options)
32
- @metrics[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
37
+ metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
33
38
 
34
- stat = @metrics[meta]
39
+ stat = metric_hash[meta]
35
40
  stat.update!(layer.total_call_time, layer.total_exclusive_time)
36
41
  end
37
- end
38
-
39
- def record!
40
- @store.track!(@metrics)
41
- @metrics # this result must be returned so it can be accessed by transaction callback extensions
42
+ metric_hash
42
43
  end
43
44
  end
44
45
  end
@@ -4,38 +4,38 @@ module ScoutApm
4
4
 
5
5
  HEADERS = %w(X-Queue-Start X-Request-Start X-QUEUE-START X-REQUEST-START x-queue-start x-request-start)
6
6
 
7
- def headers
8
- request.headers
7
+ # Headers is a hash of request headers. In Rails, request.headers would be appropriate
8
+ def initialize(request)
9
+ super(request)
10
+ @headers = request.headers
9
11
  end
10
12
 
11
- def record!
12
- return unless request.web?
13
-
14
- return unless headers
13
+ def call
14
+ return {} unless headers
15
15
 
16
16
  raw_start = locate_timestamp
17
- return unless raw_start
17
+ return {} unless raw_start
18
18
 
19
19
  parsed_start = parse(raw_start)
20
- return unless parsed_start
20
+ return {} unless parsed_start
21
21
 
22
22
  request_start = root_layer.start_time
23
23
  queue_time = (request_start - parsed_start).to_f
24
24
 
25
25
  # If we end up with a negative value, just bail out and don't report anything
26
- return if queue_time < 0
26
+ return {} if queue_time < 0
27
27
 
28
28
  meta = MetricMeta.new("QueueTime/Request", {:scope => scope_layer.legacy_metric_name})
29
29
  stat = MetricStats.new(true)
30
30
  stat.update!(queue_time)
31
- metrics = { meta => stat }
32
-
33
- @store.track!(metrics)
34
- metrics # this result must be returned so it can be accessed by transaction callback extensions
31
+
32
+ { meta => stat }
35
33
  end
36
34
 
37
35
  private
38
36
 
37
+ attr_reader :headers
38
+
39
39
  # Looks through the possible headers with this data, and extracts the raw
40
40
  # value of the header
41
41
  # Returns nil if not found
@@ -1,49 +1,42 @@
1
- # Uses a different workflow than normal metrics. We ignore the shared walk of
2
- # the layer tree, and instead wait until we're sure we even want to do any
3
- # work. Only then do we go realize all the SlowJobRecord & metrics associated.
4
- #
5
1
  module ScoutApm
6
2
  module LayerConverters
7
3
  class SlowJobConverter < ConverterBase
8
- ###################
9
- # Converter API #
10
- ###################
11
- def record!
12
- return nil unless request.job?
13
- @points = context.slow_job_policy.score(request)
14
-
15
- # Let the store know we're here, and if it wants our data, it will call
16
- # back into #call
17
- @store.track_slow_job!(self)
4
+ def initialize(*)
5
+ @backtraces = []
6
+ super
7
+
8
+ # After call to super, so @request is populated
9
+ @points = if request.job?
10
+ ScoutApm::Agent.instance.slow_job_policy.score(request)
11
+ else
12
+ -1
13
+ end
14
+ end
18
15
 
19
- nil # not returning anything in the layer results ... not used
16
+ def name
17
+ request.unique_name
20
18
  end
21
19
 
22
- #####################
23
- # ScoreItemSet API #
24
- #####################
25
- def name; request.unique_name; end
26
- def score; @points; end
20
+ def score
21
+ @points
22
+ end
27
23
 
28
- # Called by the set to force this to actually be created.
29
24
  def call
30
25
  return nil unless request.job?
31
- return nil unless layer_finder.queue
32
- return nil unless layer_finder.job
26
+ return nil unless queue_layer
27
+ return nil unless job_layer
33
28
 
34
- context.slow_job_policy.stored!(request)
29
+ ScoutApm::Agent.instance.slow_job_policy.stored!(request)
35
30
 
36
31
  # record the change in memory usage
37
- mem_delta = ScoutApm::Instruments::Process::ProcessMemory.new(context).rss_to_mb(request.capture_mem_delta!)
32
+ mem_delta = ScoutApm::Instruments::Process::ProcessMemory.rss_to_mb(request.capture_mem_delta!)
38
33
 
39
34
  timing_metrics, allocation_metrics = create_metrics
40
-
41
35
  unless ScoutApm::Instruments::Allocations::ENABLED
42
36
  allocation_metrics = {}
43
37
  end
44
38
 
45
39
  SlowJobRecord.new(
46
- context,
47
40
  queue_layer.name,
48
41
  job_layer.name,
49
42
  root_layer.stop_time,
@@ -54,48 +47,119 @@ module ScoutApm
54
47
  allocation_metrics,
55
48
  mem_delta,
56
49
  job_layer.total_allocations,
57
- score,
58
- limited?,
59
- span_trace
60
- )
50
+ score)
61
51
  end
62
52
 
63
- def create_metrics
64
- # Create a new walker, and wire up the subscope stuff
65
- walker = LayerConverters::DepthFirstWalker.new(self.root_layer)
66
- register_hooks(walker)
53
+ def queue_layer
54
+ @queue_layer ||= find_first_layer_of_type("Queue")
55
+ end
67
56
 
57
+ def job_layer
58
+ @job_layer ||= find_first_layer_of_type("Job")
59
+ end
60
+
61
+ def create_metrics
68
62
  metric_hash = Hash.new
69
63
  allocation_metric_hash = Hash.new
70
64
 
71
- walker.on do |layer|
72
- next if skip_layer?(layer)
65
+ # Keep a list of subscopes, but only ever use the front one. The rest
66
+ # get pushed/popped in cases when we have many levels of subscopable
67
+ # layers. This lets us push/pop without otherwise keeping track very closely.
68
+ subscope_layers = []
69
+
70
+ walker.before do |layer|
71
+ if layer.subscopable?
72
+ subscope_layers.push(layer)
73
+ end
74
+ end
75
+
76
+ walker.after do |layer|
77
+ if layer.subscopable?
78
+ subscope_layers.pop
79
+ end
80
+ end
81
+
82
+ walker.walk do |layer|
83
+ # Sometimes we start capturing a layer without knowing if we really
84
+ # want to make an entry for it. See ActiveRecord instrumentation for
85
+ # an example. We start capturing before we know if a query is cached
86
+ # or not, and want to skip any cached queries.
87
+ next if layer.annotations[:ignorable]
73
88
 
74
89
  # The queue_layer is useful to capture for other reasons, but doesn't
75
90
  # create a MetricMeta/Stat of its own
76
91
  next if layer == queue_layer
77
92
 
78
- store_specific_metric(layer, metric_hash, allocation_metric_hash)
79
- store_aggregate_metric(layer, metric_hash, allocation_metric_hash)
80
- end
81
-
82
- # And now run through the walk we just defined
83
- walker.walk
93
+ meta_options = if subscope_layers.first && layer != subscope_layers.first # Don't scope under ourself.
94
+ subscope_name = subscope_layers.first.legacy_metric_name
95
+ {:scope => subscope_name}
96
+ elsif layer == job_layer # We don't scope the controller under itself
97
+ {}
98
+ else
99
+ {:scope => job_layer.legacy_metric_name}
100
+ end
101
+
102
+ # Specific Metric
103
+ meta_options.merge!(:desc => layer.desc.to_s) if layer.desc
104
+ meta = MetricMeta.new(layer.legacy_metric_name, meta_options)
105
+ meta.extra.merge!(layer.annotations)
106
+
107
+ if layer.backtrace
108
+ bt = ScoutApm::Utils::BacktraceParser.new(layer.backtrace).call
109
+ if bt.any? # we could walk thru the call stack and not find in-app code
110
+ meta.backtrace = bt
111
+ # Why not just call meta.backtrace and call it done? The walker could access a later later that generates the same MetricMeta but doesn't have a backtrace. This could be
112
+ # lost in the metric_hash if it is replaced by the new key.
113
+ @backtraces << meta
114
+ else
115
+ ScoutApm::Agent.instance.logger.debug { "Unable to capture an app-specific backtrace for #{meta.inspect}\n#{layer.backtrace}" }
116
+ end
117
+ end
118
+
119
+ metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
120
+ allocation_metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
121
+ stat = metric_hash[meta]
122
+ stat.update!(layer.total_call_time, layer.total_exclusive_time)
123
+ stat = allocation_metric_hash[meta]
124
+ stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
125
+
126
+ # Merged Metric (no specifics, just sum up by type)
127
+ meta = MetricMeta.new("#{layer.type}/all")
128
+ metric_hash[meta] ||= MetricStats.new(false)
129
+ allocation_metric_hash[meta] ||= MetricStats.new(false)
130
+ stat = metric_hash[meta]
131
+ stat.update!(layer.total_call_time, layer.total_exclusive_time)
132
+ stat = allocation_metric_hash[meta]
133
+ stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
134
+
135
+ stat.add_traces(layer.traces.as_json)
136
+
137
+ # Debug logging for scoutprof traces
138
+ if ScoutApm::Agent.instance.config.value('profile')
139
+ if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
140
+ ScoutApm::Agent.instance.logger.debug do
141
+ traces_inspect = layer.traces.inspect
142
+ "****** Slow Request #{layer.type} Traces (#{layer.name}, tet: #{layer.total_exclusive_time}, tct: #{layer.total_call_time}), total raw traces: #{layer.traces.cube.total_count}, total clean traces: #{layer.traces.total_count}, skipped gc: #{layer.traces.skipped_in_gc}, skipped handler: #{layer.traces.skipped_in_handler}, skipped registered #{layer.traces.skipped_in_job_registered}, skipped not_running #{layer.traces.skipped_in_not_running}:\n#{traces_inspect}"
143
+ end
144
+ end
145
+ else
146
+ if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
147
+ ScoutApm::Agent.instance.logger.debug "****** Slow Request #{layer.type} Traces: Scoutprof is not enabled"
148
+ end
149
+ end
150
+ end # walker.walk
84
151
 
85
152
  metric_hash = attach_backtraces(metric_hash)
86
153
  allocation_metric_hash = attach_backtraces(allocation_metric_hash)
87
154
 
88
- [metric_hash, allocation_metric_hash]
155
+ [metric_hash,allocation_metric_hash]
89
156
  end
90
157
 
91
- def skip_layer?(layer); super(layer) || layer == queue_layer; end
92
- def queue_layer; layer_finder.queue; end
93
- def job_layer; layer_finder.job; end
94
-
95
- def span_trace
96
- ScoutApm::LayerConverters::TraceConverter.
97
- new(@context, @request, @layer_finder, @store).
98
- call
158
+ def attach_backtraces(metric_hash)
159
+ @backtraces.each do |meta_with_backtrace|
160
+ metric_hash.keys.find { |k| k == meta_with_backtrace }.backtrace = meta_with_backtrace.backtrace
161
+ end
162
+ metric_hash
99
163
  end
100
164
  end
101
165
  end