scout_rails 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/.DS_Store
ADDED
Binary file
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# ScoutRails
|
2
|
+
|
3
|
+
A Ruby gem for detailed Rails application performance analysis. Metrics are reported to [Scout](https://scoutapp.com), a hosted server and application monitoring service. For general server monitoring, see our [server monitoring agent](https://github.com/highgroove/scout-client).
|
4
|
+
|
5
|
+
## Getting Started
|
6
|
+
|
7
|
+
Install the gem:
|
8
|
+
|
9
|
+
gem install scout_rails
|
10
|
+
|
11
|
+
Signup for a [Scout](https://scoutapp.com) account and copy the config file to RAILS_ROOT/config/scout_rails.yml.
|
12
|
+
|
13
|
+
## Supported Frameworks
|
14
|
+
|
15
|
+
* Rails 2.2 and greater
|
16
|
+
|
17
|
+
## Supported Rubies
|
18
|
+
|
19
|
+
* Ruby 1.8.7
|
20
|
+
* Ruby 1.9.2
|
21
|
+
|
22
|
+
## Supported Application Servers
|
23
|
+
|
24
|
+
* Phusion Passenger
|
25
|
+
* Thin
|
26
|
+
* WEBrick
|
27
|
+
|
28
|
+
|
29
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/scout_rails.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module ScoutRails
|
2
|
+
end
|
3
|
+
require 'socket'
|
4
|
+
require 'set'
|
5
|
+
require 'net/http'
|
6
|
+
require File.expand_path('../scout_rails/version.rb', __FILE__)
|
7
|
+
require File.expand_path('../scout_rails/agent.rb', __FILE__)
|
8
|
+
require File.expand_path('../scout_rails/layaway.rb', __FILE__)
|
9
|
+
require File.expand_path('../scout_rails/layaway_file.rb', __FILE__)
|
10
|
+
require File.expand_path('../scout_rails/config.rb', __FILE__)
|
11
|
+
require File.expand_path('../scout_rails/environment.rb', __FILE__)
|
12
|
+
require File.expand_path('../scout_rails/metric_meta.rb', __FILE__)
|
13
|
+
require File.expand_path('../scout_rails/metric_stats.rb', __FILE__)
|
14
|
+
require File.expand_path('../scout_rails/stack_item.rb', __FILE__)
|
15
|
+
require File.expand_path('../scout_rails/store.rb', __FILE__)
|
16
|
+
require File.expand_path('../scout_rails/tracer.rb', __FILE__)
|
17
|
+
require File.expand_path('../scout_rails/instruments/process/process_cpu.rb', __FILE__)
|
18
|
+
require File.expand_path('../scout_rails/instruments/process/process_memory.rb', __FILE__)
|
19
|
+
|
20
|
+
if defined?(Rails) and Rails.respond_to?(:version) and Rails.version =~ /^3/
|
21
|
+
module ScoutRails
|
22
|
+
class Railtie < Rails::Railtie
|
23
|
+
initializer "scout_rails.start" do |app|
|
24
|
+
ScoutRails::Agent.instance.start
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
else
|
29
|
+
ScoutRails::Agent.instance.start
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,316 @@
|
|
1
|
+
module ScoutRails
|
2
|
+
# The agent gathers performance data from a Ruby application. One Agent instance is created per-Ruby process.
|
3
|
+
#
|
4
|
+
# Each Agent object creates a worker thread (unless monitoring is disabled or we're forking).
|
5
|
+
# The worker thread wakes up every +Agent#period+, merges in-memory metrics w/those saved to disk,
|
6
|
+
# saves the merged data to disk, and sends it to the Scout server.
|
7
|
+
class Agent
|
8
|
+
# Headers passed up with all API requests.
|
9
|
+
HTTP_HEADERS = { "Agent-Hostname" => Socket.gethostname }
|
10
|
+
DEFAULT_HOST = 'scoutapp.com'
|
11
|
+
# see self.instance
|
12
|
+
@@instance = nil
|
13
|
+
|
14
|
+
# Accessors below are for associated classes
|
15
|
+
attr_accessor :store
|
16
|
+
attr_accessor :layaway
|
17
|
+
attr_accessor :config
|
18
|
+
attr_accessor :environment
|
19
|
+
|
20
|
+
attr_accessor :logger
|
21
|
+
attr_accessor :log_file # path to the log file
|
22
|
+
attr_accessor :options # options passed to the agent when +#start+ is called.
|
23
|
+
attr_accessor :metric_lookup # Hash used to lookup metric ids based on their name and scope
|
24
|
+
|
25
|
+
# Hash of class (values are Sets of method names) that should be instrumented by examining
|
26
|
+
# the ActiveRecord call stack.
|
27
|
+
attr_accessor :dynamic_instruments
|
28
|
+
|
29
|
+
# All access to the agent is thru this class method to ensure multiple Agent instances are not initialized per-Ruby process.
|
30
|
+
def self.instance(options = {})
|
31
|
+
@@instance ||= self.new(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Note - this doesn't start instruments or the worker thread. This is handled via +#start+ as we don't
|
35
|
+
# want to start the worker thread or install instrumentation if (1) disabled for this environment (2) a worker thread shouldn't
|
36
|
+
# be started (when forking).
|
37
|
+
def initialize(options = {})
|
38
|
+
@started = false
|
39
|
+
@options ||= options
|
40
|
+
@store = ScoutRails::Store.new
|
41
|
+
@layaway = ScoutRails::Layaway.new
|
42
|
+
@config = ScoutRails::Config.new(options[:config_path])
|
43
|
+
@dynamic_instruments = Hash.new
|
44
|
+
@metric_lookup = Hash.new
|
45
|
+
@process_cpu=ScoutRails::Instruments::Process::ProcessCpu.new(1) # TODO: the argument is the number of processors
|
46
|
+
@process_memory=ScoutRails::Instruments::Process::ProcessMemory.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def environment
|
50
|
+
@environment ||= ScoutRails::Environment.new
|
51
|
+
end
|
52
|
+
|
53
|
+
# This is called via +ScoutRails::Agent.instance.start+ when ScoutRails is required in a Ruby application.
|
54
|
+
# It initializes the agent and starts the worker thread (if appropiate).
|
55
|
+
def start(options = {})
|
56
|
+
@options.merge!(options)
|
57
|
+
init_logger
|
58
|
+
if !config.settings['monitor'] and !@options[:force]
|
59
|
+
logger.warn "Monitoring isn't enabled for the [#{environment.env}] environment."
|
60
|
+
return false
|
61
|
+
elsif !environment.app_server
|
62
|
+
logger.warn "Couldn't find a supported app server. Not starting agent."
|
63
|
+
return false
|
64
|
+
elsif started?
|
65
|
+
logger.warn "Already started agent."
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
@started = true
|
69
|
+
logger.info "Starting monitoring. Framework [#{environment.framework}] App Server [#{environment.app_server}]."
|
70
|
+
start_instruments
|
71
|
+
if !start_worker_thread?
|
72
|
+
logger.debug "Not starting worker thread"
|
73
|
+
install_passenger_worker_process_event if environment.passenger?
|
74
|
+
return
|
75
|
+
end
|
76
|
+
start_worker_thread
|
77
|
+
handle_exit
|
78
|
+
log_version_pid
|
79
|
+
end
|
80
|
+
|
81
|
+
# Placeholder: store metrics locally on exit so those in memory aren't lost. Need to decide
|
82
|
+
# whether we'll report these immediately or just store locally and risk having stale data.
|
83
|
+
def handle_exit
|
84
|
+
if environment.sinatra? || environment.jruby? || environment.rubinius?
|
85
|
+
logger.debug "Exit handler not supported"
|
86
|
+
else
|
87
|
+
at_exit { at_exit { logger.debug "Shutdown!" } }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def started?
|
92
|
+
@started
|
93
|
+
end
|
94
|
+
|
95
|
+
def gem_root
|
96
|
+
File.expand_path(File.join("..","..",".."), __FILE__)
|
97
|
+
end
|
98
|
+
|
99
|
+
def init_logger
|
100
|
+
@log_file = "#{log_path}/scout_rails.log"
|
101
|
+
@logger = Logger.new(@log_file)
|
102
|
+
@logger.level = Logger::DEBUG
|
103
|
+
def logger.format_message(severity, timestamp, progname, msg)
|
104
|
+
prefix = "[#{timestamp.strftime("%m/%d/%y %H:%M:%S %z")} #{Socket.gethostname} (#{$$})] #{severity} : #{msg}\n"
|
105
|
+
end
|
106
|
+
@logger
|
107
|
+
end
|
108
|
+
|
109
|
+
# The worker thread will automatically start UNLESS:
|
110
|
+
# * A supported application server isn't detected (example: running via Rails console)
|
111
|
+
# * A supported application server is detected, but it forks (Passenger). In this case,
|
112
|
+
# the agent is started in the forked process.
|
113
|
+
def start_worker_thread?
|
114
|
+
!environment.forking? or environment.thin?
|
115
|
+
end
|
116
|
+
|
117
|
+
def install_passenger_worker_process_event
|
118
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
119
|
+
logger.debug "Passenger is starting a worker process. Starting worker thread."
|
120
|
+
self.class.instance.start_worker_thread
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def log_version_pid
|
125
|
+
logger.debug "Scout Agent [#{ScoutRails::VERSION}] Initialized"
|
126
|
+
end
|
127
|
+
|
128
|
+
def log_path
|
129
|
+
"#{environment.root}/log"
|
130
|
+
end
|
131
|
+
|
132
|
+
# in seconds, time between when the worker thread wakes up and runs.
|
133
|
+
def period
|
134
|
+
60
|
135
|
+
end
|
136
|
+
|
137
|
+
# Every time the worker thread completes, it calls this method
|
138
|
+
# to add instruments to selected methods in the AR call stack.
|
139
|
+
def add_dynamic_instruments
|
140
|
+
dynamic_instruments.each do |class_name,to_instrument|
|
141
|
+
klass = class_name.constantize
|
142
|
+
klass.class_eval do
|
143
|
+
if !klass.respond_to?(:instrument_method)
|
144
|
+
include ::ScoutRails::Tracer
|
145
|
+
end
|
146
|
+
to_instrument.each do |m|
|
147
|
+
self.instrument_method(m)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Creates the worker thread. The worker thread is a loop that runs continuously. It sleeps for +Agent#period+ and when it wakes,
|
154
|
+
# processes data, either saving it to disk or reporting to Scout.
|
155
|
+
def start_worker_thread(connection_options = {})
|
156
|
+
logger.debug "Creating worker thread."
|
157
|
+
@worker_thread = Thread.new do
|
158
|
+
begin
|
159
|
+
logger.debug "Starting worker thread, running every #{period} seconds"
|
160
|
+
next_time = Time.now + period
|
161
|
+
while true do
|
162
|
+
now = Time.now
|
163
|
+
while now < next_time
|
164
|
+
sleep_time = next_time - now
|
165
|
+
sleep(sleep_time) if sleep_time > 0
|
166
|
+
now = Time.now
|
167
|
+
end
|
168
|
+
process_metrics
|
169
|
+
add_dynamic_instruments
|
170
|
+
while next_time <= now
|
171
|
+
next_time += period
|
172
|
+
end
|
173
|
+
end
|
174
|
+
rescue
|
175
|
+
logger.debug "Worker Thread Exception!!!!!!!"
|
176
|
+
logger.debug $!.message
|
177
|
+
logger.debug $!.backtrace
|
178
|
+
end
|
179
|
+
end # thread new
|
180
|
+
logger.debug "Done creating worker thread."
|
181
|
+
end
|
182
|
+
|
183
|
+
# Writes each payload to a file for auditing.
|
184
|
+
def write_to_file(object)
|
185
|
+
logger.debug "Writing to file"
|
186
|
+
full_path = Pathname.new(RAILS_ROOT+'/log/audit/scout')
|
187
|
+
( full_path +
|
188
|
+
"#{Time.now.strftime('%Y-%m-%d_%H:%M:%S')}.json" ).open("w") do |f|
|
189
|
+
f.puts object.to_json
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Before reporting, lookup metric_id for each MetricMeta. This speeds up
|
194
|
+
# reporting on the server-side.
|
195
|
+
def add_metric_ids(metrics)
|
196
|
+
metrics.each do |meta,stats|
|
197
|
+
if metric_id = metric_lookup[meta]
|
198
|
+
meta.metric_id = metric_id
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Called from #process_metrics, which is run via the worker thread.
|
204
|
+
def run_samplers
|
205
|
+
begin
|
206
|
+
cpu_util=@process_cpu.run # returns a hash
|
207
|
+
logger.debug "Process CPU: #{cpu_util.inspect}"
|
208
|
+
store.track!("CPU/Utilization",cpu_util) if cpu_util
|
209
|
+
rescue => e
|
210
|
+
logger.info "Error reading ProcessCpu"
|
211
|
+
logger.debug e.message
|
212
|
+
logger.debug e.backtrace.join("\n")
|
213
|
+
end
|
214
|
+
|
215
|
+
begin
|
216
|
+
mem_usage=@process_memory.run # returns a single number, in MB
|
217
|
+
logger.debug "Process Memory: #{mem_usage}MB"
|
218
|
+
store.track!("Memory/Physical",mem_usage) if mem_usage
|
219
|
+
rescue => e
|
220
|
+
logger.info "Error reading ProcessMemory"
|
221
|
+
logger.debug e.message
|
222
|
+
logger.debug e.backtrace.join("\n")
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Called in the worker thread. Merges in-memory metrics w/those on disk and reports metrics
|
227
|
+
# to the server.
|
228
|
+
def process_metrics
|
229
|
+
logger.debug "Processing metrics"
|
230
|
+
run_samplers
|
231
|
+
metrics = layaway.deposit_and_deliver
|
232
|
+
if metrics.any?
|
233
|
+
add_metric_ids(metrics)
|
234
|
+
# for debugging, count the total number of requests
|
235
|
+
controller_count = 0
|
236
|
+
metrics.each do |meta,stats|
|
237
|
+
if meta.metric_name =~ /\AController/
|
238
|
+
controller_count += stats.call_count
|
239
|
+
end
|
240
|
+
end
|
241
|
+
logger.debug "#{config.settings['name']} Delivering metrics for #{controller_count} requests."
|
242
|
+
response = post( checkin_uri,
|
243
|
+
Marshal.dump(metrics),
|
244
|
+
"Content-Type" => "application/json" )
|
245
|
+
if response and response.is_a?(Net::HTTPSuccess)
|
246
|
+
directives = Marshal.load(response.body)
|
247
|
+
self.metric_lookup.merge!(directives[:metric_lookup])
|
248
|
+
logger.debug "Metric Cache Size: #{metric_lookup.size}"
|
249
|
+
end
|
250
|
+
end
|
251
|
+
rescue
|
252
|
+
logger.debug "Error on checkin to #{checkin_uri.to_s}"
|
253
|
+
logger.debug $!.message
|
254
|
+
logger.debug $!.backtrace
|
255
|
+
end
|
256
|
+
|
257
|
+
def checkin_uri
|
258
|
+
URI.parse("http://#{config.settings['host'] || DEFAULT_HOST}/app/#{config.settings['key']}/checkin.scout?name=#{CGI.escape(config.settings['name'])}")
|
259
|
+
end
|
260
|
+
|
261
|
+
def post(url, body, headers = Hash.new)
|
262
|
+
response = nil
|
263
|
+
request(url) do |connection|
|
264
|
+
post = Net::HTTP::Post.new( url.path +
|
265
|
+
(url.query ? ('?' + url.query) : ''),
|
266
|
+
HTTP_HEADERS.merge(headers) )
|
267
|
+
post.body = body
|
268
|
+
response=connection.request(post)
|
269
|
+
end
|
270
|
+
response
|
271
|
+
end
|
272
|
+
|
273
|
+
def request(url, &connector)
|
274
|
+
response = nil
|
275
|
+
http = Net::HTTP.new(url.host, url.port)
|
276
|
+
response = http.start(&connector)
|
277
|
+
logger.debug "got response: #{response.inspect}"
|
278
|
+
case response
|
279
|
+
when Net::HTTPSuccess, Net::HTTPNotModified
|
280
|
+
logger.debug "/checkin OK"
|
281
|
+
when Net::HTTPBadRequest
|
282
|
+
logger.warn "/checkin FAILED: The Account Key [#{config.settings['key']}] is invalid."
|
283
|
+
else
|
284
|
+
logger.debug "/checkin FAILED: #{response.inspect}"
|
285
|
+
end
|
286
|
+
rescue Exception
|
287
|
+
logger.debug "Exception sending request to server: #{$!.message}"
|
288
|
+
ensure
|
289
|
+
response
|
290
|
+
end
|
291
|
+
|
292
|
+
# Loads the instrumention logic.
|
293
|
+
def load_instruments
|
294
|
+
case environment.framework
|
295
|
+
when :rails
|
296
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'instruments/rails/action_controller_instruments.rb'))
|
297
|
+
when :rails3
|
298
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'instruments/rails3/action_controller_instruments.rb'))
|
299
|
+
when :sinatra
|
300
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'instruments/sinatra_instruments.rb'))
|
301
|
+
end
|
302
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'instruments/active_record_instruments.rb'))
|
303
|
+
rescue
|
304
|
+
logger.warn "Exception loading instruments:"
|
305
|
+
logger.warn $!.message
|
306
|
+
logger.warn $!.backtrace
|
307
|
+
end
|
308
|
+
|
309
|
+
# Injects instruments into the Ruby application.
|
310
|
+
def start_instruments
|
311
|
+
logger.debug "Installing instrumentation"
|
312
|
+
load_instruments
|
313
|
+
end
|
314
|
+
|
315
|
+
end # class Agent
|
316
|
+
end # module ScoutRails
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ScoutRails
|
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(ScoutRails::Agent.instance.environment.root,"config","scout_rails.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
|
+
ScoutRails::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))[ScoutRails::Agent.instance.environment.env]
|
26
|
+
end
|
27
|
+
rescue Exception => e
|
28
|
+
ScoutRails::Agent.instance.logger.warn "Unable to load the config file."
|
29
|
+
ScoutRails::Agent.instance.logger.warn e.message
|
30
|
+
ScoutRails::Agent.instance.logger.warn e.backtrace
|
31
|
+
@settings = {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# Used to retrieve environment information for this application.
|
2
|
+
module ScoutRails
|
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 root
|
27
|
+
if framework == :rails
|
28
|
+
RAILS_ROOT.to_s
|
29
|
+
elsif framework == :rails3
|
30
|
+
Rails.root
|
31
|
+
elsif framework == :sinatra
|
32
|
+
Sinatra::Application.root
|
33
|
+
else
|
34
|
+
'.'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def app_server
|
39
|
+
@app_server ||= if thin? then :thin
|
40
|
+
elsif passenger? then :passenger
|
41
|
+
elsif webrick? then :webrick
|
42
|
+
else nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
### app server related-checks
|
47
|
+
|
48
|
+
def thin?
|
49
|
+
defined?(::Thin) && defined?(::Thin::Server)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Called via +#forking?+ since Passenger forks. Adds an event listener to start the worker thread
|
53
|
+
# inside the passenger worker process.
|
54
|
+
# Background: http://www.modrails.com/documentation/Users%20guide%20Nginx.html#spawning%5Fmethods%5Fexplained
|
55
|
+
def passenger?
|
56
|
+
(defined?(::Passenger) && defined?(::Passenger::AbstractServer)) || defined?(::IN_PHUSION_PASSENGER)
|
57
|
+
end
|
58
|
+
|
59
|
+
def webrick?
|
60
|
+
defined?(::WEBrick) && defined?(::WEBrick::VERSION)
|
61
|
+
end
|
62
|
+
|
63
|
+
# If forking, don't start worker thread in the master process. Since it's started as a Thread, it won't survive
|
64
|
+
# the fork.
|
65
|
+
def forking?
|
66
|
+
passenger?
|
67
|
+
end
|
68
|
+
|
69
|
+
### ruby checks
|
70
|
+
|
71
|
+
def rubinius?
|
72
|
+
RUBY_VERSION =~ /rubinius/i
|
73
|
+
end
|
74
|
+
|
75
|
+
def jruby?
|
76
|
+
defined?(JRuby)
|
77
|
+
end
|
78
|
+
|
79
|
+
### framework checks
|
80
|
+
|
81
|
+
def sinatra?
|
82
|
+
defined?(Sinatra::Application)
|
83
|
+
end
|
84
|
+
|
85
|
+
end # class Environemnt
|
86
|
+
end
|