scout_apm 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +11 -0
  3. data/lib/scout_apm.rb +21 -0
  4. data/lib/scout_apm/agent.rb +21 -11
  5. data/lib/scout_apm/agent/reporting.rb +9 -5
  6. data/lib/scout_apm/app_server_load.rb +7 -3
  7. data/lib/scout_apm/config.rb +9 -4
  8. data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
  9. data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
  10. data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
  11. data/lib/scout_apm/deploy_integrations/capistrano_3.rb +82 -0
  12. data/lib/scout_apm/environment.rb +15 -2
  13. data/lib/scout_apm/instruments/action_controller_rails_2.rb +2 -2
  14. data/lib/scout_apm/instruments/action_controller_rails_3.rb +24 -12
  15. data/lib/scout_apm/instruments/active_record.rb +1 -2
  16. data/lib/scout_apm/instruments/mongoid.rb +1 -1
  17. data/lib/scout_apm/instruments/moped.rb +1 -1
  18. data/lib/scout_apm/instruments/net_http.rb +1 -1
  19. data/lib/scout_apm/instruments/process/process_cpu.rb +3 -3
  20. data/lib/scout_apm/instruments/sinatra.rb +1 -1
  21. data/lib/scout_apm/reporter.rb +21 -2
  22. data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
  23. data/lib/scout_apm/server_integrations/passenger.rb +2 -0
  24. data/lib/scout_apm/server_integrations/puma.rb +1 -2
  25. data/lib/scout_apm/server_integrations/rainbows.rb +1 -1
  26. data/lib/scout_apm/server_integrations/unicorn.rb +1 -0
  27. data/lib/scout_apm/slow_transaction.rb +42 -29
  28. data/lib/scout_apm/stackprof_tree_collapser.rb +95 -0
  29. data/lib/scout_apm/store.rb +15 -7
  30. data/lib/scout_apm/tracer.rb +14 -3
  31. data/lib/scout_apm/utils/fake_stack_prof.rb +40 -0
  32. data/lib/scout_apm/version.rb +1 -1
  33. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 359e7ebbd4d2be52175b519c7eed26801f332cf6
4
- data.tar.gz: d54ba2ef21fc61b38e54136637bd97391ba0c26b
3
+ metadata.gz: 1c2f4d07b1f636737317fc8200121bc301c8dd53
4
+ data.tar.gz: 59f8d5df43b92220fc8e482e04ab33a7b3a82337
5
5
  SHA512:
6
- metadata.gz: d5c7c714ffa299176340a4b6d1c62f248106840565245c1ecef1696e103218f3c91dcd2e31d3f0164bf97d125f36f6312c4f4db1c3875dc6c4a2a9b3d12c3420
7
- data.tar.gz: 45cabecb512ef84af5ab618d227a787d92f64e62874352c770d70a8b8ccf23facf992b30a0ce7ded385b291bf44f11858a5cf3b5f5544d63766fa34fb504ab7b
6
+ metadata.gz: 546f2ea5ac44f2a1100f8b84c99e9fa7f9493b0ec91a104ff3926e02c0373c340651c7276a27a50be72a18eafc9a389b39a6c621cf6e333d699f30e0b80beb25
7
+ data.tar.gz: dd3ed781983a7f0e3e9522eac7439a40b7af2f1ec457c7c51fe68731745868dd630fd8c3f6ec6242b1e9822d02095cb950d740f02a655faa7293190a9b464f89
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,14 @@
1
+ # 0.9.1
2
+
3
+ Big set of features getting merged in for this release.
4
+
5
+ * StackProf support! Get visibility into your Ruby code. On Ruby 2.1+, just
6
+ add `gem 'stackprof'` to your Gemfile.
7
+ * Deploy tracking! Compare your application's response time, throughput and
8
+ error rate between different releases. At the bottom of your Capistrano
9
+ deploy.rb file, add `require 'scout_apm'` and we do the rest.
10
+ * Log message overhaul. Removed a lot of the noise, clarified messages.
11
+
1
12
  # 0.9.0
2
13
 
3
14
  * Come out of alpha, and release a beta version.
data/lib/scout_apm.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  module ScoutApm
2
2
  end
3
3
 
4
+ #####################################
5
+ # Ruby StdLibrary Requires
6
+ #####################################
4
7
  require 'cgi'
5
8
  require 'logger'
6
9
  require 'net/http'
@@ -10,6 +13,18 @@ require 'socket'
10
13
  require 'yaml'
11
14
  require 'thread'
12
15
 
16
+ #####################################
17
+ # Gem Requires
18
+ #####################################
19
+ begin
20
+ require 'stackprof'
21
+ rescue LoadError
22
+ require 'scout_apm/utils/fake_stack_prof'
23
+ end
24
+
25
+ #####################################
26
+ # Internal Requires
27
+ #####################################
13
28
  require 'scout_apm/version'
14
29
 
15
30
  require 'scout_apm/server_integrations/passenger'
@@ -25,6 +40,9 @@ require 'scout_apm/framework_integrations/rails_3_or_4'
25
40
  require 'scout_apm/framework_integrations/sinatra'
26
41
  require 'scout_apm/framework_integrations/ruby'
27
42
 
43
+ require 'scout_apm/deploy_integrations/capistrano_3'
44
+ #require 'scout_apm/deploy_integrations/capistrano_2'
45
+
28
46
  require 'scout_apm/instruments/net_http'
29
47
  require 'scout_apm/instruments/moped'
30
48
  require 'scout_apm/instruments/mongoid'
@@ -41,6 +59,7 @@ require 'scout_apm/utils/sql_sanitizer'
41
59
  require 'scout_apm/utils/null_logger'
42
60
  require 'scout_apm/utils/installed_gems'
43
61
  require 'scout_apm/utils/time'
62
+
44
63
  require 'scout_apm/config'
45
64
  require 'scout_apm/environment'
46
65
  require 'scout_apm/agent'
@@ -56,12 +75,14 @@ require 'scout_apm/stack_item'
56
75
  require 'scout_apm/store'
57
76
  require 'scout_apm/tracer'
58
77
  require 'scout_apm/context'
78
+ require 'scout_apm/stackprof_tree_collapser'
59
79
  require 'scout_apm/slow_transaction'
60
80
  require 'scout_apm/capacity'
61
81
 
62
82
  require 'scout_apm/serializers/payload_serializer'
63
83
  require 'scout_apm/serializers/directive_serializer'
64
84
  require 'scout_apm/serializers/app_server_load_serializer'
