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.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +11 -0
- data/lib/scout_apm.rb +21 -0
- data/lib/scout_apm/agent.rb +21 -11
- data/lib/scout_apm/agent/reporting.rb +9 -5
- data/lib/scout_apm/app_server_load.rb +7 -3
- data/lib/scout_apm/config.rb +9 -4
- data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
- data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
- data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
- data/lib/scout_apm/deploy_integrations/capistrano_3.rb +82 -0
- data/lib/scout_apm/environment.rb +15 -2
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +2 -2
- data/lib/scout_apm/instruments/action_controller_rails_3.rb +24 -12
- data/lib/scout_apm/instruments/active_record.rb +1 -2
- data/lib/scout_apm/instruments/mongoid.rb +1 -1
- data/lib/scout_apm/instruments/moped.rb +1 -1
- data/lib/scout_apm/instruments/net_http.rb +1 -1
- data/lib/scout_apm/instruments/process/process_cpu.rb +3 -3
- data/lib/scout_apm/instruments/sinatra.rb +1 -1
- data/lib/scout_apm/reporter.rb +21 -2
- data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
- data/lib/scout_apm/server_integrations/passenger.rb +2 -0
- data/lib/scout_apm/server_integrations/puma.rb +1 -2
- data/lib/scout_apm/server_integrations/rainbows.rb +1 -1
- data/lib/scout_apm/server_integrations/unicorn.rb +1 -0
- data/lib/scout_apm/slow_transaction.rb +42 -29
- data/lib/scout_apm/stackprof_tree_collapser.rb +95 -0
- data/lib/scout_apm/store.rb +15 -7
- data/lib/scout_apm/tracer.rb +14 -3
- data/lib/scout_apm/utils/fake_stack_prof.rb +40 -0
- data/lib/scout_apm/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c2f4d07b1f636737317fc8200121bc301c8dd53
|
4
|
+
data.tar.gz: 59f8d5df43b92220fc8e482e04ab33a7b3a82337
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/scout_apm/agent.rb
CHANGED
@@ -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
|
-
|
97
|
-
|
98
|
-
|
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.
|
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.
|
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
|
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 "
|
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
|
-
|
26
|
+
total_request_count = 0
|
26
27
|
|
27
28
|
metrics.each do |meta,stats|
|
28
29
|
if meta.metric_name =~ /\AController/
|
29
|
-
|
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
|
-
|
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.
|
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
|
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
|
-
|
18
|
+
|
19
|
+
logger.debug("Finished sending Startup Info")
|
17
20
|
rescue => e
|
18
|
-
logger.
|
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
|
data/lib/scout_apm/config.rb
CHANGED
@@ -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] ||
|
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
|
58
|
-
|
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.
|
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.
|
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.
|
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.
|
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,
|
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.
|
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.
|
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?(::Net) && defined?(::Net::HTTP)
|
19
|
-
ScoutApm::Agent.instance.logger.
|
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.
|
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.
|
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.
|
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.
|
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
|
data/lib/scout_apm/reporter.rb
CHANGED
@@ -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.
|
28
|
+
logger.info "Installing Puma worker loop."
|
30
29
|
ScoutApm::Agent.instance.start_background_worker
|
31
30
|
end
|
32
31
|
rescue
|
@@ -1,36 +1,49 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
data/lib/scout_apm/store.rb
CHANGED
@@ -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
|
-
|
146
|
-
|
147
|
-
|
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
|
data/lib/scout_apm/tracer.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/scout_apm/version.rb
CHANGED
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.
|
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-
|
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
|