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
@@ -1,48 +1,46 @@
1
1
  module ScoutApm
2
2
  module LayerConverters
3
3
  class SlowRequestConverter < ConverterBase
4
- ###################
5
- # Converter API #
6
- ###################
7
- def record!
8
- return nil unless request.web?
9
- @points = context.slow_request_policy.score(request)
10
-
11
- # Let the store know we're here, and if it wants our data, it will call
12
- # back into #call
13
- @store.track_slow_transaction!(self)
14
-
15
- nil # not returning anything in the layer results ... not used
4
+ def initialize(*)
5
+ @backtraces = [] # An Array of MetricMetas that have a backtrace
6
+ super
7
+
8
+ # After call to super, so @request is populated
9
+ @points = if request.web?
10
+ ScoutApm::Agent.instance.slow_request_policy.score(request)
11
+ else
12
+ -1
13
+ end
16
14
  end
17
15
 
18
- #####################
19
- # ScoreItemSet API #
20
- #####################
21
- def name; request.unique_name; end
22
- def score; @points; end
16
+ def name
17
+ request.unique_name
18
+ end
19
+
20
+ def score
21
+ @points
22
+ end
23
23
 
24
24
  # Unconditionally attempts to convert this into a SlowTransaction object.
25
25
  # Can return nil if the request didn't have any scope_layer.
26
26
  def call
27
- return nil unless request.web?
28
- return nil unless scope_layer
27
+ scope = scope_layer
28
+ return nil unless scope
29
29
 
30
- context.slow_request_policy.stored!(request)
30
+ ScoutApm::Agent.instance.slow_request_policy.stored!(request)
31
31
 
32
32
  # record the change in memory usage
33
- mem_delta = ScoutApm::Instruments::Process::ProcessMemory.new(context).rss_to_mb(@request.capture_mem_delta!)
33
+ mem_delta = ScoutApm::Instruments::Process::ProcessMemory.rss_to_mb(@request.capture_mem_delta!)
34
34
 
35
35
  uri = request.annotations[:uri] || ""
36
36
 
37
37
  timing_metrics, allocation_metrics = create_metrics
38
-
39
38
  unless ScoutApm::Instruments::Allocations::ENABLED
40
39
  allocation_metrics = {}
41
40
  end
42
41
 
43
- SlowTransaction.new(context,
44
- uri,
45
- scope_layer.legacy_metric_name,
42
+ SlowTransaction.new(uri,
43
+ scope.legacy_metric_name,
46
44
  root_layer.total_call_time,
47
45
  timing_metrics,
48
46
  allocation_metrics,
@@ -51,48 +49,119 @@ module ScoutApm
51
49
  [], # stackprof, now unused.
52
50
  mem_delta,
53
51
  root_layer.total_allocations,
54
- @points,
55
- limited?,
56
- span_trace)
52
+ @points)
53
+ end
54
+
55
+ # Iterates over the TrackedRequest's MetricMetas that have backtraces and attaches each to correct MetricMeta in the Metric Hash.
56
+ def attach_backtraces(metric_hash)
57
+ @backtraces.each do |meta_with_backtrace|
58
+ metric_hash.keys.find { |k| k == meta_with_backtrace }.backtrace = meta_with_backtrace.backtrace
59
+ end
60
+ metric_hash
57
61
  end
58
62
 
59
63
  # Full metrics from this request. These get stored permanently in a SlowTransaction.
60
64
  # Some merging of metrics will happen here, so if a request calls the same
61
65
  # ActiveRecord or View repeatedly, it'll get merged.
62
- #
66
+ #
63
67
  # This returns a 2-element of Metric Hashes (the first element is timing metrics, the second element is allocation metrics)
64
68
  def create_metrics
65
- # Create a new walker, and wire up the subscope stuff
66
- walker = LayerConverters::DepthFirstWalker.new(self.root_layer)
67
- register_hooks(walker)
68
-
69
69
  metric_hash = Hash.new
70
70
  allocation_metric_hash = Hash.new
71
71
 
72
- walker.on do |layer|
73
- next if skip_layer?(layer)
74
- store_specific_metric(layer, metric_hash, allocation_metric_hash)
75
- store_aggregate_metric(layer, metric_hash, allocation_metric_hash)
72
+ # Keep a list of subscopes, but only ever use the front one. The rest
73
+ # get pushed/popped in cases when we have many levels of subscopable
74
+ # layers. This lets us push/pop without otherwise keeping track very closely.
75
+ subscope_layers = []
76
+
77
+ walker.before do |layer|
78
+ if layer.subscopable?
79
+ subscope_layers.push(layer)
80
+ end
76
81
  end