85
+ require 'scout_apm/serializers/deploy_serializer'
65
86
 
66
87
  if defined?(Rails) and Rails.respond_to?(:version) and Rails.version >= '3'
67
88
  module ScoutApm
@@ -78,6 +78,11 @@ module ScoutApm
78
78
  init_logger
79
79
  logger.info "Attempting to start Scout Agent [#{ScoutApm::VERSION}] on [#{environment.hostname}]"
80
80
 
81
+ if environment.deploy_integration
82
+ logger.info "Starting monitoring for [#{environment.deploy_integration.name}]]."
83
+ return environment.deploy_integration.install
84
+ end
85
+
81
86
  return false unless preconditions_met?
82
87
 
83
88
  @started = true
@@ -93,16 +98,16 @@ module ScoutApm
93
98
 
94
99
  app_server_load_hook
95
100
 
96
- if start_background_worker? # TODO: Clarify name. This is not the path that unicorn workers take....
97
- # This branch fires only on non-forking servers, directly starts the
98
- # background thread and then requests are served.
101
+ # start_background_worker? is true on non-forking servers, and directly
102
+ # starts the background worker. On forking servers, a server-specific
103
+ # hook is inserted to start the background worker after forking.
104
+ if start_background_worker?
99
105
  start_background_worker
100
106
  handle_exit
101
107
  logger.info "Scout Agent [#{ScoutApm::VERSION}] Initialized"
102
108
  else
103
- # This branch fires on.... master only? of forking servers
104
- logger.debug "Not starting worker thread. Will start worker loops after forking."
105
109
  environment.app_server_integration.install
110
+ logger.info "Scout Agent [#{ScoutApm::VERSION}] loaded in [#{environment.app_server}] master process. Monitoring will start after server forks its workers."
106
111
  end
107
112
  end
108
113
 
@@ -121,7 +126,7 @@ module ScoutApm
121
126
  logger.debug "Exit handler not supported" and return if exit_handler_unsupported?
122
127
 
123
128
  at_exit do
124
- logger.debug "Shutdown!"
129
+ logger.info "Shutting down Scout Agent"
125
130
  # MRI 1.9 bug drops exit codes.
126
131
  # http://bugs.ruby-lang.org/issues/5218
127
132
  if environment.ruby_19?
@@ -160,12 +165,11 @@ module ScoutApm
160
165
  # Creates the worker thread. The worker thread is a loop that runs continuously. It sleeps for +Agent#period+ and when it wakes,
161
166
  # processes data, either saving it to disk or reporting to Scout.
162
167
  def start_background_worker
163
- logger.debug "Creating worker thread."
168
+ logger.info "Initializing worker thread."
164
169
  @background_worker = ScoutApm::BackgroundWorker.new
165
170
  @background_worker_thread = Thread.new do
166
171
  @background_worker.start { process_metrics }
167
- end # thread new
168
- logger.debug "Done creating worker thread."
172
+ end
169
173
  end
170
174
 
171
175
  def should_load_instruments?
@@ -174,8 +178,6 @@ module ScoutApm
174
178
 
175
179
  # Loads the instrumention logic.
176
180
  def load_instruments
177
- logger.debug "Installing instrumentation"
178
-
179
181
  case environment.framework
180
182
  when :rails then install_instrument(ScoutApm::Instruments::ActionControllerRails2)
181
183
  when :rails3_or_4 then install_instrument(ScoutApm::Instruments::ActionControllerRails3)
@@ -186,6 +188,10 @@ module ScoutApm
186
188
  install_instrument(ScoutApm::Instruments::Moped)
187
189
  install_instrument(ScoutApm::Instruments::Mongoid)
188
190
  install_instrument(ScoutApm::Instruments::NetHttp)
191
+
192
+ if StackProf.respond_to?(:fake?) && StackProf.fake?
193
+ logger.info 'StackProf not found - add `gem "stackprof"` to your Gemfile to enable advanced code profiling (only for Ruby 2.1+)'
194
+ end
189
195
  rescue
190
196
  logger.warn "Exception loading instruments:"
191
197
  logger.warn $!.message
@@ -197,5 +203,9 @@ module ScoutApm
197
203
  @installed_instruments << instance
198
204
  instance.install
199
205
  end
206
+
207
+ def deploy_integration
208
+ environment.deploy_integration
209
+ end
200
210
  end
201
211
  end
@@ -20,13 +20,14 @@ module ScoutApm
20
20
  if payload.any?
21
21
  add_metric_ids(metrics)
22
22
 
23
- logger.warn "Some data may be lost - metric size is at limit" if metrics.size == ScoutApm::Store::MAX_SIZE
23
+ logger.warn "Metric Size is at Limit, truncating" if metrics.size == ScoutApm::Store::MAX_SIZE
24
+
24
25
  # for debugging, count the total number of requests
25
- controller_count = 0
26
+ total_request_count = 0
26
27
 
27
28
  metrics.each do |meta,stats|
28
29
  if meta.metric_name =~ /\AController/
29
- controller_count += stats.call_count
30
+ total_request_count += stats.call_count
30
31
  end
31
32
  end
32
33
 
@@ -35,7 +36,10 @@ module ScoutApm
35
36
 
36
37
  payload = ScoutApm::Serializers::PayloadSerializer.serialize(metrics, slow_transactions)
37
38
  slow_transactions_kb = Marshal.dump(slow_transactions).size/1024 # just for performance debugging
38
- logger.debug "#{config.value('name')} Delivering total payload [#{payload.size/1024} KB] for #{controller_count} requests and slow transactions [#{slow_transactions_kb} KB] for #{slow_transactions.size} transactions of durations: #{slow_transactions.map(&:total_call_time).join(',')}."
39
+
40
+ logger.info "Delivering #{metrics.length} Metrics for #{total_request_count} requests and #{slow_transactions.length} Slow Transaction Traces"
41
+
42
+ logger.debug "Total payload [#{payload.size/1024} KB] for #{total_request_count} requests and Slow Transactions [#{slow_transactions_kb} KB] for #{slow_transactions.size} transactions of durations: #{slow_transactions.map(&:total_call_time).join(',')}."
39
43
 
40
44
  response = reporter.report(payload)
41
45
 
@@ -44,7 +48,7 @@ module ScoutApm
44
48
 
45
49
  self.metric_lookup.merge!(directives[:metric_lookup])
46
50
  if directives[:reset]
47
- logger.info "Resetting metric_lookup."
51
+ logger.debug "Resetting metric_lookup."
48
52
  self.metric_lookup = Hash.new
