scout_rails 0.0.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.
- data/.DS_Store +0 -0
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.markdown +29 -0
- data/Rakefile +1 -0
- data/lib/scout_rails.rb +31 -0
- data/lib/scout_rails/agent.rb +316 -0
- data/lib/scout_rails/config.rb +34 -0
- data/lib/scout_rails/environment.rb +86 -0
- data/lib/scout_rails/instruments/active_record_instruments.rb +88 -0
- data/lib/scout_rails/instruments/process/process_cpu.rb +27 -0
- data/lib/scout_rails/instruments/process/process_memory.rb +40 -0
- data/lib/scout_rails/instruments/rails/action_controller_instruments.rb +47 -0
- data/lib/scout_rails/instruments/rails3/action_controller_instruments.rb +35 -0
- data/lib/scout_rails/instruments/sinatra_instruments.rb +34 -0
- data/lib/scout_rails/layaway.rb +77 -0
- data/lib/scout_rails/layaway_file.rb +70 -0
- data/lib/scout_rails/metric_meta.rb +36 -0
- data/lib/scout_rails/metric_stats.rb +45 -0
- data/lib/scout_rails/stack_item.rb +18 -0
- data/lib/scout_rails/store.rb +78 -0
- data/lib/scout_rails/tracer.rb +90 -0
- data/lib/scout_rails/version.rb +3 -0
- data/scout_rails.gemspec +24 -0
- metadata +71 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module ScoutRails::Instruments
|
|
2
|
+
# Contains ActiveRecord instrument, aliasing +ActiveRecord::ConnectionAdapters::AbstractAdapter#log+ calls
|
|
3
|
+
# to trace calls to the database.
|
|
4
|
+
module ActiveRecordInstruments
|
|
5
|
+
def self.included(instrumented_class)
|
|
6
|
+
ScoutRails::Agent.instance.logger.debug "Instrumenting #{instrumented_class.inspect}"
|
|
7
|
+
instrumented_class.class_eval do
|
|
8
|
+
unless instrumented_class.method_defined?(:log_without_scout_instruments)
|
|
9
|
+
alias_method :log_without_scout_instruments, :log
|
|
10
|
+
alias_method :log, :log_with_scout_instruments
|
|
11
|
+
protected :log
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end # self.included
|
|
15
|
+
|
|
16
|
+
def log_with_scout_instruments(*args, &block)
|
|
17
|
+
sql, name = args
|
|
18
|
+
self.class.instrument(scout_ar_metric_name(sql,name)) do
|
|
19
|
+
log_without_scout_instruments(sql, name, &block)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Searches for the first AR model in the call stack. If found, adds it to a Hash of
|
|
24
|
+
# classes and methods to later instrument. Used to provide a better breakdown.
|
|
25
|
+
def scout_instrument_caller(called)
|
|
26
|
+
model_call = called.find { |call| call =~ /\/app\/models\/(.+)\.rb:\d+:in `(.+)'/ }
|
|
27
|
+
if model_call and !model_call.include?("without_scout_instrument")
|
|
28
|
+
set=ScoutRails::Agent.instance.dynamic_instruments[$1.camelcase] || Set.new
|
|
29
|
+
ScoutRails::Agent.instance.dynamic_instruments[$1.camelcase] = (set << $2)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Only instrument the caller if dynamic_instruments isn't disabled. By default, it is enabled.
|
|
34
|
+
def scout_dynamic?
|
|
35
|
+
dynamic=ScoutRails::Agent.instance.config.settings['dynamic_instruments']
|
|
36
|
+
dynamic.nil? or dynamic
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def scout_ar_metric_name(sql,name)
|
|
40
|
+
if name && (parts = name.split " ") && parts.size == 2
|
|
41
|
+
model = parts.first
|
|
42
|
+
# samples 10% of calls
|
|
43
|
+
if scout_dynamic? and rand*10 < 1
|
|
44
|
+
scout_instrument_caller(caller(10)[0..9]) # for performance, limits the number of call stack items to examine
|
|
45
|
+
end
|
|
46
|
+
operation = parts.last.downcase
|
|
47
|
+
metric_name = case operation
|
|
48
|
+
when 'load' then 'find'
|
|
49
|
+
when 'indexes', 'columns' then nil # not under developer control
|
|
50
|
+
when 'destroy', 'find', 'save', 'create' then operation
|
|
51
|
+
when 'update' then 'save'
|
|
52
|
+
else
|
|
53
|
+
if model == 'Join'
|
|
54
|
+
operation
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
metric = "ActiveRecord/#{model}/#{metric_name}" if metric_name
|
|
58
|
+
metric = "Database/SQL/other" if metric.nil?
|
|
59
|
+
else
|
|
60
|
+
metric = "Database/SQL/Unknown"
|
|
61
|
+
end
|
|
62
|
+
metric
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end # module ActiveRecordInstruments
|
|
66
|
+
end # module Instruments
|
|
67
|
+
|
|
68
|
+
def add_instruments
|
|
69
|
+
if defined?(ActiveRecord) && defined?(ActiveRecord::Base)
|
|
70
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
|
|
71
|
+
include ::ScoutRails::Instruments::ActiveRecordInstruments
|
|
72
|
+
include ::ScoutRails::Tracer
|
|
73
|
+
end
|
|
74
|
+
ActiveRecord::Base.class_eval do
|
|
75
|
+
include ::ScoutRails::Tracer
|
|
76
|
+
end
|
|
77
|
+
ScoutRails::Agent.instance.logger.debug "Dynamic instrumention is #{ActiveRecord::Base.connection.scout_dynamic? ? 'enabled' : 'disabled'}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i == 3
|
|
82
|
+
Rails.configuration.after_initialize do
|
|
83
|
+
ScoutRails::Agent.instance.logger.debug "Adding ActiveRecord instrumentation to a Rails 3 app"
|
|
84
|
+
add_instruments
|
|
85
|
+
end
|
|
86
|
+
else
|
|
87
|
+
add_instruments
|
|
88
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module ScoutRails::Instruments
|
|
2
|
+
module Process
|
|
3
|
+
class ProcessCpu
|
|
4
|
+
def initialize(num_processors)
|
|
5
|
+
@num_processors = num_processors || 1
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def run
|
|
9
|
+
res=nil
|
|
10
|
+
now = Time.now
|
|
11
|
+
t = ::Process.times
|
|
12
|
+
if @last_run
|
|
13
|
+
elapsed_time = now - @last_run
|
|
14
|
+
if elapsed_time >= 1
|
|
15
|
+
user_time_since_last_sample = t.utime - @last_utime
|
|
16
|
+
system_time_since_last_sample = t.stime - @last_stime
|
|
17
|
+
res = ((user_time_since_last_sample + system_time_since_last_sample)/(elapsed_time * @num_processors))*100
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
@last_utime = t.utime
|
|
21
|
+
@last_stime = t.stime
|
|
22
|
+
@last_run = now
|
|
23
|
+
return res
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module ScoutRails::Instruments
|
|
2
|
+
module Process
|
|
3
|
+
class ProcessMemory
|
|
4
|
+
def run
|
|
5
|
+
res=nil
|
|
6
|
+
platform = RUBY_PLATFORM.downcase
|
|
7
|
+
|
|
8
|
+
if platform =~ /linux/
|
|
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")
|
|
14
|
+
end
|
|
15
|
+
return res
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def get_mem_from_procfile
|
|
21
|
+
res = nil
|
|
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
|
|
25
|
+
end
|
|
26
|
+
res
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def procfile
|
|
30
|
+
"/proc/#{$$}/status"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# memory in MB the current process is using
|
|
34
|
+
def get_mem_from_shell(command)
|
|
35
|
+
res = `#{command} #{$$}`.split("\n")[1].to_f / 1024.0 #rescue nil
|
|
36
|
+
res
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module ScoutRails::Instruments
|
|
2
|
+
module ActionControllerInstruments
|
|
3
|
+
def self.included(instrumented_class)
|
|
4
|
+
ScoutRails::Agent.instance.logger.debug "Instrumenting #{instrumented_class.inspect}"
|
|
5
|
+
instrumented_class.class_eval do
|
|
6
|
+
unless instrumented_class.method_defined?(:perform_action_without_scout_instruments)
|
|
7
|
+
alias_method :perform_action_without_scout_instruments, :perform_action
|
|
8
|
+
alias_method :perform_action, :perform_action_with_scout_instruments
|
|
9
|
+
private :perform_action
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end # self.included
|
|
13
|
+
|
|
14
|
+
# In addition to instrumenting actions, this also sets the scope to the controller action name. The scope is later
|
|
15
|
+
# applied to metrics recorded during this transaction. This lets us associate ActiveRecord calls with
|
|
16
|
+
# specific controller actions.
|
|
17
|
+
def perform_action_with_scout_instruments(*args, &block)
|
|
18
|
+
scout_controller_action = "Controller/#{controller_path}/#{action_name}"
|
|
19
|
+
self.class.instrument(scout_controller_action) do
|
|
20
|
+
Thread::current[:scout_scope_name] = scout_controller_action
|
|
21
|
+
perform_action_without_scout_instruments(*args, &block)
|
|
22
|
+
Thread::current[:scout_scope_name] = nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if defined?(ActionController) && defined?(ActionController::Base)
|
|
29
|
+
ActionController::Base.class_eval do
|
|
30
|
+
include ScoutRails::Tracer
|
|
31
|
+
include ::ScoutRails::Instruments::ActionControllerInstruments
|
|
32
|
+
|
|
33
|
+
def rescue_action_with_scout(exception)
|
|
34
|
+
ScoutRails::Agent.instance.store.track!("Errors/Request",1, :scope => nil)
|
|
35
|
+
rescue_action_without_scout exception
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
alias_method :rescue_action_without_scout, :rescue_action
|
|
39
|
+
alias_method :rescue_action, :rescue_action_with_scout
|
|
40
|
+
protected :rescue_action
|
|
41
|
+
end
|
|
42
|
+
ScoutRails::Agent.instance.logger.debug "Instrumenting ActionView::Template"
|
|
43
|
+
ActionView::Template.class_eval do
|
|
44
|
+
include ::ScoutRails::Tracer
|
|
45
|
+
instrument_method :render, 'View/#{path[%r{^(/.*/)?(.*)$},2]}/Rendering'
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module ScoutRails::Instruments
|
|
2
|
+
module ActionControllerInstruments
|
|
3
|
+
# Instruments the action and tracks errors.
|
|
4
|
+
def process_action(*args)
|
|
5
|
+
scout_controller_action = "Controller/#{controller_path}/#{action_name}"
|
|
6
|
+
self.class.instrument(scout_controller_action) do
|
|
7
|
+
Thread::current[:scout_scope_name] = scout_controller_action
|
|
8
|
+
begin
|
|
9
|
+
super
|
|
10
|
+
rescue Exception => e
|
|
11
|
+
ScoutRails::Agent.instance.store.track!("Errors/Request",1, :scope => nil)
|
|
12
|
+
raise
|
|
13
|
+
ensure
|
|
14
|
+
Thread::current[:scout_scope_name] = nil
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if defined?(ActionController) && defined?(ActionController::Base)
|
|
22
|
+
ScoutRails::Agent.instance.logger.debug "Instrumenting ActionController::Base"
|
|
23
|
+
ActionController::Base.class_eval do
|
|
24
|
+
include ScoutRails::Tracer
|
|
25
|
+
include ::ScoutRails::Instruments::ActionControllerInstruments
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if defined?(ActionView) && defined?(ActionView::PartialRenderer)
|
|
30
|
+
ScoutRails::Agent.instance.logger.debug "Instrumenting ActionView::PartialRenderer"
|
|
31
|
+
ActionView::PartialRenderer.class_eval do
|
|
32
|
+
include ScoutRails::Tracer
|
|
33
|
+
instrument_method :render_partial, 'View/#{@template.virtual_path}/Rendering'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module ScoutRails::Instruments
|
|
2
|
+
module SinatraInstruments
|
|
3
|
+
def route_eval_with_scout_instruments(&blockarg)
|
|
4
|
+
path = unescape(@request.path_info)
|
|
5
|
+
name = path
|
|
6
|
+
# Go through each route and look for a match
|
|
7
|
+
if routes = self.class.routes[@request.request_method]
|
|
8
|
+
routes.detect do |pattern, keys, conditions, block|
|
|
9
|
+
if blockarg.equal? block
|
|
10
|
+
name = pattern.source
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
name.gsub!(%r{^[/^]*(.*?)[/\$\?]*$}, '\1')
|
|
15
|
+
name = 'root' if name.empty?
|
|
16
|
+
name = @request.request_method + ' ' + name if @request && @request.respond_to?(:request_method)
|
|
17
|
+
scout_controller_action = "Controller/Sinatra/#{name}"
|
|
18
|
+
self.class.instrument(scout_controller_action) do
|
|
19
|
+
Thread::current[:scout_scope_name] = scout_controller_action
|
|
20
|
+
route_eval_without_scout_instruments(&blockarg)
|
|
21
|
+
end
|
|
22
|
+
end # route_eval_with_scout_instrumentss
|
|
23
|
+
end # SinatraInstruments
|
|
24
|
+
end # ScoutRails::Instruments
|
|
25
|
+
|
|
26
|
+
if defined?(::Sinatra) && defined?(::Sinatra::Base)
|
|
27
|
+
ScoutRails::Agent.instance.logger.debug "Instrumenting Sinatra"
|
|
28
|
+
::Sinatra::Base.class_eval do
|
|
29
|
+
include ScoutRails::Tracer
|
|
30
|
+
include ::ScoutRails::Instruments::SinatraInstruments
|
|
31
|
+
alias route_eval_without_scout_instruments route_eval
|
|
32
|
+
alias route_eval route_eval_with_scout_instruments
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Stores metrics in a file before sending them to the server. Two uses:
|
|
2
|
+
# 1. A centralized store for multiple Agent processes. This way, only 1 checkin is sent to Scout rather than 1 per-process.
|
|
3
|
+
# 2. Bundling up reports from multiple timeslices to make updates more efficent server-side.
|
|
4
|
+
#
|
|
5
|
+
# Metrics are stored in a Hash, where the keys are Time.to_i on the minute. When depositing data,
|
|
6
|
+
# metrics are either merged with an existing time or placed in a new key.
|
|
7
|
+
class ScoutRails::Layaway
|
|
8
|
+
attr_accessor :file
|
|
9
|
+
def initialize
|
|
10
|
+
@file = ScoutRails::LayawayFile.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def deposit_and_deliver
|
|
14
|
+
new_data = ScoutRails::Agent.instance.store.metric_hash
|
|
15
|
+
controller_count = 0
|
|
16
|
+
new_data.each do |meta,stats|
|
|
17
|
+
if meta.metric_name =~ /\AController/
|
|
18
|
+
controller_count += stats.call_count
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
ScoutRails::Agent.instance.logger.debug "Depositing #{controller_count} requests into #{Time.at(slot).strftime("%m/%d/%y %H:%M:%S %z")} slot."
|
|
22
|
+
|
|
23
|
+
to_deliver = {}
|
|
24
|
+
file.read_and_write do |old_data|
|
|
25
|
+
old_data ||= Hash.new
|
|
26
|
+
# merge data
|
|
27
|
+
# if the previous minute has ended, its time to send those metrics
|
|
28
|
+
if old_data.any? and old_data[slot].nil?
|
|
29
|
+
to_deliver = old_data
|
|
30
|
+
old_data = Hash.new
|
|
31
|
+
elsif old_data.any?
|
|
32
|
+
ScoutRails::Agent.instance.logger.debug "Not yet time to deliver metrics for slot [#{Time.at(old_data.keys.sort.last).strftime("%m/%d/%y %H:%M:%S %z")}]"
|
|
33
|
+
else
|
|
34
|
+
ScoutRails::Agent.instance.logger.debug "There is no data in the layaway file to deliver."
|
|
35
|
+
end
|
|
36
|
+
old_data[slot]=ScoutRails::Agent.instance.store.merge_data_and_clear(old_data[slot] || Hash.new)
|
|
37
|
+
ScoutRails::Agent.instance.logger.debug "Saving the following #{old_data.size} time slots locally:"
|
|
38
|
+
old_data.each do |k,v|
|
|
39
|
+
controller_count = 0
|
|
40
|
+
new_data.each do |meta,stats|
|
|
41
|
+
if meta.metric_name =~ /\AController/
|
|
42
|
+
controller_count += stats.call_count
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
ScoutRails::Agent.instance.logger.debug "#{Time.at(k).strftime("%m/%d/%y %H:%M:%S %z")} => #{controller_count} requests"
|
|
46
|
+
end
|
|
47
|
+
old_data
|
|
48
|
+
end
|
|
49
|
+
to_deliver.any? ? validate_data(to_deliver) : {}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Ensures the data we're sending to the server isn't stale.
|
|
53
|
+
# This can occur if the agent is collecting data, and app server goes down w/data in the local storage.
|
|
54
|
+
# When it is restarted later data will remain in local storage but it won't be for the current reporting interval.
|
|
55
|
+
#
|
|
56
|
+
# If the data is stale, an empty Hash is returned. Otherwise, the data from the most recent slot is returned.
|
|
57
|
+
def validate_data(data)
|
|
58
|
+
data = data.to_a.sort
|
|
59
|
+
now = Time.now
|
|
60
|
+
if (most_recent = data.first.first) < now.to_i - 2*60
|
|
61
|
+
ScoutRails::Agent.instance.logger.debug "Local Storage is stale (#{Time.at(most_recent).strftime("%m/%d/%y %H:%M:%S %z")}). Not sending data."
|
|
62
|
+
{}
|
|
63
|
+
else
|
|
64
|
+
#ScoutRails::Agent.instance.logger.debug "Validated time slot: #{Time.at(most_recent).strftime("%m/%d/%y %H:%M:%S %z")}"
|
|
65
|
+
data.first.last
|
|
66
|
+
end
|
|
67
|
+
rescue
|
|
68
|
+
ScoutRails::Agent.instance.logger.debug $!.message
|
|
69
|
+
ScoutRails::Agent.instance.logger.debug $!.backtrace
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def slot
|
|
73
|
+
t = Time.now
|
|
74
|
+
t -= t.sec
|
|
75
|
+
t.to_i
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Logic for the serialized file access
|
|
2
|
+
class ScoutRails::LayawayFile
|
|
3
|
+
def path
|
|
4
|
+
"#{ScoutRails::Agent.instance.log_path}/scout_rails.db"
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def dump(object)
|
|
8
|
+
Marshal.dump(object)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def load(dump)
|
|
12
|
+
if dump.size == 0
|
|
13
|
+
ScoutRails::Agent.instance.logger.debug("No data in layaway file.")
|
|
14
|
+
return nil
|
|
15
|
+
end
|
|
16
|
+
Marshal.load(dump)
|
|
17
|
+
rescue ArgumentError, TypeError => e
|
|
18
|
+
ScoutRails::Agent.instance.logger.debug("Error loading data from layaway file: #{e.inspect}")
|
|
19
|
+
ScoutRails::Agent.instance.logger.debug(e.backtrace.inspect)
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def read_and_write
|
|
24
|
+
File.open(path, File::RDWR | File::CREAT) do |f|
|
|
25
|
+
f.flock(File::LOCK_EX)
|
|
26
|
+
begin
|
|
27
|
+
result = (yield get_data(f))
|
|
28
|
+
f.rewind
|
|
29
|
+
f.truncate(0)
|
|
30
|
+
if result
|
|
31
|
+
write(f, dump(result))
|
|
32
|
+
end
|
|
33
|
+
ensure
|
|
34
|
+
f.flock(File::LOCK_UN)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
rescue Errno::ENOENT, Exception => e
|
|
38
|
+
ScoutRails::Agent.instance.logger.error(e.message)
|
|
39
|
+
ScoutRails::Agent.instance.logger.debug(e.backtrace.split("\n"))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get_data(f)
|
|
43
|
+
data = read_until_end(f)
|
|
44
|
+
result = load(data)
|
|
45
|
+
f.truncate(0)
|
|
46
|
+
result
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def write(f, string)
|
|
50
|
+
result = 0
|
|
51
|
+
while (result < string.length)
|
|
52
|
+
result += f.write_nonblock(string)
|
|
53
|
+
end
|
|
54
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
|
55
|
+
IO.select(nil, [f])
|
|
56
|
+
retry
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def read_until_end(f)
|
|
60
|
+
contents = ""
|
|
61
|
+
while true
|
|
62
|
+
contents << f.read_nonblock(10_000)
|
|
63
|
+
end
|
|
64
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
|
65
|
+
IO.select([f])
|
|
66
|
+
retry
|
|
67
|
+
rescue EOFError
|
|
68
|
+
contents
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Contains the meta information associated with a metric. Used to lookup Metrics in to Store's metric_hash.
|
|
2
|
+
class ScoutRails::MetricMeta
|
|
3
|
+
def initialize(metric_name)
|
|
4
|
+
@metric_name = metric_name
|
|
5
|
+
@metric_id = nil
|
|
6
|
+
@scope = Thread::current[:scout_scope_name]
|
|
7
|
+
end
|
|
8
|
+
attr_accessor :metric_id, :metric_name
|
|
9
|
+
attr_accessor :scope
|
|
10
|
+
attr_accessor :client_id
|
|
11
|
+
|
|
12
|
+
# To avoid conflicts with different JSON libaries
|
|
13
|
+
def to_json(*a)
|
|
14
|
+
%Q[{"metric_id":#{metric_id || 'null'},"metric_name":#{metric_name.to_json},"scope":#{scope.to_json || 'null'}}]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def ==(o)
|
|
18
|
+
self.eql?(o)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def hash
|
|
22
|
+
h = metric_name.hash
|
|
23
|
+
h ^= scope.hash unless scope.nil?
|
|
24
|
+
h
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def <=>(o)
|
|
28
|
+
namecmp = self.name <=> o.name
|
|
29
|
+
return namecmp if namecmp != 0
|
|
30
|
+
return (self.scope || '') <=> (o.scope || '')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def eql?(o)
|
|
34
|
+
self.class == o.class && metric_name.eql?(o.metric_name) && scope == o.scope && client_id == o.client_id
|
|
35
|
+
end
|
|
36
|
+
end # class MetricMeta
|