77
82
 
78
- # And now run through the walk we just defined
79
- walker.walk
83
+ walker.after do |layer|
84
+ if layer.subscopable?
85
+ subscope_layers.pop
86
+ end
87
+ end
88
+
89
+ walker.walk do |layer|
90
+ # Sometimes we start capturing a layer without knowing if we really
91
+ # want to make an entry for it. See ActiveRecord instrumentation for
92
+ # an example. We start capturing before we know if a query is cached
93
+ # or not, and want to skip any cached queries.
94
+ if layer.annotations[:ignorable]
95
+ next
96
+ end
97
+
98
+ meta_options = if subscope_layers.first && layer != subscope_layers.first # Don't scope under ourself.
99
+ subscope_name = subscope_layers.first.legacy_metric_name
100
+ {:scope => subscope_name}
101
+ elsif layer == scope_layer # We don't scope the controller under itself
102
+ {}
103
+ else
104
+ {:scope => scope_layer.legacy_metric_name}
105
+ end
106
+
107
+ # Specific Metric
108
+ meta_options.merge!(:desc => layer.desc.to_s) if layer.desc
109
+ meta = MetricMeta.new(layer.legacy_metric_name, meta_options)
110
+ meta.extra.merge!(layer.annotations)
111
+ if layer.backtrace
112
+ bt = ScoutApm::Utils::BacktraceParser.new(layer.backtrace).call
113
+ if bt.any? # we could walk thru the call stack and not find in-app code
114
+ meta.backtrace = bt
115
+ # Why not just call meta.backtrace and call it done? The walker
116
+ # could access a later later that generates the same MetricMeta
117
+ # but doesn't have a backtrace. This could be lost in the
118
+ # metric_hash if it is replaced by the new key.
119
+ @backtraces << meta
120
+ else
121
+ ScoutApm::Agent.instance.logger.debug { "Unable to capture an app-specific backtrace for #{meta.inspect}\n#{layer.backtrace}" }
122
+ end
123
+ end
124
+ metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
125
+ allocation_metric_hash[meta] ||= MetricStats.new( meta_options.has_key?(:scope) )
126
+ # timing
127
+ stat = metric_hash[meta]
128
+ stat.update!(layer.total_call_time, layer.total_exclusive_time)
129
+ stat.add_traces(layer.traces.as_json)
130
+
131
+ # Debug logging for scoutprof traces
132
+ if ScoutApm::Agent.instance.config.value('profile')
133
+ if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
134
+ ScoutApm::Agent.instance.logger.debug do
135
+ traces_inspect = layer.traces.inspect
136
+ "****** 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}"
137
+ end
138
+ end
139
+ else
140
+ if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
141
+ ScoutApm::Agent.instance.logger.debug "****** Slow Request #{layer.type} Traces: Scoutprof is not enabled"
142
+ end
143
+ end
144
+
145
+ # allocations
146
+ stat = allocation_metric_hash[meta]
147
+ stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
148
+
149
+ # Merged Metric (no specifics, just sum up by type)
150
+ meta = MetricMeta.new("#{layer.type}/all")
151
+ metric_hash[meta] ||= MetricStats.new(false)
152
+ allocation_metric_hash[meta] ||= MetricStats.new(false)
153
+ # timing
154
+ stat = metric_hash[meta]
155
+ stat.update!(layer.total_call_time, layer.total_exclusive_time)
156
+ # allocations
157
+ stat = allocation_metric_hash[meta]
158
+ stat.update!(layer.total_allocations, layer.total_exclusive_allocations)
159
+ end
80
160
 
81
161
  metric_hash = attach_backtraces(metric_hash)
82
162
  allocation_metric_hash = attach_backtraces(allocation_metric_hash)
83
163
 
84
- [metric_hash, allocation_metric_hash]
85
- end
86
-
87
- ###########################################################
88
- # Also create a new style trace. This is not a good #
89
- # spot for this long term, but fixes an issue for now. #
90
- ###########################################################
91
-
92
- def span_trace
93
- ScoutApm::LayerConverters::TraceConverter.
94
- new(@context, @request, @layer_finder, @store).
95
- call
164
+ [metric_hash,allocation_metric_hash]
96
165
  end