49
53
  end
50
54
  logger.debug "Metric Cache Size: #{metric_lookup.size}"
@@ -9,13 +9,17 @@ module ScoutApm
9
9
  def run
10
10
  @thread = Thread.new do
11
11
  begin
12
- logger.info("Sending Startup Info: #{data.inspect}")
12
+ logger.info "Sending Application Startup Info - App Server: #{data[:app_server]}, Framework: #{data[:framework]}, Framework Version: #{data[:framework_version]}, Database Engine: #{data[:database_engine]}"
13
+ logger.debug("Full Application Startup Info: #{data.inspect}")
14
+
13
15
  payload = ScoutApm::Serializers::AppServerLoadSerializer.serialize(data)
14
16
  reporter = Reporter.new(:app_server_load)
15
17
  reporter.report(payload)
16
- logger.info("Finished sending Startup Info")
18
+
19
+ logger.debug("Finished sending Startup Info")
17
20
  rescue => e
18
- logger.debug("Failed Startup Info In Thread - #{e.message} \n\t#{e.backtrace.join("\t\n")}")
21
+ logger.info("Failed Sending Application Startup Info - #{e.message}")
22
+ logger.debug(e.backtrace.join("\t\n"))
19
23
  end
20
24
  end
21
25
  rescue => e
@@ -21,6 +21,7 @@ module ScoutApm
21
21
  DEFAULTS = {
22
22
  'host' => 'https://apm.scoutapp.com',
23
23
  'log_level' => 'info',
24
+ 'stackprof_interval' => 20000 # microseconds, 1000 = 1 millisecond, so 20k == 20 milliseconds
24
25
  }.freeze
25
26
 
26
27
  def initialize(config_path = nil)
@@ -36,9 +37,9 @@ module ScoutApm
36
37
  # boot, where we needed an option to set the application root.
37
38
  def value(key, env_only=false)
38
39
  value = if env_only
39
- ENV['SCOUT_'+key.upcase]
40
+ ENV['SCOUT_' + key.upcase]
40
41
  else
41
- ENV['SCOUT_'+key.upcase] || settings[key]
42
+ ENV['SCOUT_' + key.upcase] || setting(key)
42
43
  end
43
44
 
44
45
  value.to_s.strip.length.zero? ? nil : value
@@ -54,8 +55,12 @@ module ScoutApm
54
55
  File.expand_path(config_path)
55
56
  end
56
57
 
57
- def settings
58
- @settings ||= load_file
58
+ def setting(key)
59
+ settings[key] || settings(true)[key]
60
+ end
61
+
62
+ def settings(try_reload=false)
63
+ (@settings.nil? || try_reload) ? @settings = load_file : @settings
59
64
  end
60
65
 
61
66
  def config_environment
@@ -0,0 +1,12 @@
1
+ namespace :scout_apm do
2
+ namespace :deploy do
3
+ task :starting do
4
+ # Warn if missing scout apm deploy creds?
5
+ end
6
+ task :finished do
7
+ ScoutApm::Agent.instance.deploy_integration.report
8
+ end
9
+ end
10
+ end
11
+
12
+ after 'deploy:finished', 'scout_apm:deploy:finished'
@@ -0,0 +1,83 @@
1
+ require 'scout_apm'
2
+
3
+ module ScoutApm
4
+ module DeployIntegrations
5
+ class Capistrano2
6
+ attr_reader :logger
7
+
8
+ def initialize(logger)
9
+ @logger = logger
10
+ @cap = defined?(Capistrano::Configuration) ? ObjectSpace.each_object(Capistrano::Configuration).map.first : nil rescue nil
11
+ end
12
+
13
+ def name
14
+ :capistrano_2
15
+ end
16
+
17
+ def version
18
+ present? ? Capistrano::VERSION : nil
19
+ end
20
+
21
+ def present?
22
+ if !@cap.nil? && @cap.is_a?(Capistrano::Configuration)
23
+ require 'capistrano/version'
24
+ defined?(Capistrano::VERSION) && Gem::Dependency.new('', '~> 2.0').match?('', Capistrano::VERSION.to_s)
25
+ else
26
+ return false
27
+ end
28
+ return true
29
+ rescue
30
+ return false
31
+ end
32
+
33
+ def install
34
+ logger.debug "Initializing Capistrano2 Deploy Integration."
35
+ @cap.load File.expand_path("../capistrano_2.cap", __FILE__)
36
+ end
37
+
38
+ def root
39
+ '.'
40
+ end
41
+
42
+ def env
43
+ @cap.fetch(:stage)
44
+ end
45
+
46
+ def found?
47
+ true
48
+ end
49
+
50
+ def report
51
+ if reporter.can_report?
52
+ data = deploy_data
53
+ logger.debug "Sending deploy hook data: #{data}"
54
+ payload = ScoutApm::Serializers::DeploySerializer.serialize(data)
55
+ reporter.report(payload, ScoutApm::Serializers::DeploySerializer::HTTP_HEADERS)
56
+ else
57
+ logger.warn "Unable to post deploy hook data"
58
+ end
59
+ end
60
+
61
+ def reporter
62
+ @reporter ||= ScoutApm::Reporter.new(:deploy_hook, ScoutApm::Agent.instance.config, @logger)
63
+ end
64
+
65
+ def deploy_data
66
+ {:revision => current_revision, :branch => branch, :deployed_by => deployed_by}
67
+ end
68
+
69
+ def branch
70
+ @cap.fetch(:branch)
71
+ end
72
+
73
+ def current_revision
74
+ @cap.fetch(:current_revision) || `git rev-list --max-count=1 --abbrev-commit --abbrev=12 #{branch}`.chomp
75
+ end
76
+
77
+ def deployed_by
78
+ ScoutApm::Agent.instance.config.value('deployed_by')
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,12 @@
1
+ namespace :scout_apm do
2
+ namespace :deploy do
3
+ task :starting do
4
+ # Warn if missing scout apm deploy creds?
5
+ end
6
+ task :finished do
7
+ ScoutApm::Agent.instance.deploy_integration.report
8
+ end
9
+ end
10
+ end
11
+
12
+ after 'deploy:finished', 'scout_apm:deploy:finished'
@@ -0,0 +1,82 @@
1
+ require 'scout_apm'
2
+
3
+ module ScoutApm
4
+ module DeployIntegrations
5
+ class Capistrano3
6
+ attr_reader :logger
7
+
8
+ def initialize(logger)
9
+ @logger = logger
10
+ @cap = Rake.application rescue nil
11
+ end
12
+
13
+ def name
14
+ :capistrano_3
15
+ end
16
+
17
+ def version
18
+ present? ? Capistrano::VERSION : nil
19
+ end
20
+
21
+ def present?
22
+ if !@cap.nil? && @cap.is_a?(Capistrano::Application)
23
+ require 'capistrano/version'
24
+ defined?(Capistrano::VERSION) && Gem::Dependency.new('', '~> 3.0').match?('', Capistrano::VERSION.to_s)
25
+ else
26
+ return false
27
+ end
28
+ rescue
29
+ return false
30
+ end
31
+
32
+ def install
33
+ logger.debug "Initializing Capistrano3 Deploy Integration."
34
+ load File.expand_path("../capistrano_3.cap", __FILE__)
35
+ end
36
+
37
+ def root
38
+ '.'
39
+ end
40
+
41
+ def env
42
+ @cap.fetch(:stage).to_s
43
+ end
44
+
45
+ def found?
46
+ true
47
+ end
48
+
49
+ def report
50
+ if reporter.can_report?
51
+ data = deploy_data
52
+ logger.debug "Sending deploy hook data: #{data}"
53
+ payload = ScoutApm::Serializers::DeploySerializer.serialize(data)
54
+ reporter.report(payload, ScoutApm::Serializers::DeploySerializer::HTTP_HEADERS)
55
+ else
56
+ logger.warn "Unable to post deploy hook data"
57
+ end
58
+ end
59
+
60
+ def reporter
61
+ @reporter ||= ScoutApm::Reporter.new(:deploy_hook, ScoutApm::Agent.instance.config, @logger)
62
+ end
63
+
64
+ def deploy_data
65
+ {:revision => current_revision, :branch => branch, :deployed_by => deployed_by}
66
+ end
67
+
68
+ def branch
69
+ @cap.fetch(:branch)
70
+ end
71
+
72
+ def current_revision
73
+ @cap.fetch(:current_revision) || `git rev-list --max-count=1 --abbrev-commit --abbrev=12 #{branch}`.chomp
74
+ end
75
+
76
+ def deployed_by
77
+ ScoutApm::Agent.instance.config.value('deployed_by')
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -24,8 +24,13 @@ module ScoutApm
24
24
  ScoutApm::FrameworkIntegrations::Ruby.new, # Fallback if none match
