scout_apm 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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