97
166
  end
98
167
  end
@@ -33,6 +33,11 @@ class MetricMeta
33
33
  !!(metric_name =~ /\A(Controller|Job)\//)
34
34
  end
35
35
 
36
+ # To avoid conflicts with different JSON libaries
37
+ def to_json(*a)
38
+ %Q[{"metric_id":#{metric_id || 'null'},"metric_name":#{metric_name.to_json},"scope":#{scope.to_json || 'null'}}]
39
+ end
40
+
36
41
  def ==(o)
37
42
  self.eql?(o)
38
43
  end
@@ -1,8 +1,6 @@
1
1
  module ScoutApm
2
2
  class MetricSet
3
- # We can't aggregate a handful of things like samplers (CPU, Memory), or
4
- # Controller, and Percentiles so pass through these metrics directly
5
- #
3
+ # We can't aggregate CPU, Memory, Capacity, or Controller, so pass through these metrics directly
6
4
  # TODO: Figure out a way to not have this duplicate what's in Samplers, and also on server's ingest
7
5
  PASSTHROUGH_METRICS = ["CPU", "Memory", "Instance", "Controller", "SlowTransaction", "Percentile", "Job"]
8
6
 
@@ -51,11 +49,5 @@ module ScoutApm
51
49
  @combine_in_progress = false
52
50
  self
53
51
  end
54
-
55
-
56
- def eql?(other)
57
- metrics == other.metrics
58
- end
59
- alias :== :eql?
60
52
  end
61
53
  end
@@ -9,6 +9,7 @@ class MetricStats
9
9
  attr_accessor :sum_of_squares
10
10
  attr_accessor :queue
11
11
  attr_accessor :latency
12
+ attr_accessor :traces
12
13
 
13
14
  def initialize(scoped = false)
14
15
  @scoped = scoped
@@ -18,6 +19,7 @@ class MetricStats
18
19
  self.min_call_time = 0.0
19
20
  self.max_call_time = 0.0
20
21
  self.sum_of_squares = 0.0
22
+ self.traces = []
21
23
  end
22
24
 
23
25
  # Note, that you must include exclusive_time if you wish to set
@@ -39,6 +41,10 @@ class MetricStats
39
41
  self
40
42
  end
41
43
 
44
+ def add_traces(traces)
45
+ self.traces += Array(traces)
46
+ end
47
+
42
48
  # combines data from another MetricStats object
43
49
  def combine!(other)
44
50
  self.call_count += other.call_count
@@ -47,17 +53,12 @@ class MetricStats
47
53
  self.min_call_time = other.min_call_time if self.min_call_time.zero? or other.min_call_time < self.min_call_time
48
54
  self.max_call_time = other.max_call_time if other.max_call_time > self.max_call_time
49
55
  self.sum_of_squares += other.sum_of_squares
56
+ self.traces = Array(self.traces) + Array(other.traces)
50
57
  self
51
58
  end
52
59
 
53
60
  def as_json
54
- json_attributes = [
55
- :call_count,
56
- :max_call_time,
57
- :min_call_time,
58
- :total_call_time,
59
- :total_exclusive_time,
60
- ]
61
+ json_attributes = [:call_count, :total_call_time, :total_exclusive_time, :min_call_time, :max_call_time, :traces]
61
62
  ScoutApm::AttributeArranger.call(self, json_attributes)
62
63
  end
63
64
  end
@@ -5,10 +5,8 @@ module ScoutApm
5
5
  def initialize(app)
6
6
  @app = app
7
7
  @attempts = 0
8
- # @enabled = ScoutApm::Agent.instance.context.apm_enabled?
9
- # XXX: Figure out if this middleware should even know
10
- @enabled = true
11
- @started = ScoutApm::Agent.instance.context.started? && ScoutApm::Agent.instance.background_worker_running?
8
+ @enabled = ScoutApm::Agent.instance.apm_enabled?
9
+ @started = ScoutApm::Agent.instance.started? && ScoutApm::Agent.instance.background_worker_running?
12
10
  end
13
11
 
14
12
  # If we get a web request in, then we know we're running in some sort of app server
@@ -23,10 +21,14 @@ module ScoutApm
23
21
 
24
22
  def attempt_to_start_agent
25
23
  @attempts += 1
26
- ScoutApm::Agent.instance.start
27
- @started = ScoutApm::Agent.instance.context.started? && ScoutApm::Agent.instance.background_worker_running?
24
+ ScoutApm::Agent.instance.start(:skip_app_server_check => true)
25
+ ScoutApm::Agent.instance.start_background_worker
26
+ @started = ScoutApm::Agent.instance.started? && ScoutApm::Agent.instance.background_worker_running?
28
27
  rescue => e
29
- ScoutApm::Agent.instance.context.logger.info("Failed to start via Middleware: #{e.message}\n\t#{e.backtrace.join("\n\t")}")
28
+ # Can't be sure of any logging here, so fall back to ENV var and STDOUT
29
+ if ENV["SCOUT_LOG_LEVEL"] == "debug"
30
+ STDOUT.puts "Failed to start via Middleware: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
31
+ end
30
32
  end
31
33
  end
32
34
  end
@@ -2,60 +2,46 @@ require 'openssl'
2
2
 
3
3
  module ScoutApm
4
4
  class Reporter
5
+ CA_FILE = File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] )
5
6
  VERIFY_MODE = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
6
7
 
8
+ attr_reader :config
9
+ attr_reader :logger
7
10
  attr_reader :type
8
- attr_reader :context
9
11
  attr_reader :instant_key
10
12
 
11
- def initialize(context, type, instant_key=nil)
12
- @context = context
13
+ def initialize(type = :checkin, config=Agent.instance.config, logger=Agent.instance.logger, instant_key=nil)
14
+ @config = config
15
+ @logger = logger
13
16
  @type = type
14
17
  @instant_key = instant_key
15
18
  end
16
19
 
17
- def config
18
- context.config
19
- end
20
-
21
- def logger
22
- context.logger
23
- end
24
-
25
- # The fully serialized string payload to be sent
20
+ # TODO: Parse & return a real response object, not the HTTP Response object
26
21
  def report(payload, headers = {})
27
- hosts = determine_hosts
22
+ # Some posts (typically ones under development) bypass the ingestion pipeline and go directly to the webserver. They use direct_host instead of host
23
+ hosts = [:deploy_hook, :instant_trace].include?(type) ? config.value('direct_host') : config.value('host')
28
24
 
29
- if config.value('compress_payload')
30
- original_payload_size = payload.length
31
-
32
- payload, compression_headers = compress_payload(payload)
33
- headers.merge!(compression_headers)
34
-
35
- compress_payload_size = payload.length
36
- logger.debug("Original Size: #{original_payload_size} Compressed Size: #{compress_payload_size}")
25
+ Array(hosts).each do |host|
26
+ full_uri = uri(host)
27
+ response = post(full_uri, payload, headers)
28
+ unless response && response.is_a?(Net::HTTPSuccess)
29
+ logger.warn "Error on checkin to #{full_uri.to_s}: #{response.inspect}"
30
+ end
37
31
  end
38
-
39
- logger.info("Posting payload to #{hosts.inspect}")
40
- post_payload(hosts, payload, headers)
41
32
  end
42
33
 
43
34
  def uri(host)
44
- encoded_app_name = CGI.escape(context.environment.application_name)
45
- key = config.value('key')
46
-
47
35
  case type
48
36
  when :checkin
49
- URI.parse("#{host}/apps/checkin.scout?key=#{key}&name=#{encoded_app_name}")
37
+ URI.parse("#{host}/apps/checkin.scout?key=#{config.value('key')}&name=#{CGI.escape(Environment.instance.application_name)}")
50
38
  when :app_server_load
51
- URI.parse("#{host}/apps/app_server_load.scout?key=#{key}&name=#{encoded_app_name}")
39
+ URI.parse("#{host}/apps/app_server_load.scout?key=#{config.value('key')}&name=#{CGI.escape(Environment.instance.application_name)}")
52
40
  when :deploy_hook
53
- URI.parse("#{host}/apps/deploy.scout?key=#{key}&name=#{encoded_app_name}")
41
+ URI.parse("#{host}/apps/deploy.scout?key=#{config.value('key')}&name=#{CGI.escape(config.value('name'))}")
54
42
  when :instant_trace
55
- URI.parse("#{host}/apps/instant_trace.scout?key=#{key}&name=#{encoded_app_name}&instant_key=#{instant_key}")
56
- when :errors
57
- URI.parse("#{host}/apps/error.scout?key=#{key}&name=#{encoded_app_name}")
58
- end.tap { |u| logger.debug("Posting to #{u}") }
43
+ URI.parse("#{host}/apps/instant_trace.scout?key=#{config.value('key')}&name=#{CGI.escape(config.value('name'))}&instant_key=#{instant_key}")
44
+ end.tap{|u| logger.debug("Posting to #{u.to_s}")}
59
45
  end
60
46
 
61
47
  def can_report?
@@ -93,7 +79,7 @@ module ScoutApm
93
79
  logger.debug "got response: #{response.inspect}"
94
80
  case response
95
81
  when Net::HTTPSuccess, Net::HTTPNotModified
96
- logger.debug "#{type} OK"
82
+ logger.debug "/#{type} OK"
97
83
  when Net::HTTPBadRequest
98
84
  logger.warn "/#{type} FAILED: The Account Key [#{config.value('key')}] is invalid."
99
85
  when Net::HTTPUnprocessableEntity
@@ -109,7 +95,7 @@ module ScoutApm
109
95
 
110
96
  # Headers passed up with all API requests.
111
97
  def default_http_headers
112
- { "Agent-Hostname" => context.environment.hostname,
98
+ { "Agent-Hostname" => ScoutApm::Environment.instance.hostname,
113
99
  "Content-Type" => "application/octet-stream",
114
100
  "Agent-Version" => ScoutApm::VERSION,
115
101
  }
@@ -120,46 +106,13 @@ module ScoutApm
120
106
  # Net::HTTP::Proxy returns a regular Net::HTTP class if the first argument (host) is nil.
121
107
  def http(url)
122
108
  proxy_uri = URI.parse(config.value('proxy').to_s)
123
- http = Net::HTTP::Proxy(proxy_uri.host,
124
- proxy_uri.port,
125
- proxy_uri.user,
126
- proxy_uri.password).new(url.host, url.port)
109
+ http = Net::HTTP::Proxy(proxy_uri.host,proxy_uri.port,proxy_uri.user,proxy_uri.password).new(url.host, url.port)
127
110
  if url.is_a?(URI::HTTPS)
128
111
  http.use_ssl = true
129
- http.ca_file = config.value("ssl_cert_file")
112
+ http.ca_file = CA_FILE
130
113
  http.verify_mode = VERIFY_MODE
131
114
  end
132
115
  http
133
116
  end
134
-
135
- def compress_payload(payload)
136
- [
137
- ScoutApm::Utils::GzipHelper.new.deflate(payload),
138
- { 'Content-Encoding' => 'gzip' }
139
- ]
140
- end
141
-
142
- # Some posts (typically ones under development) bypass the ingestion
143
- # pipeline and go directly to the webserver. They use direct_host instead
144
- # of host
145
- def determine_hosts
146
- if [:deploy_hook, :instant_trace].include?(type)
147
- config.value('direct_host')
148
- elsif [:errors].include?(type)
149
- config.value('errors_host')
150
- else
151
- config.value('host')
152
- end
153
- end
154
-
155
- def post_payload(hosts, payload, headers)
156
- Array(hosts).each do |host|
157
- full_uri = uri(host)
158
- response = post(full_uri, payload, headers)
159
- unless response && response.is_a?(Net::HTTPSuccess)
160
- logger.warn "Error on checkin to #{full_uri}: #{response.inspect}"
161
- end
162
- end
163
- end
164
117
  end
165
118
  end
@@ -22,14 +22,6 @@ module ScoutApm
22
22
  @histograms.keys.each { |n| yield n }
23
23
  end
24
24
 
25
- def as_json
26
- Hash[
27
- @histograms.map{ |key, histogram|
28
- [key, histogram.as_json]
29
- }
30
- ]
31
- end
32
-
33
25
  def add(item, value)
34
26
  @histograms[item].add(value)
35
27
  end
@@ -47,10 +39,6 @@ module ScoutApm
47
39
  initialize_histograms_hash
48
40
  end
49
41
 
50
- def raw(item)
51
- @histograms[item]
52
- end
53
-
54
42
  def initialize_histograms_hash
55
43
  @histograms = Hash.new { |h, k| h[k] = NumericHistogram.new(histogram_size) }
56
44
  end
@@ -11,7 +11,7 @@ module ScoutApm
11
11
  def self.find
12
12
  req = Thread.current[:scout_request]
13
13
 
14
- if req && (req.stopping? || req.recorded?)
14
+ if req && req.recorded?
15
15
  nil
16
16
  else
17
17
  req
@@ -19,11 +19,13 @@ module ScoutApm
19
19
  end
20
20
 
21
21
  # Create a new TrackedRequest object for this thread
22
- # XXX: Figure out who is in charge of creating a `FakeStore` - previously was here
23
22
  def self.create
24
- agent_context = ScoutApm::Agent.instance.context
25
- store = agent_context.store
26
- Thread.current[:scout_request] = TrackedRequest.new(agent_context, store)
23
+ store = if ScoutApm::Agent.instance.apm_enabled?
24
+ ScoutApm::Agent.instance.store
25
+ else
26
+ ScoutApm::FakeStore.new
27
+ end
28
+ Thread.current[:scout_request] = TrackedRequest.new(store)
27
29
  end
28
30
  end
29
31
  end
@@ -63,12 +63,6 @@ module ScoutApm
63
63
  end
64
64
  end
65
65
 
66
- # Equal to another set only if exactly the same set of items is inside
67
- def eql?(other)
68
- items == other.items
69
- end
70
-
71
- alias :== :eql?
72
66
 
73
67
  private
74
68
 
@@ -81,6 +75,5 @@ module ScoutApm
81
75
  items[new_item.name] = [new_item.score, new_item.call]
82
76
  end
83
77
  end
84
-
85
78
  end
86
79
  end
@@ -5,10 +5,6 @@ module ScoutApm
5
5
  class AppServerLoadSerializer
6
6
  def self.serialize(data)
7
7
  Marshal.dump(data)
8
- rescue
9
- ScoutApm::Agent.instance.logger.info("Failed Marshalling AppServerLoad")
10
- ScoutApm::Agent.instance.logger.info(ScoutApm::Utils::MarshalLogging.new(data).dive) rescue nil
11
- raise
12
8
  end
13
9
 
14
10
  def self.deserialize(data)
@@ -0,0 +1,16 @@
1
+ # Serialize & deserialize deploy data up to the APM server
2
+ module ScoutApm
3
+ module Serializers
4
+ class DeploySerializer
5
+ HTTP_HEADERS = {'Content-Type' => 'application/x-www-form-urlencoded'}
6
+
7
+ def self.serialize(data)
8
+ URI.encode_www_form(data)
9
+ end
10
+
11
+ def self.deserialize(data)
12
+ Marshal.load(data)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -5,10 +5,6 @@ module ScoutApm
5
5
  class DirectiveSerializer
6
6
  def self.serialize(data)
7
7
  Marshal.dump(data)
8
- rescue
9
- ScoutApm::Agent.instance.logger.info("Failed Marshalling Directive")
10
- ScoutApm::Agent.instance.logger.info(ScoutApm::Utils::MarshalLogging.new(data).dive) rescue nil
11
- raise
12
8
  end
13
9
 
14
10
  def self.deserialize(data)
@@ -2,9 +2,9 @@
2
2
  module ScoutApm
3
3
  module Serializers
4
4
  class PayloadSerializer
5
- def self.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics, traces)
6
- if ScoutApm::Agent.instance.context.config.value("report_format") == 'json'
7
- ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics, traces)
5
+ def self.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs)
6
+ if ScoutApm::Agent.instance.config.value("report_format") == 'json'
7
+ ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs)
8
8
  else
9
9
  metadata = metadata.dup
10
10
  metadata.default = nil
@@ -15,14 +15,7 @@ module ScoutApm
15
15
  :metrics => metrics,
16
16
  :slow_transactions => slow_transactions,
17
17
  :jobs => jobs,
18
- :slow_jobs => slow_jobs,
19
-
20
- # as_json returns a ruby object. Since it's not a simple
21
- # array, use this to maintain compatibility with json
22
- # payloads. At this point, the marshal code branch is
23
- # very rarely used anyway.
24
- :histograms => HistogramsSerializerToJson.new(histograms).as_json,
25
- :db_query_metrics => db_query_metrics)
18
+ :slow_jobs => slow_jobs)
26
19
  end
27
20
  end
28
21