25
25
  ]
26
26
 
27
+ DEPLOY_INTEGRATIONS = [
28
+ ScoutApm::DeployIntegrations::Capistrano3.new(Logger.new(STDOUT)),
29
+ # ScoutApm::DeployIntegrations::Capistrano2.new(Logger.new(STDOUT)),
30
+ ]
31
+
27
32
  def env
28
- @env ||= framework_integration.env
33
+ @env ||= deploy_integration? ? deploy_integration.env : framework_integration.env
29
34
  end
30
35
 
31
36
  def framework
@@ -58,6 +63,7 @@ module ScoutApm
58
63
  end
59
64
 
60
65
  def root
66
+ return deploy_integration.root if deploy_integration
61
67
  framework_root
62
68
  end
63
69
 
@@ -65,7 +71,6 @@ module ScoutApm
65
71
  if override_root = Agent.instance.config.value("application_root", true)
66
72
  return override_root
67
73
  end
68
-
69
74
  if framework == :rails
70
75
  RAILS_ROOT.to_s
71
76
  elsif framework == :rails3_or_4
@@ -106,6 +111,14 @@ module ScoutApm
106
111
  app_server_integration.forking?
107
112
  end
108
113
 
114
+ def deploy_integration
115
+ @deploy_integration ||= DEPLOY_INTEGRATIONS.detect{ |integration| integration.present? }
116
+ end
117
+
118
+ def deploy_integration?
119
+ !@deploy_integration.nil?
120
+ end
121
+
109
122
  ### ruby checks
110
123
 
111
124
  def rubinius?
@@ -31,7 +31,7 @@ module ScoutApm
31
31
  protected :rescue_action
32
32
  end
33
33
 
34
- ScoutApm::Agent.instance.logger.debug "Instrumenting ActionView::Template"
34
+ ScoutApm::Agent.instance.logger.info "Instrumenting ActionView::Template"
35
35
  ::ActionView::Template.class_eval do
36
36
  include ::ScoutApm::Tracer
37
37
  instrument_method :render, :metric_name => 'View/#{path[%r{^(/.*/)?(.*)$},2]}/Rendering', :scope => true
@@ -43,7 +43,7 @@ module ScoutApm
43
43
 
44
44
  module ActionControllerRails2Instruments
45
45
  def self.included(instrumented_class)
46
- ScoutApm::Agent.instance.logger.debug "Instrumenting #{instrumented_class.inspect}"
46
+ ScoutApm::Agent.instance.logger.info "Instrumenting #{instrumented_class.inspect}"
47
47
  instrumented_class.class_eval do
48
48
  unless instrumented_class.method_defined?(:perform_action_without_scout_instruments)
49
49
  alias_method :perform_action_without_scout_instruments, :perform_action
@@ -18,7 +18,7 @@ module ScoutApm
18
18
  # ActionController::Base is a subclass of ActionController::Metal, so this instruments both
19
19
  # standard Rails requests + Metal.
20
20
  if defined?(::ActionController) && defined?(::ActionController::Metal)
21
- ScoutApm::Agent.instance.logger.debug "Instrumenting ActionController::Metal"
21
+ ScoutApm::Agent.instance.logger.info "Instrumenting ActionController::Metal"
22
22
  ::ActionController::Metal.class_eval do
23
23
  include ScoutApm::Tracer
24
24
  include ScoutApm::Instruments::ActionControllerRails3Instruments
@@ -26,13 +26,26 @@ module ScoutApm
26
26
  end
27
27
 
28
28
  if defined?(::ActionView) && defined?(::ActionView::PartialRenderer)
29
- ScoutApm::Agent.instance.logger.debug "Instrumenting ActionView::PartialRenderer"
30
- ActionView::PartialRenderer.class_eval do
29
+ ScoutApm::Agent.instance.logger.info "Instrumenting ActionView::PartialRenderer"
30
+ ::ActionView::PartialRenderer.class_eval do
31
31
  include ScoutApm::Tracer
