scout_rails_proxy_proxy 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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