scout_apm 0.1.6 → 0.1.7
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 +4 -0
- data/lib/scout_apm.rb +48 -23
- data/lib/scout_apm/agent.rb +93 -130
- data/lib/scout_apm/agent/reporting.rb +34 -63
- data/lib/scout_apm/app_server_load.rb +29 -0
- data/lib/scout_apm/background_worker.rb +6 -6
- data/lib/scout_apm/capacity.rb +48 -48
- data/lib/scout_apm/config.rb +5 -5
- data/lib/scout_apm/context.rb +3 -3
- data/lib/scout_apm/environment.rb +64 -100
- data/lib/scout_apm/framework_integrations/rails_2.rb +32 -0
- data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +33 -0
- data/lib/scout_apm/framework_integrations/ruby.rb +26 -0
- data/lib/scout_apm/framework_integrations/sinatra.rb +27 -0
- data/lib/scout_apm/instruments/active_record_instruments.rb +1 -1
- data/lib/scout_apm/instruments/mongoid_instruments.rb +1 -1
- data/lib/scout_apm/instruments/moped_instruments.rb +3 -3
- data/lib/scout_apm/instruments/net_http.rb +2 -2
- data/lib/scout_apm/instruments/process/process_cpu.rb +41 -20
- data/lib/scout_apm/instruments/process/process_memory.rb +45 -30
- data/lib/scout_apm/instruments/rails/action_controller_instruments.rb +20 -18
- data/lib/scout_apm/instruments/rails3_or_4/action_controller_instruments.rb +17 -14
- data/lib/scout_apm/layaway.rb +12 -12
- data/lib/scout_apm/layaway_file.rb +1 -1
- data/lib/scout_apm/metric_meta.rb +9 -9
- data/lib/scout_apm/metric_stats.rb +8 -8
- data/lib/scout_apm/reporter.rb +83 -0
- data/lib/scout_apm/serializers/app_server_load_serializer.rb +15 -0
- data/lib/scout_apm/serializers/directive_serializer.rb +15 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +14 -0
- data/lib/scout_apm/server_integrations/null.rb +30 -0
- data/lib/scout_apm/server_integrations/passenger.rb +35 -0
- data/lib/scout_apm/server_integrations/puma.rb +30 -0
- data/lib/scout_apm/server_integrations/rainbows.rb +36 -0
- data/lib/scout_apm/server_integrations/thin.rb +41 -0
- data/lib/scout_apm/server_integrations/unicorn.rb +35 -0
- data/lib/scout_apm/server_integrations/webrick.rb +25 -0
- data/lib/scout_apm/slow_transaction.rb +3 -3
- data/lib/scout_apm/stack_item.rb +19 -17
- data/lib/scout_apm/store.rb +35 -35
- data/lib/scout_apm/tracer.rb +121 -110
- data/lib/scout_apm/utils/sql_sanitizer.rb +2 -2
- data/lib/scout_apm/version.rb +2 -1
- data/test/unit/environment_test.rb +5 -5
- metadata +18 -2
@@ -0,0 +1,33 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module FrameworkIntegrations
|
3
|
+
class Rails3Or4
|
4
|
+
def name
|
5
|
+
:rails3_or_4
|
6
|
+
end
|
7
|
+
|
8
|
+
def version
|
9
|
+
Rails::VERSION::STRING
|
10
|
+
end
|
11
|
+
|
12
|
+
def present?
|
13
|
+
defined?(::Rails) &&
|
14
|
+
defined?(ActionController) &&
|
15
|
+
Rails::VERSION::MAJOR >= 3
|
16
|
+
end
|
17
|
+
|
18
|
+
def application_name
|
19
|
+
if defined?(::Rails)
|
20
|
+
::Rails.application.class.to_s
|
21
|
+
.sub(/::Application$/, '')
|
22
|
+
end
|
23
|
+
rescue
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def env
|
28
|
+
::Rails.env
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module FrameworkIntegrations
|
3
|
+
class Ruby
|
4
|
+
def name
|
5
|
+
:ruby
|
6
|
+
end
|
7
|
+
|
8
|
+
def version
|
9
|
+
"Unknown"
|
10
|
+
end
|
11
|
+
|
12
|
+
def present?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO: Fetch the name (Somehow?)
|
17
|
+
def application_name
|
18
|
+
"Ruby"
|
19
|
+
end
|
20
|
+
|
21
|
+
def env
|
22
|
+
ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module FrameworkIntegrations
|
3
|
+
class Sinatra
|
4
|
+
def name
|
5
|
+
:sinatra
|
6
|
+
end
|
7
|
+
|
8
|
+
def version
|
9
|
+
Sinatra::VERSION
|
10
|
+
end
|
11
|
+
|
12
|
+
def present?
|
13
|
+
defined?(::Sinatra) &&
|
14
|
+
defined?(::Sinatra::Base)
|
15
|
+
end
|
16
|
+
|
17
|
+
# TODO: Fetch the name
|
18
|
+
def application_name
|
19
|
+
"Sinatra"
|
20
|
+
end
|
21
|
+
|
22
|
+
def env
|
23
|
+
ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -3,7 +3,7 @@ require 'scout_apm/utils/sql_sanitizer'
|
|
3
3
|
module ScoutApm
|
4
4
|
module Instruments
|
5
5
|
# Contains ActiveRecord instrument, aliasing +ActiveRecord::ConnectionAdapters::AbstractAdapter#log+ calls
|
6
|
-
# to trace calls to the database.
|
6
|
+
# to trace calls to the database.
|
7
7
|
module ActiveRecordInstruments
|
8
8
|
def self.included(instrumented_class)
|
9
9
|
ScoutApm::Agent.instance.logger.debug "Instrumenting #{instrumented_class.inspect}"
|
@@ -12,13 +12,13 @@ if defined?(::Moped)
|
|
12
12
|
end
|
13
13
|
alias_method :process_without_scout_instruments, :process
|
14
14
|
alias_method :process, :process_with_scout_instruments
|
15
|
-
|
15
|
+
|
16
16
|
# replaces values w/ ?
|
17
17
|
def scout_sanitize_log(log)
|
18
18
|
return nil if log.length > 1000 # safeguard - don't sanitize large SQL statements
|
19
|
-
log.gsub(/(=>")((?:[^"]|"")*)"/) do
|
19
|
+
log.gsub(/(=>")((?:[^"]|"")*)"/) do
|
20
20
|
$1 + '?' + '"'
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
|
-
end
|
24
|
+
end
|
@@ -2,7 +2,7 @@ if defined?(::Net) && defined?(Net::HTTP)
|
|
2
2
|
ScoutApm::Agent.instance.logger.debug "Instrumenting Net::HTTP"
|
3
3
|
Net::HTTP.class_eval do
|
4
4
|
include ScoutApm::Tracer
|
5
|
-
|
5
|
+
|
6
6
|
def request_with_scout_instruments(*args,&block)
|
7
7
|
self.class.instrument("HTTP/request", :desc => "#{(@address+args.first.path.split('?').first)[0..99]}") do
|
8
8
|
request_without_scout_instruments(*args,&block)
|
@@ -11,4 +11,4 @@ if defined?(::Net) && defined?(Net::HTTP)
|
|
11
11
|
alias request_without_scout_instruments request
|
12
12
|
alias request request_with_scout_instruments
|
13
13
|
end
|
14
|
-
end
|
14
|
+
end
|
@@ -1,26 +1,47 @@
|
|
1
|
-
module ScoutApm
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module ScoutApm
|
2
|
+
module Instruments
|
3
|
+
module Process
|
4
|
+
class ProcessCpu
|
5
|
+
attr_reader :logger
|
6
|
+
|
7
|
+
def initialize(num_processors, logger)
|
8
|
+
@num_processors = num_processors || 1
|
9
|
+
@logger = logger
|
10
|
+
|
11
|
+
t = ::Process.times
|
12
|
+
@last_run = Time.now
|
13
|
+
@last_utime = t.utime
|
14
|
+
@last_stime = t.stime
|
15
|
+
end
|
16
|
+
|
17
|
+
def metric_name
|
18
|
+
"CPU/Utilization"
|
19
|
+
end
|
20
|
+
|
21
|
+
def human_name
|
22
|
+
"Process CPU"
|
23
|
+
end
|
7
24
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
25
|
+
def run
|
26
|
+
res = nil
|
27
|
+
now = Time.now
|
28
|
+
t = ::Process.times
|
29
|
+
if @last_run
|
30
|
+
elapsed_time = now - @last_run
|
31
|
+
if elapsed_time >= 1
|
32
|
+
user_time_since_last_sample = t.utime - @last_utime
|
33
|
+
system_time_since_last_sample = t.stime - @last_stime
|
34
|
+
res = ((user_time_since_last_sample + system_time_since_last_sample)/(elapsed_time * @num_processors))*100
|
35
|
+
end
|
18
36
|
end
|
37
|
+
@last_utime = t.utime
|
38
|
+
@last_stime = t.stime
|
39
|
+
@last_run = now
|
40
|
+
|
41
|
+
logger.debug "#{human_name}: #{res.inspect} [#{Environment.instance.processors} CPU(s)]"
|
42
|
+
|
43
|
+
return res
|
19
44
|
end
|
20
|
-
@last_utime = t.utime
|
21
|
-
@last_stime = t.stime
|
22
|
-
@last_run = now
|
23
|
-
return res
|
24
45
|
end
|
25
46
|
end
|
26
47
|
end
|
@@ -1,39 +1,54 @@
|
|
1
|
-
module ScoutApm
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
res = get_mem_from_procfile
|
10
|
-
elsif platform =~ /darwin9/ # 10.5
|
11
|
-
res = get_mem_from_shell("ps -o rsz")
|
12
|
-
elsif platform =~ /darwin1[01]/ # 10.6 & 10.7
|
13
|
-
res = get_mem_from_shell("ps -o rss")
|
1
|
+
module ScoutApm
|
2
|
+
module Instruments
|
3
|
+
module Process
|
4
|
+
class ProcessMemory
|
5
|
+
attr_reader :logger
|
6
|
+
|
7
|
+
def initialize(logger)
|
8
|
+
@logger = logger
|
14
9
|
end
|
15
|
-
return res
|
16
|
-
end
|
17
10
|
|
18
|
-
|
11
|
+
def metric_name
|
12
|
+
"Memory/Physical"
|
13
|
+
end
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
proc_status = File.open(procfile, "r") { |f| f.read_nonblock(4096).strip }
|
23
|
-
if proc_status =~ /RSS:\s*(\d+) kB/i
|
24
|
-
res= $1.to_f / 1024.0
|
15
|
+
def human_name
|
16
|
+
"Process Memory"
|
25
17
|
end
|
26
|
-
res
|
27
|
-
end
|
28
18
|
|
29
|
-
|
30
|
-
|
31
|
-
|
19
|
+
def run
|
20
|
+
case RUBY_PLATFORM.downcase
|
21
|
+
when /linux/
|
22
|
+
get_mem_from_procfile
|
23
|
+
when /darwin9/ # 10.5
|
24
|
+
get_mem_from_shell("ps -o rsz")
|
25
|
+
when /darwin1[0123]/ # 10.6 - 10.10
|
26
|
+
get_mem_from_shell("ps -o rss")
|
27
|
+
else
|
28
|
+
0 # What default? was nil.
|
29
|
+
end.tap { |res| logger.debug "#{human_name}: #{res.inspect}" }
|
30
|
+
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
private
|
33
|
+
|
34
|
+
def get_mem_from_procfile
|
35
|
+
res = nil
|
36
|
+
proc_status = File.open(procfile, "r") { |f| f.read_nonblock(4096).strip }
|
37
|
+
if proc_status =~ /RSS:\s*(\d+) kB/i
|
38
|
+
res= $1.to_f / 1024.0
|
39
|
+
end
|
40
|
+
res
|
41
|
+
end
|
42
|
+
|
43
|
+
def procfile
|
44
|
+
"/proc/#{$$}/status"
|
45
|
+
end
|
46
|
+
|
47
|
+
# memory in MB the current process is using
|
48
|
+
def get_mem_from_shell(command)
|
49
|
+
res = `#{command} #{$$}`.split("\n")[1].to_f / 1024.0 #rescue nil
|
50
|
+
res
|
51
|
+
end
|
37
52
|
end
|
38
53
|
end
|
39
54
|
end
|
@@ -1,23 +1,25 @@
|
|
1
|
-
module ScoutApm
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
module ScoutApm
|
2
|
+
module Instruments
|
3
|
+
module ActionControllerInstruments
|
4
|
+
def self.included(instrumented_class)
|
5
|
+
ScoutApm::Agent.instance.logger.debug "Instrumenting #{instrumented_class.inspect}"
|
6
|
+
instrumented_class.class_eval do
|
7
|
+
unless instrumented_class.method_defined?(:perform_action_without_scout_instruments)
|
8
|
+
alias_method :perform_action_without_scout_instruments, :perform_action
|
9
|
+
alias_method :perform_action, :perform_action_with_scout_instruments
|
10
|
+
private :perform_action
|
11
|
+
end
|
10
12
|
end
|
11
13
|
end
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
|
15
|
+
# In addition to instrumenting actions, this also sets the scope to the controller action name. The scope is later
|
16
|
+
# applied to metrics recorded during this transaction. This lets us associate ActiveRecord calls with
|
17
|
+
# specific controller actions.
|
18
|
+
def perform_action_with_scout_instruments(*args, &block)
|
19
|
+
scout_controller_action = "Controller/#{controller_path}/#{action_name}"
|
20
|
+
self.class.scout_apm_trace(scout_controller_action, :uri => request.request_uri, :ip => request.remote_ip) do
|
21
|
+
perform_action_without_scout_instruments(*args, &block)
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
@@ -1,18 +1,20 @@
|
|
1
1
|
# Rails 3/4
|
2
|
-
module ScoutApm
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
2
|
+
module ScoutApm
|
3
|
+
module Instruments
|
4
|
+
module ActionControllerInstruments
|
5
|
+
# Instruments the action and tracks errors.
|
6
|
+
def process_action(*args)
|
7
|
+
scout_controller_action = "Controller/#{controller_path}/#{action_name}"
|
8
|
+
|
9
|
+
self.class.scout_apm_trace(scout_controller_action, :uri => request.fullpath, :ip => request.remote_ip) do
|
10
|
+
begin
|
11
|
+
super
|
12
|
+
rescue Exception
|
13
|
+
ScoutApm::Agent.instance.store.track!("Errors/Request",1, :scope => nil)
|
14
|
+
raise
|
15
|
+
ensure
|
16
|
+
Thread::current[:scout_apm_scope_name] = nil
|
17
|
+
end
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
@@ -36,3 +38,4 @@ if defined?(ActionView) && defined?(ActionView::PartialRenderer)
|
|
36
38
|
instrument_method :render_partial, :metric_name => 'View/#{@template.virtual_path}/Rendering', :scope => true
|
37
39
|
end
|
38
40
|
end
|
41
|
+
|
data/lib/scout_apm/layaway.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Stores metrics in a file before sending them to the server. Two uses:
|
2
2
|
# 1. A centralized store for multiple Agent processes. This way, only 1 checkin is sent to Scout rather than 1 per-process.
|
3
3
|
# 2. Bundling up reports from multiple timeslices to make updates more efficent server-side.
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# Data is stored in a Hash, where the keys are Time.to_i on the minute. The value is a Hash {:metrics => Hash, :slow_transactions => Array}.
|
6
6
|
# When depositing data, the new data is either merged with an existing time or placed in a new key.
|
7
7
|
class ScoutApm::Layaway
|
@@ -9,7 +9,7 @@ class ScoutApm::Layaway
|
|
9
9
|
def initialize
|
10
10
|
@file = ScoutApm::LayawayFile.new
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def deposit_and_deliver
|
14
14
|
new_metrics = ScoutApm::Agent.instance.store.metric_hash
|
15
15
|
log_deposited_metrics(new_metrics)
|
@@ -18,14 +18,14 @@ class ScoutApm::Layaway
|
|
18
18
|
file.read_and_write do |old_data|
|
19
19
|
old_data ||= Hash.new
|
20
20
|
# merge data
|
21
|
-
# if (1) there's data in the file and (2) there isn't any data yet for the current minute, this means we've
|
21
|
+
# if (1) there's data in the file and (2) there isn't any data yet for the current minute, this means we've
|
22
22
|
# collected all metrics for the previous slots and we're ready to deliver.
|
23
23
|
#
|
24
24
|
# Example w/2 processes:
|
25
25
|
#
|
26
26
|
# 12:00:34 ---
|
27
27
|
# Process 1: old_data.any? => false, so deposits.
|
28
|
-
# Process 2: old_data_any? => true and old_data[12:00].nil? => false, so deposits.
|
28
|
+
# Process 2: old_data_any? => true and old_data[12:00].nil? => false, so deposits.
|
29
29
|
#
|
30
30
|
# 12:01:34 ---
|
31
31
|
# Process 1: old_data.any? => true and old_data[12:01].nil? => true, so delivers metrics.
|
@@ -44,11 +44,11 @@ class ScoutApm::Layaway
|
|
44
44
|
end
|
45
45
|
to_deliver.any? ? validate_data(to_deliver) : {}
|
46
46
|
end
|
47
|
-
|
48
|
-
# Ensures the data we're sending to the server isn't stale.
|
49
|
-
# This can occur if the agent is collecting data, and app server goes down w/data in the local storage.
|
47
|
+
|
48
|
+
# Ensures the data we're sending to the server isn't stale.
|
49
|
+
# This can occur if the agent is collecting data, and app server goes down w/data in the local storage.
|
50
50
|
# When it is restarted later data will remain in local storage but it won't be for the current reporting interval.
|
51
|
-
#
|
51
|
+
#
|
52
52
|
# If the data is stale, an empty Hash is returned. Otherwise, the data from the most recent slot is returned.
|
53
53
|
def validate_data(data)
|
54
54
|
data = data.to_a.sort
|
@@ -63,14 +63,14 @@ class ScoutApm::Layaway
|
|
63
63
|
ScoutApm::Agent.instance.logger.debug $!.message
|
64
64
|
ScoutApm::Agent.instance.logger.debug $!.backtrace
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
# Data is stored under timestamp-keys (without the second).
|
68
68
|
def slot
|
69
69
|
t = Time.now
|
70
70
|
t -= t.sec
|
71
71
|
t.to_i
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
def log_deposited_metrics(new_metrics)
|
75
75
|
controller_count = 0
|
76
76
|
new_metrics.each do |meta,stats|
|
@@ -84,7 +84,7 @@ class ScoutApm::Layaway
|
|
84
84
|
def log_deposited_slow_transactions(new_slow_transactions)
|
85
85
|
ScoutApm::Agent.instance.logger.debug "Depositing #{new_slow_transactions.size} slow transactions into #{Time.at(slot).strftime("%m/%d/%y %H:%M:%S %z")} slot."
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
def log_saved_data(old_data,new_metrics)
|
89
89
|
ScoutApm::Agent.instance.logger.debug "Saving the following #{old_data.size} time slots locally:"
|
90
90
|
old_data.each do |k,v|
|
@@ -97,4 +97,4 @@ class ScoutApm::Layaway
|
|
97
97
|
ScoutApm::Agent.instance.logger.debug "#{Time.at(k).strftime("%m/%d/%y %H:%M:%S %z")} => #{controller_count} requests and #{v[:slow_transactions].size} slow transactions"
|
98
98
|
end
|
99
99
|
end
|
100
|
-
end
|
100
|
+
end
|