32
- instrument_method :render_partial, :metric_name => 'View/#{@template.virtual_path}/Rendering', :scope => true
32
+ instrument_method :render_partial,
33
+ :metric_name => 'View/#{@template.virtual_path rescue "Unknown Partial"}/Rendering',
34
+ :scope => true
35
+
36
+ instrument_method :collection_with_template,
37
+ :metric_name => 'View/#{@template.virtual_path rescue "Unknown Collection"}/Rendering',
38
+ :scope => true
33
39
  end
34
- end
35
40
 
41
+ ScoutApm::Agent.instance.logger.info "Instrumenting ActionView::TemplateRenderer"
42
+ ::ActionView::TemplateRenderer.class_eval do
43
+ include ScoutApm::Tracer
44
+ instrument_method :render_template,
45
+ :metric_name => 'View/#{args[0].virtual_path rescue "Unknown"}/Rendering',
46
+ :scope => true
47
+ end
48
+ end
36
49
  end
37
50
  end
38
51
 
@@ -42,13 +55,18 @@ module ScoutApm
42
55
  scout_controller_action = "Controller/#{controller_path}/#{action_name}"
43
56
 
44
57
  self.class.scout_apm_trace(scout_controller_action, :uri => request.fullpath, :ip => request.remote_ip) do
58
+ Thread::current[:scout_apm_prof] = nil
59
+ StackProf.start(mode: :wall, interval: ScoutApm::Agent.instance.config.value("stackprof_interval"))
60
+
45
61
  begin
46
62
  super
47
63
  rescue Exception
48
- ScoutApm::Agent.instance.store.track!("Errors/Request",1, :scope => nil)
64
+ ScoutApm::Agent.instance.store.track!("Errors/Request", 1, :scope => nil)
49
65
  raise
50
66
  ensure
51
67
  Thread::current[:scout_apm_scope_name] = nil
68
+ StackProf.stop
69
+ Thread::current[:scout_apm_prof] = StackProf.results
52
70
  end
53
71
  end
54
72
  end
@@ -56,9 +74,3 @@ module ScoutApm
56
74
  end
57
75
  end
58
76
 
59
-
60
- # Rails 3/4
61
- module ScoutApm
62
- module Instruments
63
- end
64
- end
@@ -19,7 +19,6 @@ module ScoutApm
19
19
 
20
20
  if defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i == 3 && ::Rails.respond_to?(:configuration)
21
21
  Rails.configuration.after_initialize do
22
- ScoutApm::Agent.instance.logger.debug "Adding ActiveRecord instrumentation to a Rails 3 app"
23
22
  add_instruments
24
23
  end
25
24
  else
@@ -47,7 +46,7 @@ module ScoutApm
47
46
  # to trace calls to the database.
48
47
  module ActiveRecordInstruments
49
48
  def self.included(instrumented_class)
50
- ScoutApm::Agent.instance.logger.debug "Instrumenting #{instrumented_class.inspect}"
49
+ ScoutApm::Agent.instance.logger.info "Instrumenting #{instrumented_class.inspect}"
51
50
  instrumented_class.class_eval do
52
51
  unless instrumented_class.method_defined?(:log_without_scout_instruments)
53
52
  alias_method :log_without_scout_instruments, :log
@@ -17,7 +17,7 @@ module ScoutApm
17
17
 
18
18
  # Mongoid versions that use Moped should instrument Moped.
19
19
  if defined?(::Mongoid) and !defined?(::Moped)
20
- ScoutApm::Agent.instance.logger.debug "Instrumenting Mongoid"
20
+ ScoutApm::Agent.instance.logger.info "Instrumenting Mongoid"
21
21
 
22
22
  ::Mongoid::Collection.class_eval do
23
23
  include ScoutApm::Tracer
@@ -16,7 +16,7 @@ module ScoutApm
16
16
  @installed = true
17
17
 
18
18
  if defined?(::Moped)
19
- ScoutApm::Agent.instance.logger.debug "Instrumenting Moped"
19
+ ScoutApm::Agent.instance.logger.info "Instrumenting Moped"
20
20
  ::Moped::Node.class_eval do
21
21
  include ScoutApm::Tracer
22
22
 
@@ -16,7 +16,7 @@ module ScoutApm
16
16
  @installed = true
17
17
 
18
18
  if defined?(::Net) && defined?(::Net::HTTP)
19
- ScoutApm::Agent.instance.logger.debug "Instrumenting Net::HTTP"
19
+ ScoutApm::Agent.instance.logger.info "Instrumenting Net::HTTP"
20
20
 
21
21
  ::Net::HTTP.class_eval do
22
22
  include ScoutApm::Tracer
@@ -37,7 +37,7 @@ module ScoutApm
37
37
  wall_clock_elapsed = now - last_run
38
38
  if wall_clock_elapsed < 0
39
39
  save_times(now, utime, stime)
40
- logger.warn "#{human_name}: Negative time elapsed. now: #{now}, last_run: #{last_run}, total time: #{wall_clock_elapsed}"
40
+ logger.info "#{human_name}: Negative time elapsed. now: #{now}, last_run: #{last_run}, total time: #{wall_clock_elapsed}."
41
41
  return nil
42
42
  end
43
43
 
@@ -49,7 +49,7 @@ module ScoutApm
49
49
  # pre-fork, records {u,s}time, then forks. This resets {u,s}time to 0
50
50
  if process_elapsed < 0
51
51
  save_times(now, utime, stime)
52
- logger.warn "#{human_name}: Negative process time elapsed. utime: #{utime_elapsed}, stime: #{stime_elapsed}, total time: #{process_elapsed}"
52
+ logger.debug "#{human_name}: Negative process time elapsed. utime: #{utime_elapsed}, stime: #{stime_elapsed}, total time: #{process_elapsed}. This is normal to see when starting a forking web server."
53
53
  return nil
54
54
  end
55
55
 
@@ -65,7 +65,7 @@ module ScoutApm
65
65
 
66
66
  if res < 0
67
67
  save_times(now, utime, stime)
68
- logger.warn "#{human_name}: Negative CPU. #{process_elapsed} / #{normalized_wall_clock_elapsed} * 100 ==> #{res}"
68
+ logger.info "#{human_name}: Negative CPU. #{process_elapsed} / #{normalized_wall_clock_elapsed} * 100 ==> #{res}"
69
69
  return nil
70
70
  end
71
71
 
@@ -16,7 +16,7 @@ module ScoutApm
16
16
  @installed = true
17
17
 
18
18
  if defined?(::Sinatra) && defined?(::Sinatra::Base) && ::Sinatra::Base.private_method_defined?(:dispatch!)
