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.
- 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
|