scout_rails_proxy_proxy 1.0.5

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.
@@ -0,0 +1,34 @@
1
+ module ScoutRailsProxy
2
+ class Config
3
+ def initialize(config_path = nil)
4
+ @config_path = config_path
5
+ end
6
+
7
+ def settings
8
+ return @settings if @settings
9
+ load_file
10
+ end
11
+
12
+ def config_path
13
+ @config_path || File.join(ScoutRailsProxy::Agent.instance.environment.root,"config","scout_rails_proxy.yml")
14
+ end
15
+
16
+ def config_file
17
+ File.expand_path(config_path)
18
+ end
19
+
20
+ def load_file
21
+ if !File.exist?(config_file)
22
+ ScoutRailsProxy::Agent.instance.logger.warn "No config file found at [#{config_file}]."
23
+ @settings = {}
24
+ else
25
+ @settings = YAML.load(ERB.new(File.read(config_file)).result(binding))[ScoutRailsProxy::Agent.instance.environment.env] || {}
26
+ end
27
+ rescue Exception => e
28
+ ScoutRailsProxy::Agent.instance.logger.warn "Unable to load the config file."
29
+ ScoutRailsProxy::Agent.instance.logger.warn e.message
30
+ ScoutRailsProxy::Agent.instance.logger.warn e.backtrace
31
+ @settings = {}
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,122 @@
1
+ # Used to retrieve environment information for this application.
2
+ module ScoutRailsProxy
3
+ class Environment
4
+ def env
5
+ @env ||= case framework
6
+ when :rails then RAILS_ENV.dup
7
+ when :rails3 then Rails.env
8
+ when :sinatra
9
+ ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
10
+ end
11
+ end
12
+
13
+ def framework
14
+ @framework ||= case
15
+ when defined?(::Rails) && defined?(ActionController)
16
+ if Rails::VERSION::MAJOR < 3
17
+ :rails
18
+ else
19
+ :rails3
20
+ end
21
+ when defined?(::Sinatra) && defined?(::Sinatra::Base) then :sinatra
22
+ else :ruby
23
+ end
24
+ end
25
+
26
+ def processors
27
+ return @processors if @processors
28
+ unless @processors
29
+ proc_file = '/proc/cpuinfo'
30
+ if !File.exist?(proc_file)
31
+ @processors = 1
32
+ elsif `cat #{proc_file} | grep 'model name' | wc -l` =~ /(\d+)/
33
+ @processors = $1.to_i
34
+ end
35
+ if @processors < 1
36
+ @processors = 1
37
+ end
38
+ end
39
+ @processors
40
+ end
41
+
42
+ def root
43
+ if framework == :rails
44
+ RAILS_ROOT.to_s
45
+ elsif framework == :rails3
46
+ Rails.root
47
+ elsif framework == :sinatra
48
+ Sinatra::Application.root
49
+ else
50
+ '.'
51
+ end
52
+ end
53
+
54
+ def app_server
55
+ @app_server ||= if thin? then :thin
56
+ elsif passenger? then :passenger
57
+ elsif webrick? then :webrick
58
+ elsif unicorn? then :unicorn
59
+ else nil
60
+ end
61
+ end
62
+
63
+ ### app server related-checks
64
+
65
+ def thin?
66
+ if defined?(::Thin) && defined?(::Thin::Server)
67
+ # Ensure Thin is actually initialized. It could just be required and not running.
68
+ ObjectSpace.each_object(Thin::Server) { |x| return true }
69
+ false
70
+ end
71
+ end
72
+
73
+ def unicorn?
74
+ if defined?(::Unicorn) && defined?(::Unicorn::HttpServer)
75
+ # Ensure Unicorn is actually initialized. It could just be required and not running.
76
+ ObjectSpace.each_object(::Unicorn::HttpServer) { |x| return true }
77
+ false
78
+ end
79
+ end
80
+
81
+ # Called via +#forking?+ since Passenger forks. Adds an event listener to start the worker thread
82
+ # inside the passenger worker process.
83
+ # Background: http://www.modrails.com/documentation/Users%20guide%20Nginx.html#spawning%5Fmethods%5Fexplained
84
+ def passenger?
85
+ (defined?(::Passenger) && defined?(::Passenger::AbstractServer)) || defined?(::IN_PHUSION_PASSENGER)
86
+ end
87
+
88
+ def webrick?
89
+ defined?(::WEBrick) && defined?(::WEBrick::VERSION)
90
+ end
91
+
92
+ def unicorn?
93
+ if defined?(::Unicorn) && defined?(::Unicorn::HttpServer)
94
+ # ensure Unicorn is actually initialized.
95
+ ObjectSpace.each_object(::Unicorn::HttpServer) { |x| return true }
96
+ end
97
+ end
98
+
99
+ # If forking, don't start worker thread in the master process. Since it's started as a Thread, it won't survive
100
+ # the fork.
101
+ def forking?
102
+ passenger? or unicorn?
103
+ end
104
+
105
+ ### ruby checks
106
+
107
+ def rubinius?
108
+ RUBY_VERSION =~ /rubinius/i
109
+ end
110
+
111
+ def jruby?
112
+ defined?(JRuby)
113
+ end
114
+
115
+ ### framework checks
116
+
117
+ def sinatra?
118
+ defined?(Sinatra::Application)
119
+ end
120
+
121
+ end # class Environemnt
122
+ end
@@ -0,0 +1,83 @@
1
+ module ScoutRailsProxy::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
+ ScoutRailsProxy::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), :desc => scout_sanitize_sql(sql)) do
19
+ log_without_scout_instruments(sql, name, &block)
20
+ end
21
+ end
22
+
23
+ def scout_ar_metric_name(sql,name)
24
+ if name && (parts = name.split " ") && parts.size == 2
25
+ model = parts.first
26
+ operation = parts.last.downcase
27
+ metric_name = case operation
28
+ when 'load' then 'find'
29
+ when 'indexes', 'columns' then nil # not under developer control
30
+ when 'destroy', 'find', 'save', 'create' then operation
31
+ when 'update' then 'save'
32
+ else
33
+ if model == 'Join'
34
+ operation
35
+ end
36
+ end
37
+ metric = "ActiveRecord/#{model}/#{metric_name}" if metric_name
38
+ metric = "ActiveRecord/SQL/other" if metric.nil?
39
+ else
40
+ metric = "ActiveRecord/SQL/Unknown"
41
+ end
42
+ metric
43
+ end
44
+
45
+ # Removes actual values from SQL. Used to both obfuscate the SQL and group
46
+ # similar queries in the UI.
47
+ def scout_sanitize_sql(sql)
48
+ return nil if sql.length > 1000 # safeguard - don't sanitize large SQL statements
49
+ sql = sql.dup
50
+ sql.gsub!(/\\"/, '') # removing escaping double quotes
51
+ sql.gsub!(/\\'/, '') # removing escaping single quotes
52
+ sql.gsub!(/'(?:[^']|'')*'/, '?') # removing strings (single quote)
53
+ sql.gsub!(/"(?:[^"]|"")*"/, '?') # removing strings (double quote)
54
+ sql.gsub!(/\b\d+\b/, '?') # removing integers
55
+ sql.gsub!(/\?(,\?)+/,'?') # replace multiple ? w/a single ?
56
+ sql
57
+ end
58
+
59
+ end # module ActiveRecordInstruments
60
+ end # module Instruments
61
+
62
+ def add_instruments
63
+ if defined?(ActiveRecord) && defined?(ActiveRecord::Base)
64
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
65
+ include ::ScoutRailsProxy::Instruments::ActiveRecordInstruments
66
+ include ::ScoutRailsProxy::Tracer
67
+ end
68
+ ActiveRecord::Base.class_eval do
69
+ include ::ScoutRailsProxy::Tracer
70
+ end
71
+ end
72
+ rescue
73
+ ScoutRailsProxy::Agent.instance.logger.warn "ActiveRecord instrumentation exception: #{$!.message}"
74
+ end
75
+
76
+ if defined?(::Rails) && ::Rails::VERSION::MAJOR.to_i == 3
77
+ Rails.configuration.after_initialize do
78
+ ScoutRailsProxy::Agent.instance.logger.debug "Adding ActiveRecord instrumentation to a Rails 3 app"
79
+ add_instruments
80
+ end
81
+ else
82
+ add_instruments
83
+ end
@@ -0,0 +1,14 @@
1
+ if defined?(::Net) && defined?(Net::HTTP)
2
+ ScoutRailsProxy::Agent.instance.logger.debug "Instrumenting Net::HTTP"
3
+ Net::HTTP.class_eval do
4
+ include ScoutRailsProxy::Tracer
5
+
6
+ def request_with_scout_instruments(*args,&block)
7
+ self.class.instrument("HTTP/request", :desc => "#{(@address+args.first.path.split('?').first)[0..99]}") do
8
+ request_without_scout_instruments(*args,&block)
9
+ end
10
+ end
11
+ alias request_without_scout_instruments request
12
+ alias request request_with_scout_instruments
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module ScoutRailsProxy::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 ScoutRailsProxy::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,46 @@
1
+ module ScoutRailsProxy::Instruments
2
+ module ActionControllerInstruments
3
+ def self.included(instrumented_class)
4
+ ScoutRailsProxy::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.trace(scout_controller_action, :uri => request.request_uri) do
20
+ perform_action_without_scout_instruments(*args, &block)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ if defined?(ActionController) && defined?(ActionController::Base)
27
+ ActionController::Base.class_eval do
28
+ include ScoutRailsProxy::Tracer
29
+ include ::ScoutRailsProxy::Instruments::ActionControllerInstruments
30
+
31
+ def rescue_action_with_scout(exception)
32
+ ScoutRailsProxy::Agent.instance.store.track!("Errors/Request",1, :scope => nil)
33
+ ScoutRailsProxy::Agent.instance.store.ignore_transaction!
34
+ rescue_action_without_scout exception
35
+ end
36
+
37
+ alias_method :rescue_action_without_scout, :rescue_action
38
+ alias_method :rescue_action, :rescue_action_with_scout
39
+ protected :rescue_action
40
+ end
41
+ ScoutRailsProxy::Agent.instance.logger.debug "Instrumenting ActionView::Template"
42
+ ActionView::Template.class_eval do
43
+ include ::ScoutRailsProxy::Tracer
44
+ instrument_method :render, :metric_name => 'View/#{path[%r{^(/.*/)?(.*)$},2]}/Rendering', :scope => true
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ # Rails 3
2
+ module ScoutRailsProxy::Instruments
3
+ module ActionControllerInstruments
4
+ # Instruments the action and tracks errors.
5
+ def process_action(*args)
6
+ scout_controller_action = "Controller/#{controller_path}/#{action_name}"
7
+ #ScoutRailsProxy::Agent.instance.logger.debug "Processing #{scout_controller_action}"
8
+ self.class.trace(scout_controller_action, :uri => request.fullpath) do
9
+ begin
10
+ super
11
+ rescue Exception => e
12
+ ScoutRailsProxy::Agent.instance.store.track!("Errors/Request",1, :scope => nil)
13
+ raise
14
+ ensure
15
+ Thread::current[:scout_scope_name] = nil
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ # ActionController::Base is a subclass of ActionController::Metal, so this instruments both
23
+ # standard Rails requests + Metal.
24
+ if defined?(ActionController) && defined?(ActionController::Metal)
25
+ ScoutRailsProxy::Agent.instance.logger.debug "Instrumenting ActionController::Metal"
26
+ ActionController::Metal.class_eval do
27
+ include ScoutRailsProxy::Tracer
28
+ include ::ScoutRailsProxy::Instruments::ActionControllerInstruments
29
+ end
30
+ end
31
+
32
+ if defined?(ActionView) && defined?(ActionView::PartialRenderer)
33
+ ScoutRailsProxy::Agent.instance.logger.debug "Instrumenting ActionView::PartialRenderer"
34
+ ActionView::PartialRenderer.class_eval do
35
+ include ScoutRailsProxy::Tracer
36
+ instrument_method :render_partial, :metric_name => 'View/#{@template.virtual_path}/Rendering', :scope => true
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ module ScoutRailsProxy::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.trace(scout_controller_action, :uri => @request.path_info) do
19
+ route_eval_without_scout_instruments(&blockarg)
20
+ end
21
+ end # route_eval_with_scout_instrumentss
22
+ end # SinatraInstruments
23
+ end # ScoutRailsProxy::Instruments
24
+
25
+ if defined?(::Sinatra) && defined?(::Sinatra::Base)
26
+ ScoutRailsProxy::Agent.instance.logger.debug "Instrumenting Sinatra"
27
+ ::Sinatra::Base.class_eval do
28
+ include ScoutRailsProxy::Tracer
29
+ include ::ScoutRailsProxy::Instruments::SinatraInstruments
30
+ alias route_eval_without_scout_instruments route_eval
31
+ alias route_eval route_eval_with_scout_instruments
32
+ end
33
+ end