19
- ScoutApm::Agent.instance.logger.debug "Instrumenting Sinatra"
19
+ ScoutApm::Agent.instance.logger.info "Instrumenting Sinatra"
20
20
  ::Sinatra::Base.class_eval do
21
21
  include ScoutApm::Tracer
22
22
  include ScoutApm::Instruments::SinatraInstruments
@@ -16,8 +16,8 @@ module ScoutApm
16
16
  end
17
17
 
18
18
  # TODO: Parse & return a real response object, not the HTTP Response object
19
- def report(payload)
20
- post(uri, payload)
19
+ def report(payload, headers = {})
20
+ post(uri, payload, headers)
21
21
  end
22
22
 
23
23
  def uri
@@ -26,9 +26,26 @@ module ScoutApm
26
26
  URI.parse("#{config.value('host')}/apps/checkin.scout?key=#{config.value('key')}&name=#{CGI.escape(Environment.instance.application_name)}")
27
27
  when :app_server_load
28
28
  URI.parse("#{config.value('host')}/apps/app_server_load.scout?key=#{config.value('key')}&name=#{CGI.escape(Environment.instance.application_name)}")
29
+ when :deploy_hook
30
+ URI.parse("#{config.value('host')}/apps/deploy.scout?key=#{config.value('key')}&name=#{CGI.escape(config.value('name'))}")
29
31
  end.tap{|u| logger.debug("Posting to #{u.to_s}")}
30
32
  end
31
33
 
34
+ def can_report?
35
+ case type
36
+ when :deploy_hook
37
+ %w(host key name).each do |k|
38
+ if config.value(k).nil?
39
+ logger.warn "/#{type} FAILED: missing required config value for #{k}"
40
+ return false
41
+ end
42
+ end
43
+ return true
44
+ else
45
+ return true
46
+ end
47
+ end
48
+
32
49
  private
33
50
 
34
51
  def post(uri, body, headers = Hash.new)
@@ -52,6 +69,8 @@ module ScoutApm
52
69
  logger.debug "/#{type} OK"
53
70
  when Net::HTTPBadRequest
54
71
  logger.warn "/#{type} FAILED: The Account Key [#{config.value('key')}] is invalid."
72
+ when Net::HTTPUnprocessableEntity
73
+ logger.warn "/#{type} FAILED: #{response.body}"
55
74
  else
56
75
  logger.debug "/#{type} FAILED: #{response.inspect}"
57
76
  end
@@ -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
@@ -18,6 +18,8 @@ module ScoutApm
18
18
  end
19
19
 
20
20
  def install
21
+ logger.info "Installing Passenger worker loop."
22
+
21
23
  PhusionPassenger.on_event(:starting_worker_process) do |forked|
22
24
  logger.debug "Passenger is starting a worker process. Starting worker thread."
23
25
  ScoutApm::Agent.instance.start_background_worker
@@ -14,7 +14,6 @@ module ScoutApm
14
14
  def forking?
15
15
  return false unless defined?(::Puma)
16
16
  options = ::Puma.cli_config.instance_variable_get(:@options)
17
- logger.debug options.inspect
18
17
  options[:preload_app]
19
18
  rescue
20
19
  false
@@ -26,7 +25,7 @@ module ScoutApm
26
25
 
27
26
  def install
28
27
  ::Puma.cli_config.options[:before_worker_boot] << Proc.new do
29
- logger.debug "Installing Puma worker loop."
28
+ logger.info "Installing Puma worker loop."
30
29
  ScoutApm::Agent.instance.start_background_worker
31
30
  end
32
31
  rescue
@@ -21,7 +21,7 @@ module ScoutApm
21
21
  end
22
22
 
23
23
  def install
24
- logger.debug "Installing Rainbows worker loop."
24
+ logger.info "Installing Rainbows worker loop."
25
25
 
26
26
  Rainbows::HttpServer.class_eval do
27
27
  old = instance_method(:worker_loop)
@@ -29,6 +29,7 @@ module ScoutApm
29
29
  end
30
30
 
31
31
  def install
32
+ logger.info "Installing Unicorn worker loop."
32
33
  ::Unicorn::HttpServer.class_eval do
33
34
  old = instance_method(:worker_loop)
34
35
  define_method(:worker_loop) do |worker|
@@ -1,36 +1,49 @@
1
- class ScoutApm::SlowTransaction
2
- BACKTRACE_THRESHOLD = 0.5 # the minimum threshold to record the backtrace for a metric.
3
- BACKTRACE_LIMIT = 5 # Max length of callers to display
4
- MAX_SIZE = 100 # Limits the size of the metric hash to prevent a metric explosion.
5
- attr_reader :metric_name, :total_call_time, :metrics, :meta, :uri, :context, :time, :prof, :raw_prof
1
+ module ScoutApm
2
+ class SlowTransaction
3
+ BACKTRACE_THRESHOLD = 0.5 # the minimum threshold to record the backtrace for a metric.
4
+ BACKTRACE_LIMIT = 5 # Max length of callers to display
5
+ MAX_SIZE = 100 # Limits the size of the metric hash to prevent a metric explosion.
6
6
 
7
- # Given a call stack, generates a filtered backtrace that:
8
- # * Limits to the app/models, app/controllers, or app/views directories
9
- # * Limits to 5 total callers
10
- # * Makes the app folder the top-level folder used in trace info
11
- def self.backtrace_parser(backtrace)
12
- stack = []
13
- backtrace.each do |c|
14
- if m=c.match(/(\/app\/(controllers|models|views)\/.+)/)
15
- stack << m[1]
16
- break if stack.size == BACKTRACE_LIMIT
7
+ attr_reader :metric_name
8
+ attr_reader :total_call_time
9
+ attr_reader :metrics
10
+ attr_reader :meta
11
+ attr_reader :uri
12
+ attr_reader :context
13
+ attr_reader :time
14
+ attr_reader :prof
15
+ attr_reader :raw_prof
16
+
17
+ # Given a call stack, generates a filtered backtrace that:
18
+ # * Limits to the app/models, app/controllers, or app/views directories
19
+ # * Limits to 5 total callers
20
+ # * Makes the app folder the top-level folder used in trace info
21
+ def self.backtrace_parser(backtrace)
22
+ stack = []
23
+ backtrace.each do |c|
24
+ if m=c.match(/(\/app\/(controllers|models|views)\/.+)/)
25
+ stack << m[1]
26
+ break if stack.size == BACKTRACE_LIMIT
27
+ end
17
28
  end
29
+ stack
18
30
  end
