scout_apm 0.1.1 → 0.1.3
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 +14 -0
- data/lib/scout_apm.rb +1 -0
- data/lib/scout_apm/agent.rb +8 -6
- data/lib/scout_apm/agent/logging.rb +14 -5
- data/lib/scout_apm/agent/reporting.rb +11 -5
- data/lib/scout_apm/capacity.rb +54 -0
- data/lib/scout_apm/config.rb +13 -5
- data/lib/scout_apm/environment.rb +8 -0
- data/lib/scout_apm/layaway_file.rb +1 -1
- data/lib/scout_apm/tracer.rb +9 -1
- data/lib/scout_apm/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e446ee188c5294111023e7110aa649c3d1e0d6d
|
4
|
+
data.tar.gz: 28bf8811e6e819ea847b806ae3f5abf62c2531b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b174e8e7a4aa643a6b7ae8491aab27d969b2b64c3513ee32911b2045b2b4d9703cbca2a011404f7fd9c4557c6ed45948dce8d0119f55eafdcbc17950d34a188
|
7
|
+
data.tar.gz: 292f67f6b0d69de6aaf2582f53dfb1faae882baaf66127431566ba1c30692f1d2b5fd018e45df2fd9da664dacd037013d97d98805f9908880ec898b558ec1af2
|
data/CHANGELOG.markdown
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# 0.1.3
|
2
|
+
|
3
|
+
* Adds capacity calculation via "Instance/Capacity" metric.
|
4
|
+
* Tweaks tracing to still count a transaction if it results in a 500 error and includes it in accumulated time.
|
5
|
+
* Adds per-transaction error tracking (ex: Errors/Controller/widgets/index)
|
6
|
+
|
7
|
+
# 0.1.2
|
8
|
+
|
9
|
+
* Adds Heroku support:
|
10
|
+
* Detects Heroku via the 'DYNO' environment variable
|
11
|
+
* Defaults logger to STDOUT
|
12
|
+
* uses the dyno name vs. the hostname as the hostname
|
13
|
+
* Environment vars with "SCOUT_" prefix override any settings specified in the config file.
|
14
|
+
|
1
15
|
# 0.1.1
|
2
16
|
|
3
17
|
* Store the start time of slow requests.
|
data/lib/scout_apm.rb
CHANGED
@@ -24,6 +24,7 @@ require File.expand_path('../scout_apm/store.rb', __FILE__)
|
|
24
24
|
require File.expand_path('../scout_apm/tracer.rb', __FILE__)
|
25
25
|
require File.expand_path('../scout_apm/context.rb', __FILE__)
|
26
26
|
require File.expand_path('../scout_apm/slow_transaction.rb', __FILE__)
|
27
|
+
require File.expand_path('../scout_apm/capacity.rb', __FILE__)
|
27
28
|
require File.expand_path('../scout_apm/instruments/process/process_cpu.rb', __FILE__)
|
28
29
|
require File.expand_path('../scout_apm/instruments/process/process_memory.rb', __FILE__)
|
29
30
|
|
data/lib/scout_apm/agent.rb
CHANGED
@@ -5,8 +5,6 @@ module ScoutApm
|
|
5
5
|
# The worker thread wakes up every +Agent#period+, merges in-memory metrics w/those saved to disk,
|
6
6
|
# saves tshe merged data to disk, and sends it to the Scout server.
|
7
7
|
class Agent
|
8
|
-
# Headers passed up with all API requests.
|
9
|
-
HTTP_HEADERS = { "Agent-Hostname" => Socket.gethostname }
|
10
8
|
# see self.instance
|
11
9
|
@@instance = nil
|
12
10
|
|
@@ -15,7 +13,7 @@ module ScoutApm
|
|
15
13
|
attr_accessor :layaway
|
16
14
|
attr_accessor :config
|
17
15
|
attr_accessor :environment
|
18
|
-
|
16
|
+
attr_accessor :capacity
|
19
17
|
attr_accessor :logger
|
20
18
|
attr_accessor :log_file # path to the log file
|
21
19
|
attr_accessor :options # options passed to the agent when +#start+ is called.
|
@@ -38,6 +36,7 @@ module ScoutApm
|
|
38
36
|
@metric_lookup = Hash.new
|
39
37
|
@process_cpu=ScoutApm::Instruments::Process::ProcessCpu.new(environment.processors)
|
40
38
|
@process_memory=ScoutApm::Instruments::Process::ProcessMemory.new
|
39
|
+
@capacity = ScoutApm::Capacity.new
|
41
40
|
end
|
42
41
|
|
43
42
|
def environment
|
@@ -49,10 +48,13 @@ module ScoutApm
|
|
49
48
|
def start(options = {})
|
50
49
|
@options.merge!(options)
|
51
50
|
init_logger
|
52
|
-
logger.info "Attempting to start Scout Agent [#{ScoutApm::VERSION}] on [#{
|
53
|
-
if !config.
|
51
|
+
logger.info "Attempting to start Scout Agent [#{ScoutApm::VERSION}] on [#{environment.hostname}]"
|
52
|
+
if !config.value('monitor') and !@options[:force]
|
54
53
|
logger.warn "Monitoring isn't enabled for the [#{environment.env}] environment."
|
55
54
|
return false
|
55
|
+
elsif !config.value('name')
|
56
|
+
logger.warn "An application name is required. Specify the :name value in scout_apm.yml. Not starting agent."
|
57
|
+
return false
|
56
58
|
elsif !environment.app_server
|
57
59
|
logger.warn "Couldn't find a supported app server. Not starting agent."
|
58
60
|
return false
|
@@ -61,7 +63,7 @@ module ScoutApm
|
|
61
63
|
return false
|
62
64
|
end
|
63
65
|
@started = true
|
64
|
-
logger.info "Starting monitoring. Framework [#{environment.framework}] App Server [#{environment.app_server}]."
|
66
|
+
logger.info "Starting monitoring for [#{config.value('name')}]. Framework [#{environment.framework}] App Server [#{environment.app_server}]."
|
65
67
|
start_instruments
|
66
68
|
if !start_background_worker?
|
67
69
|
logger.debug "Not starting worker thread"
|
@@ -2,12 +2,13 @@
|
|
2
2
|
module ScoutApm
|
3
3
|
class Agent
|
4
4
|
module Logging
|
5
|
-
|
5
|
+
|
6
|
+
def default_log_path
|
6
7
|
"#{environment.root}/log"
|
7
8
|
end
|
8
9
|
|
9
10
|
def init_logger
|
10
|
-
@log_file = "#{
|
11
|
+
@log_file = wants_stdout? ? STDOUT : "#{log_file_path}/scout_apm.log"
|
11
12
|
begin
|
12
13
|
@logger = Logger.new(@log_file)
|
13
14
|
@logger.level = log_level
|
@@ -23,13 +24,13 @@ module ScoutApm
|
|
23
24
|
def apply_log_format
|
24
25
|
def logger.format_message(severity, timestamp, progname, msg)
|
25
26
|
# since STDOUT isn't exclusive like the scout_apm.log file, apply a prefix.
|
26
|
-
prefix = @logdev.dev == STDOUT ? "
|
27
|
-
prefix + "[#{timestamp.strftime("%m/%d/%y %H:%M:%S %z")} #{
|
27
|
+
prefix = @logdev.dev == STDOUT ? "[Scout] " : ''
|
28
|
+
prefix + "[#{timestamp.strftime("%m/%d/%y %H:%M:%S %z")} #{ScoutApm::Agent.instance.environment.hostname} (#{$$})] #{severity} : #{msg}\n"
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
32
|
def log_level
|
32
|
-
case config.
|
33
|
+
case config.value('log_level').downcase
|
33
34
|
when "debug" then Logger::DEBUG
|
34
35
|
when "info" then Logger::INFO
|
35
36
|
when "warn" then Logger::WARN
|
@@ -38,6 +39,14 @@ module ScoutApm
|
|
38
39
|
else Logger::INFO
|
39
40
|
end
|
40
41
|
end
|
42
|
+
|
43
|
+
def log_file_path
|
44
|
+
config.value('log_file_path') || default_log_path
|
45
|
+
end
|
46
|
+
|
47
|
+
def wants_stdout?
|
48
|
+
config.value('log_file_path').to_s.upcase == 'STDOUT' || environment.heroku?
|
49
|
+
end
|
41
50
|
end # module Logging
|
42
51
|
include Logging
|
43
52
|
end # class Agent
|
@@ -10,6 +10,7 @@ module ScoutApm
|
|
10
10
|
def process_metrics
|
11
11
|
logger.debug "Processing metrics"
|
12
12
|
run_samplers
|
13
|
+
capacity.process
|
13
14
|
payload = layaway.deposit_and_deliver
|
14
15
|
metrics = payload[:metrics]
|
15
16
|
slow_transactions = payload[:slow_transactions]
|
@@ -25,7 +26,7 @@ module ScoutApm
|
|
25
26
|
end
|
26
27
|
payload = Marshal.dump(:metrics => metrics, :slow_transactions => slow_transactions)
|
27
28
|
slow_transactions_kb = Marshal.dump(slow_transactions).size/1024 # just for performance debugging
|
28
|
-
logger.debug "#{config.
|
29
|
+
logger.debug "#{config.value('name')} Delivering total payload [#{payload.size/1024} KB] for #{controller_count} requests and slow transactions [#{slow_transactions_kb} KB] for #{slow_transactions.size} transactions of durations: #{slow_transactions.map(&:total_call_time).join(',')}."
|
29
30
|
response = post( checkin_uri,
|
30
31
|
payload,
|
31
32
|
"Content-Type" => "application/octet-stream" )
|
@@ -58,7 +59,7 @@ module ScoutApm
|
|
58
59
|
end
|
59
60
|
|
60
61
|
def checkin_uri
|
61
|
-
URI.parse("#{config.
|
62
|
+
URI.parse("#{config.value('host')}/apps/checkin.scout?key=#{config.value('key')}&name=#{CGI.escape(config.value('name'))}")
|
62
63
|
end
|
63
64
|
|
64
65
|
def post(uri, body, headers = Hash.new)
|
@@ -66,7 +67,7 @@ module ScoutApm
|
|
66
67
|
request(uri) do |connection|
|
67
68
|
post = Net::HTTP::Post.new( uri.path +
|
68
69
|
(uri.query ? ('?' + uri.query) : ''),
|
69
|
-
|
70
|
+
http_headers.merge(headers) )
|
70
71
|
post.body = body
|
71
72
|
response=connection.request(post)
|
72
73
|
end
|
@@ -81,7 +82,7 @@ module ScoutApm
|
|
81
82
|
when Net::HTTPSuccess, Net::HTTPNotModified
|
82
83
|
logger.debug "/checkin OK"
|
83
84
|
when Net::HTTPBadRequest
|
84
|
-
logger.warn "/checkin FAILED: The Account Key [#{config.
|
85
|
+
logger.warn "/checkin FAILED: The Account Key [#{config.value('key')}] is invalid."
|
85
86
|
else
|
86
87
|
logger.debug "/checkin FAILED: #{response.inspect}"
|
87
88
|
end
|
@@ -91,11 +92,16 @@ module ScoutApm
|
|
91
92
|
response
|
92
93
|
end
|
93
94
|
|
95
|
+
# Headers passed up with all API requests.
|
96
|
+
def http_headers
|
97
|
+
@http_headers ||= { "Agent-Hostname" => environment.hostname }
|
98
|
+
end
|
99
|
+
|
94
100
|
# Take care of the http proxy, if specified in config.
|
95
101
|
# Given a blank string, the proxy_uri URI instance's host/port/user/pass will be nil.
|
96
102
|
# Net::HTTP::Proxy returns a regular Net::HTTP class if the first argument (host) is nil.
|
97
103
|
def http(url)
|
98
|
-
proxy_uri = URI.parse(config.
|
104
|
+
proxy_uri = URI.parse(config.value('proxy').to_s)
|
99
105
|
http = Net::HTTP::Proxy(proxy_uri.host,proxy_uri.port,proxy_uri.user,proxy_uri.password).new(url.host, url.port)
|
100
106
|
if url.is_a?(URI::HTTPS)
|
101
107
|
http.use_ssl = true
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Encapsulates logic for determining capacity utilization of the Ruby processes.
|
2
|
+
class ScoutApm::Capacity
|
3
|
+
attr_reader :processing_start_time, :accumulated_time, :transaction_entry_time
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@processing_start_time = Time.now
|
7
|
+
@lock ||= Mutex.new # the transaction_entry_time could be modified while processing a request or when #process is called.
|
8
|
+
@accumulated_time = 0.0
|
9
|
+
end
|
10
|
+
|
11
|
+
# Called when a transaction is traced.
|
12
|
+
def start_transaction!
|
13
|
+
@lock.synchronize do
|
14
|
+
@transaction_entry_time = Time.now
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Called when a transaction completes to record its time used.
|
19
|
+
def finish_transaction!
|
20
|
+
@lock.synchronize do
|
21
|
+
if transaction_entry_time
|
22
|
+
@accumulated_time += (Time.now - transaction_entry_time).to_f
|
23
|
+
else
|
24
|
+
ScoutApm::Agent.instance.logger.warn "No transaction entry time. Not recording capacity metrics for transaction."
|
25
|
+
end
|
26
|
+
@transaction_entry_time = nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Ran when sending metrics to server. Reports capacity usage metrics.
|
31
|
+
def process
|
32
|
+
process_time = Time.now
|
33
|
+
ScoutApm::Agent.instance.logger.debug "Processing capacity usage for [#{@processing_start_time}] to [#{process_time}]. Time Spent: #{@accumulated_time}."
|
34
|
+
@lock.synchronize do
|
35
|
+
time_spent = @accumulated_time
|
36
|
+
@accumulated_time = 0.0
|
37
|
+
# If a transaction is still running, capture its running time up to now and
|
38
|
+
# reset the +transaction_entry_time+ to now.
|
39
|
+
if @transaction_entry_time
|
40
|
+
time_spent += (process_time - @transaction_entry_time).to_f
|
41
|
+
ScoutApm::Agent.instance.logger.debug "A transaction is running while calculating capacity. Start time: [#{transaction_entry_time}]. Will update the entry time to [#{process_time}]."
|
42
|
+
@transaction_entry_time = process_time # prevent from over-counting capacity usage. update the transaction start time to now.
|
43
|
+
end
|
44
|
+
time_spent = 0.0 if time_spent < 0.0
|
45
|
+
|
46
|
+
window = (process_time - processing_start_time).to_f # time period we are evaulating capacity usage.
|
47
|
+
window = 1.0 if window <= 0.0 # prevent divide-by-zero if clock adjusted.
|
48
|
+
capacity = time_spent / window
|
49
|
+
ScoutApm::Agent.instance.logger.debug "Instance/Capacity: #{capacity}"
|
50
|
+
ScoutApm::Agent.instance.store.track!("Instance/Capacity",capacity,:scope => nil)
|
51
|
+
@processing_start_time = process_time
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/scout_apm/config.rb
CHANGED
@@ -8,12 +8,15 @@ module ScoutApm
|
|
8
8
|
def initialize(config_path = nil)
|
9
9
|
@config_path = config_path
|
10
10
|
end
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
|
12
|
+
# Fetch a config value. It first attempts to fetch an ENV var prefixed with 'SCOUT_', then from the settings file.
|
13
|
+
def value(key)
|
14
|
+
value = ENV['SCOUT_'+key.upcase] || settings[key]
|
15
|
+
value.to_s.strip.length.zero? ? nil : value
|
15
16
|
end
|
16
|
-
|
17
|
+
|
18
|
+
private
|
19
|
+
|
17
20
|
def config_path
|
18
21
|
@config_path || File.join(ScoutApm::Agent.instance.environment.root,"config","scout_apm.yml")
|
19
22
|
end
|
@@ -21,6 +24,11 @@ module ScoutApm
|
|
21
24
|
def config_file
|
22
25
|
File.expand_path(config_path)
|
23
26
|
end
|
27
|
+
|
28
|
+
def settings
|
29
|
+
return @settings if @settings
|
30
|
+
load_file
|
31
|
+
end
|
24
32
|
|
25
33
|
def load_file
|
26
34
|
begin
|
@@ -50,6 +50,14 @@ module ScoutApm
|
|
50
50
|
'.'
|
51
51
|
end
|
52
52
|
end
|
53
|
+
|
54
|
+
def heroku?
|
55
|
+
ENV['DYNO']
|
56
|
+
end
|
57
|
+
|
58
|
+
def hostname
|
59
|
+
heroku? ? ENV['DYNO'] : Socket.gethostname
|
60
|
+
end
|
53
61
|
|
54
62
|
# This needs to be improved. Frequently, multiple app servers gem are present and which
|
55
63
|
# ever is checked first becomes the designated app server.
|
data/lib/scout_apm/tracer.rb
CHANGED
@@ -22,15 +22,23 @@ module ScoutApm::Tracer
|
|
22
22
|
ScoutApm::Agent.instance.store.reset_transaction!
|
23
23
|
ScoutApm::Context.current.add_user(:ip => options[:ip]) if options[:ip]
|
24
24
|
Thread::current[:scout_apm_trace_time] = Time.now.utc
|
25
|
+
ScoutApm::Agent.instance.capacity.start_transaction!
|
26
|
+
e = nil
|
25
27
|
instrument(metric_name, options) do
|
26
28
|
Thread::current[:scout_apm_scope_name] = metric_name
|
27
|
-
|
29
|
+
begin
|
30
|
+
yield
|
31
|
+
rescue Exception => e
|
32
|
+
ScoutApm::Agent.instance.store.track!("Errors/#{metric_name}",1, :scope => nil)
|
33
|
+
end
|
28
34
|
Thread::current[:scout_apm_scope_name] = nil
|
29
35
|
end
|
30
36
|
Thread::current[:scout_apm_trace_time] = nil
|
37
|
+
ScoutApm::Agent.instance.capacity.finish_transaction!
|
31
38
|
# The context is cleared after instrumentation (rather than before) as tracing controller-actions doesn't occur until the controller-action is called.
|
32
39
|
# It does not trace before filters, which is a likely spot to add context. This means that any context applied during before_filters would be cleared.
|
33
40
|
ScoutApm::Context.clear!
|
41
|
+
raise e if e
|
34
42
|
end
|
35
43
|
|
36
44
|
# Options:
|
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.1.
|
4
|
+
version: 0.1.3
|
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-
|
12
|
+
date: 2015-08-03 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Monitors Ruby apps and reports detailed metrics on performance to Scout.
|
15
15
|
email:
|
@@ -29,6 +29,7 @@ files:
|
|
29
29
|
- lib/scout_apm/agent/logging.rb
|
30
30
|
- lib/scout_apm/agent/reporting.rb
|
31
31
|
- lib/scout_apm/background_worker.rb
|
32
|
+
- lib/scout_apm/capacity.rb
|
32
33
|
- lib/scout_apm/config.rb
|
33
34
|
- lib/scout_apm/context.rb
|
34
35
|
- lib/scout_apm/environment.rb
|
@@ -70,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
71
|
version: '0'
|
71
72
|
requirements: []
|
72
73
|
rubyforge_project: scout_apm
|
73
|
-
rubygems_version: 2.
|
74
|
+
rubygems_version: 2.4.6
|
74
75
|
signing_key:
|
75
76
|
specification_version: 4
|
76
77
|
summary: Ruby application performance monitoring
|