19
- stack
20
- end
21
31
 
22
- def initialize(uri,metric_name,total_call_time,metrics,context,time)
23
- @uri = uri
24
- @metric_name = metric_name
25
- @total_call_time = total_call_time
26
- @metrics = metrics
27
- @context = context
28
- @time = time
29
- end
32
+ def initialize(uri, metric_name, total_call_time, metrics, context, time, raw_stackprof)
33
+ @uri = uri
34
+ @metric_name = metric_name
35
+ @total_call_time = total_call_time
36
+ @metrics = metrics
37
+ @context = context
38
+ @time = time
39
+ @prof = ScoutApm::StackprofTreeCollapser.new(raw_stackprof).call
40
+ @raw_prof = raw_stackprof # Send whole data up to server
41
+ end
30
42
 
31
- # Used to remove metrics when the payload will be too large.
32
- def clear_metrics!
33
- @metrics = nil
34
- self
43
+ # Used to remove metrics when the payload will be too large.
44
+ def clear_metrics!
45
+ @metrics = nil
46
+ self
47
+ end
35
48
  end
36
49
  end
@@ -0,0 +1,95 @@
1
+ module ScoutApm
2
+ class StackprofTreeCollapser
3
+ attr_reader :raw_stackprof
4
+ attr_reader :nodes
5
+
6
+ def initialize(raw_stackprof)
7
+ @raw_stackprof = raw_stackprof
8
+ unless StackProf.respond_to?(:fake?) && StackProf.fake?
9
+ ScoutApm::Agent.instance.logger.debug("StackProf - Samples: #{raw_stackprof[:samples]}, GC: #{raw_stackprof[:gc_samples]}, missed: #{raw_stackprof[:missed_samples]}, Interval: #{raw_stackprof[:interval]}")
10
+ end
11
+ end
12
+
13
+ def call
14
+ build_tree
15
+ connect_children
16
+ total_samples_of_app_nodes
17
+ end
18
+
19
+ private
20
+
21
+ def build_tree
22
+ @nodes = raw_stackprof[:frames].map do |(frame_id, frame_data)|
23
+ TreeNode.new(frame_id, # frame_id
24
+ frame_data[:name], # name
25
+ frame_data[:file], # file
26
+ frame_data[:line], # line
27
+ frame_data[:samples], # samples
28
+ frame_data[:total_samples], # total_samples
29
+ (frame_data[:edges] || {}), # children_edges [ { id => weight } ]
30
+ [], # children [ treenode, ... ]
31
+ [] # parents [ [treenode, int (weight) ], [...] ]
32
+ )
33
+ end
34
+ end
35
+
36
+ def connect_children
37
+ nodes.each do |node|
38
+ children = nodes.find_all { |n| node.children_edges.keys.include? n.frame_id }
39
+
40
+ node.children_edges.each do |(frame_id, weight)|
41
+ child = children.detect{ |c| c.frame_id == frame_id }
42
+ child.parents << [node, weight]
43
+ end
44
+
45
+ node.children = children
46
+ end
47
+ end
48
+
49
+ def in_app_nodes
50
+ nodes.select {|n| n.app? }
51
+ end
52
+
53
+ def total_samples_of_app_nodes
54
+ in_app_nodes.reject{|n| n.calls_only_app_nodes? && !n.has_samples? }.
55
+ map{|n| { samples: n.total_samples,
56
+ name: n.name,
57
+ file: n.file,
58
+ line: n.line
59
+ }
60
+ }
61
+ end
62
+
63
+ ###########################################
64
+ # TreeNode class represents a single node.
65
+ ###########################################
66
+ TreeNode = Struct.new(:frame_id, :name, :file, :line, :samples, :total_samples,
67
+ :children_edges, :children, :parents) do
68
+ def app?
69
+ @is_app ||= file =~ /^#{ScoutApm::Environment.instance.root}/
70
+ end
71
+
72
+ # Force object_id to be the equality mechanism, rather than struct's
73
+ # default which delegates to == on each value. That is wrong because
74
+ # we want to be able to dup a node in the tree construction process and
75
+ # not have those compare equal to each other.
76
+ def ==(other)
77
+ object_id == other.object_id
78
+ end
79
+
80
+ def inspect
81
+ "#{frame_id}: #{name} - ##{samples}\n" +
82
+ " Parents: #{parents.map{ |(p, w)| "#{p.name}: #{w}"}.join("\n ") }\n" +
83
+ " Children: #{children_edges.inspect} \n"
84
+ end
85
+
86
+ def calls_only_app_nodes?
87
+ children.all?(&:app?)
88
+ end
89
+
90
+ def has_samples?
91
+ samples > 0
92
+ end
93
+ end
94
+ end
95
+ end
@@ -72,7 +72,7 @@ module ScoutApm
72
72
  end
73
73
 
74
74
  duration = Time.now - item.start_time
75
- if last=stack.last
75
+ if last = stack.last
76
76
  last.children_time += duration
77
77
  end
78
78
 
@@ -90,8 +90,8 @@ module ScoutApm
90
90
 
91
91
  # Uses controllers as the entry point for a transaction. Otherwise, stats are ignored.
92
92
  if stack_empty and meta.metric_name.match(/\AController\//)
93
- aggs=aggregate_calls(transaction_hash.dup,meta)
94
- store_slow(options[:uri],transaction_hash.dup.merge(aggs),meta,stat)
93
+ aggs = aggregate_calls(transaction_hash.dup,meta)
94
+ store_slow(options[:uri], transaction_hash.dup.merge(aggs), meta, stat)
95
95
  # deep duplicate
96
96
  duplicate = aggs.dup
97
97
  duplicate.each_pair do |k,v|
@@ -139,12 +139,20 @@ module ScoutApm
139
139
  aggregates
140
140
  end
141
141
 
142
+ SLOW_TRANSACTION_THRESHOLD = 2
143
+
142
144
  # Stores slow transactions. This will be sent to the server.
143
- def store_slow(uri,transaction_hash,parent_meta,parent_stat,options = {})
145
+ def store_slow(uri, transaction_hash, parent_meta, parent_stat, options = {})
144
146
  @slow_transaction_lock.synchronize do
145
- # tree map of all slow transactions
146
- if parent_stat.total_call_time >= 2
147
- @slow_transactions.push(ScoutApm::SlowTransaction.new(uri,parent_meta.metric_name,parent_stat.total_call_time,transaction_hash.dup,ScoutApm::Context.current,Thread::current[:scout_apm_trace_time]))
147
+ if parent_stat.total_call_time >= SLOW_TRANSACTION_THRESHOLD
148
+ slow_transaction = ScoutApm::SlowTransaction.new(uri,
149
+ parent_meta.metric_name,
150
+ parent_stat.total_call_time,
151
+ transaction_hash.dup,
152
+ ScoutApm::Context.current,
153
+ Thread::current[:scout_apm_trace_time],
154
+ Thread::current[:scout_apm_prof])
155
+ @slow_transactions.push(slow_transaction)
148
156
  ScoutApm::Agent.instance.logger.debug "Slow transaction sample added. [URI: #{uri}] [Context: #{ScoutApm::Context.current.to_hash}] Array Size: #{@slow_transactions.size}"
149
157
  end
150
158
  end
@@ -54,13 +54,16 @@ module ScoutApm
54
54
  elsif Thread::current[:scout_ignore_children]
55
55
  return yield
56
56
  end
57
+
57
58
  if options.delete(:scope)
58
59
  Thread::current[:scout_apm_sub_scope] = metric_name
59
60
  end
61
+
60
62
  if options[:ignore_children]
61
63
  Thread::current[:scout_ignore_children] = true
62
64
  end
63
65
  stack_item = ScoutApm::Agent.instance.store.record(metric_name)
66
+
64
67
  begin
65
68
  yield
66
69
  ensure
@@ -68,15 +71,21 @@ module ScoutApm
68
71
  if options[:ignore_children]
69
72
  Thread::current[:scout_ignore_children] = nil
70
73
  end
74
+
71
75
  ScoutApm::Agent.instance.store.stop_recording(stack_item,options)
72
76
  end
73
77
  end
74
78
 
75
- def instrument_method(method,options = {})
79
+ def instrument_method(method, options = {})
76
80
  ScoutApm::Agent.instance.logger.info "Instrumenting #{method}"
77
81
  metric_name = options[:metric_name] || default_metric_name(method)
78
82
  return if !instrumentable?(method) or instrumented?(method,metric_name)
79
- class_eval instrumented_method_string(method, {:metric_name => metric_name, :scope => options[:scope]}), __FILE__, __LINE__
83
+
84
+ class_eval(instrumented_method_string(
85
+ method,
86
+ {:metric_name => metric_name, :scope => options[:scope] }),
87
+ __FILE__, __LINE__
88
+ )
80
89
 
81
90
  alias_method _uninstrumented_method_name(method, metric_name), method
82
91
  alias_method method, _instrumented_method_name(method, metric_name)
@@ -86,12 +95,14 @@ module ScoutApm
86
95
 
87
96
  def instrumented_method_string(method, options)
88
97
  klass = (self === Module) ? "self" : "self.class"
89
- "def #{_instrumented_method_name(method, options[:metric_name])}(*args, &block)
98
+ method_str = "def #{_instrumented_method_name(method, options[:metric_name])}(*args, &block)
90
99
  result = #{klass}.instrument(\"#{options[:metric_name]}\",{:scope => #{options[:scope] || false}}) do
91
100
  #{_uninstrumented_method_name(method, options[:metric_name])}(*args, &block)
92
101
  end
93
102
  result
94
103
  end"
104
+
105
+ method_str
95
106
  end
96
107
 
97
108
  # The method must exist to be instrumented.
@@ -0,0 +1,40 @@
1
+ # A fake implementation of stackprof, for systems that don't support it.
2
+ class StackProf
3
+ def self.start(*args)
4
+ @running = true
5
+ end
6
+
7
+ def self.stop(*args)
8
+ @running = false
9
+ end
10
+
11
+ def self.running?
12
+ !!@running
13
+ end
14
+
15
+ def self.run(*args)
16
+ start
17
+ yield
18
+ stop
19
+ results
20
+ end
21
+
22
+ def self.sample(*args)
23
+ end
24
+
25
+ def self.results(*args)
26
+ {
27
+ :version => 0.0,
28
+ :mode => :wall,
29
+ :interval => 1000,
30
+ :samples => 0,
31
+ :gc_samples => 0,
32
+ :missed_samples => 0,
33
+ :frames => {},
34
+ }
35
+ end
36
+
37
+ def self.fake?
38
+ true
39
+ end
40
+ end
@@ -1,4 +1,4 @@
1
1
  module ScoutApm
2
- VERSION = "0.9.0"
2
+ VERSION = "0.9.1"
3
3
  end
4
4
 
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: 0.9.0
4
+ version: 0.9.1
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: 2015-09-29 00:00:00.000000000 Z
12
+ date: 2015-09-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -75,6 +75,10 @@ files:
75
75
  - lib/scout_apm/capacity.rb
76
76
  - lib/scout_apm/config.rb
77
77
  - lib/scout_apm/context.rb
78
+ - lib/scout_apm/deploy_integrations/capistrano_2.cap
79
+ - lib/scout_apm/deploy_integrations/capistrano_2.rb
80
+ - lib/scout_apm/deploy_integrations/capistrano_3.cap
81
+ - lib/scout_apm/deploy_integrations/capistrano_3.rb
78
82
  - lib/scout_apm/environment.rb
79
83
  - lib/scout_apm/framework_integrations/rails_2.rb
80
84
  - lib/scout_apm/framework_integrations/rails_3_or_4.rb
@@ -95,6 +99,7 @@ files:
95
99
  - lib/scout_apm/metric_stats.rb
96
100
  - lib/scout_apm/reporter.rb
97
101
  - lib/scout_apm/serializers/app_server_load_serializer.rb
102
+ - lib/scout_apm/serializers/deploy_serializer.rb
98
103
  - lib/scout_apm/serializers/directive_serializer.rb
99
104
  - lib/scout_apm/serializers/payload_serializer.rb
100
105
  - lib/scout_apm/server_integrations/null.rb
@@ -106,8 +111,10 @@ files:
106
111
  - lib/scout_apm/server_integrations/webrick.rb
107
112
  - lib/scout_apm/slow_transaction.rb
108
113
  - lib/scout_apm/stack_item.rb
114
+ - lib/scout_apm/stackprof_tree_collapser.rb
109
115
  - lib/scout_apm/store.rb
110
116
  - lib/scout_apm/tracer.rb
117
+ - lib/scout_apm/utils/fake_stack_prof.rb
111
118
  - lib/scout_apm/utils/installed_gems.rb
112
119
  - lib/scout_apm/utils/null_logger.rb
113
120
  - lib/scout_apm/utils/sql_sanitizer.rb