forked from newrelic/newrelic-ruby-agent
/
local_environment.rb
391 lines (338 loc) · 13.1 KB
/
local_environment.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
require 'set'
require 'new_relic/version'
module NewRelic
# An instance of LocalEnvironment is responsible for determining
# three things:
#
# * Framework - :rails, :rails3, :merb, :ruby, :external, :test
# * Dispatcher - A supported dispatcher, or nil (:mongrel, :thin, :passenger, :webrick, etc)
# * Dispatcher Instance ID, which distinguishes agents on a single host from each other
#
# If the environment can't be determined, it will be set to
# nil and dispatcher_instance_id will have nil.
#
# NewRelic::LocalEnvironment should be accessed through NewRelic::Control#env (via the NewRelic::Control singleton).
class LocalEnvironment
attr_accessor :dispatcher # mongrel, thin, webrick, or possibly nil
attr_accessor :dispatcher_instance_id # used to distinguish instances of a dispatcher from each other, may be nil
attr_accessor :framework # rails, rails3, merb, external, ruby, test
attr_reader :mongrel # The mongrel instance, if there is one, captured as a convenience
attr_reader :processors # The number of cpus, if detected, or nil
alias environment dispatcher
def initialize
# Extend self with any any submodules of LocalEnvironment. These can override
# the discover methods to discover new framworks and dispatchers.
NewRelic::LocalEnvironment.constants.each do | const |
mod = NewRelic::LocalEnvironment.const_get const
self.extend mod if mod.instance_of? Module
end
discover_framework
discover_dispatcher
@dispatcher = nil if @dispatcher == :none
@gems = Set.new
@plugins = Set.new
@config = Hash.new
end
# Add the given key/value pair to the app environment
# settings. Must pass either a value or a block. Block
# is called to get the value and any raised errors are
# silently ignored.
def append_environment_value(name, value = nil)
value = yield if block_given?
@config[name] = value if value
rescue Exception
# puts "#{e}\n #{e.backtrace.join("\n ")}"
raise if @framework == :test
end
def append_gem_list
@gems += yield
rescue Exception => e
# puts "#{e}\n #{e.backtrace.join("\n ")}"
raise if @framework == :test
end
def append_plugin_list
@plugins += yield
rescue Exception
# puts "#{e}\n #{e.backtrace.join("\n ")}"
raise if @framework == :test
end
def dispatcher_instance_id
if @dispatcher_instance_id.nil?
if @dispatcher.nil?
@dispatcher_instance_id = File.basename($0).split(".").first
end
end
@dispatcher_instance_id
end
def gather_ruby_info
append_environment_value('Ruby version'){ RUBY_VERSION }
append_environment_value('Ruby description'){ RUBY_DESCRIPTION } if defined? ::RUBY_DESCRIPTION
append_environment_value('Ruby platform') { RUBY_PLATFORM }
append_environment_value('Ruby patchlevel') { RUBY_PATCHLEVEL }
# room here for other ruby implementations, when.
if defined? ::JRUBY_VERSION
gather_jruby_info
end
end
def gather_jruby_info
append_environment_value('JRuby version') { JRUBY_VERSION }
append_environment_value('Java VM version') { ENV_JAVA['java.vm.version']}
end
# See what the number of cpus is, works only on linux.
def gather_cpu_info
return unless File.readable? '/proc/cpuinfo'
@processors = append_environment_value('Processors') do
processors = File.readlines('/proc/cpuinfo').select { |line| line =~ /^processor\s*:/ }.size
raise "Cannot determine the number of processors in /proc/cpuinfo" unless processors > 0
processors
end
end
def gather_architecture_info
append_environment_value('Arch') { `uname -p` } ||
append_environment_value('Arch') { ENV['PROCESSOR_ARCHITECTURE'] }
end
def gather_os_info
append_environment_value('OS version') { `uname -v` }
append_environment_value('OS') { `uname -s` } ||
append_environment_value('OS') { ENV['OS'] }
end
def gather_system_info
gather_architecture_info
gather_cpu_info
end
def gather_revision_info
# Look for a capistrano file indicating the current revision:
rev_file = File.join(NewRelic::Control.instance.root, "REVISION")
if File.readable?(rev_file) && File.size(rev_file) < 64
append_environment_value('Revision') do
File.open(rev_file) { | file | file.readline.strip }
end
end
end
def gather_ar_adapter_info
# The name of the database adapter for the current environment.
append_environment_value 'Database adapter' do
if defined?(ActiveRecord) && defined?(ActiveRecord::Base) &&
ActiveRecord::Base.respond_to?(:configurations)
config = ActiveRecord::Base.configurations[NewRelic::Control.instance.env]
if config
config['adapter']
end
end
end
append_environment_value 'Database schema version' do
ActiveRecord::Migrator.current_version
end
end
def gather_dm_adapter_info
append_environment_value 'DataMapper version' do
require 'dm-core/version'
DataMapper::VERSION
end
end
def gather_db_info
# room here for more database adapters, when.
if defined? ::ActiveRecord
gather_ar_adapter_info
end
if defined? ::DataMapper
gather_dm_adapter_info
end
end
# Collect base statistics about the environment and record them for
# comparison and change detection.
def gather_environment_info
append_environment_value 'Framework', @framework.to_s
append_environment_value 'Dispatcher', @dispatcher.to_s if @dispatcher
append_environment_value 'Dispatcher instance id', @dispatcher_instance_id if @dispatcher_instance_id
append_environment_value('Environment') { NewRelic::Control.instance.env }
# miscellaneous other helpful debugging information
gather_ruby_info
gather_system_info
gather_revision_info
gather_db_info
end
# Take a snapshot of the environment information for this application
# Returns an associative array
def snapshot
i = @config.to_a
i << [ 'Plugin List', @plugins.to_a] if not @plugins.empty?
i << [ 'Gems', @gems.to_a] if not @gems.empty?
i
end
def working_jruby?
!(defined?(::JRuby) && JRuby.respond_to?(:runtime) && !JRuby.runtime.is_object_space_enabled)
end
def find_class_in_object_space(klass)
ObjectSpace.each_object(klass) do |x|
return x
end
end
def mongrel
return @mongrel if @mongrel
if defined?(::Mongrel) && defined?(::Mongrel::HttpServer) && working_jruby?
@mongrel = find_class_in_object_space(::Mongrel::HttpServer)
end
@mongrel
end
def unicorn
return @unicorn if @unicorn
if (defined?(::Unicorn) && defined?(::Unicorn::HttpServer)) && working_jruby?
@unicorn = find_class_in_object_space(::Unicorn::HttpServer)
end
@unicorn
end
private
# Although you can override the framework with NEWRELIC_DISPATCHER this
# is not advisable since it implies certain api's being available.
def discover_dispatcher
@dispatcher ||= ENV['NEWRELIC_DISPATCHER'] && ENV['NEWRELIC_DISPATCHER'].to_sym
dispatchers = %w[passenger torquebox glassfish thin mongrel litespeed webrick fastcgi unicorn sinatra]
while dispatchers.any? && @dispatcher.nil?
send 'check_for_'+(dispatchers.shift)
end
end
def discover_framework
# Although you can override the framework with NEWRELIC_FRAMEWORK this
# is not advisable since it implies certain api's being available.
#
# Note that the odd defined? sequence is necessary to work around a bug in an older version
# of JRuby.
@framework ||= case
when ENV['NEWRELIC_FRAMEWORK'] then ENV['NEWRELIC_FRAMEWORK'].to_sym
when defined?(::NewRelic::TEST) then :test
when defined?(::Merb) && defined?(::Merb::Plugins) then :merb
when defined?(::Rails) then check_rails_version
when defined?(::Sinatra) && defined?(::Sinatra::Base) then :sinatra
when defined?(::NewRelic::IA) then :external
else :ruby
end
end
def check_rails_version
if Rails::VERSION::MAJOR < 3
:rails
else
:rails3
end
end
def check_for_torquebox
return unless defined?(::JRuby) &&
( Java::OrgTorqueboxRailsWebDeployers::RailsRackDeployer rescue nil)
@dispatcher = :torquebox
end
def check_for_glassfish
return unless defined?(::JRuby) &&
(((com.sun.grizzly.jruby.rack.DefaultRackApplicationFactory rescue nil) &&
defined?(com::sun::grizzly::jruby::rack::DefaultRackApplicationFactory)) ||
(jruby_rack? && defined?(::GlassFish::Server)))
@dispatcher = :glassfish
end
def check_for_trinidad
return unless defined?(::JRuby) && jruby_rack? && defined?(::Trinidad::Server)
@dispatcher = :trinidad
end
def jruby_rack?
((org.jruby.rack.DefaultRackApplicationFactory rescue nil) &&
defined?(org::jruby::rack::DefaultRackApplicationFactory))
end
def check_for_webrick
return unless defined?(::WEBrick) && defined?(::WEBrick::VERSION)
@dispatcher = :webrick
if defined?(::OPTIONS) && OPTIONS.respond_to?(:fetch)
# OPTIONS is set by script/server
@dispatcher_instance_id = OPTIONS.fetch(:port)
end
@dispatcher_instance_id = default_port unless @dispatcher_instance_id
end
def check_for_fastcgi
return unless defined?(::FCGI)
@dispatcher = :fastcgi
end
# this case covers starting by mongrel_rails
def check_for_mongrel
return unless defined?(::Mongrel) && defined?(::Mongrel::HttpServer)
@dispatcher = :mongrel
# Get the port from the server if it's started
if mongrel && mongrel.respond_to?(:port)
@dispatcher_instance_id = mongrel.port.to_s
end
# Get the port from the configurator if one was created
if @dispatcher_instance_id.nil? && defined?(::Mongrel::Configurator)
ObjectSpace.each_object(Mongrel::Configurator) do |mongrel|
@dispatcher_instance_id = mongrel.defaults[:port] && mongrel.defaults[:port].to_s
end unless defined?(::JRuby) && !JRuby.runtime.is_object_space_enabled
end
# Still can't find the port. Let's look at ARGV to fall back
@dispatcher_instance_id = default_port if @dispatcher_instance_id.nil?
end
def check_for_unicorn
return unless defined?(::Unicorn) && defined?(::Unicorn::HttpServer)
# unlike mongrel, unicorn manages muliple threads and ports, so we
# have to map multiple processes into one instance, as we do with passenger
@dispatcher = :unicorn
end
def check_for_sinatra
return unless defined?(::Sinatra) && defined?(::Sinatra::Base)
begin
version = ::Sinatra::VERSION
rescue
$stderr.puts("Error determining Sinatra version")
end
if ::NewRelic::VersionNumber.new('0.9.2') > version
$stderr.puts("Your Sinatra version is #{version}, we highly recommend upgrading to >=0.9.2")
end
@dispatcher = :sinatra
end
def check_for_thin
if defined?(::Thin) && defined?(::Thin::Server)
# This case covers the thin web dispatcher
# Same issue as above- we assume only one instance per process
ObjectSpace.each_object(Thin::Server) do |thin_dispatcher|
@dispatcher = :thin
backend = thin_dispatcher.backend
# We need a way to uniquely identify and distinguish agents. The port
# works for this. When using sockets, use the socket file name.
if backend.respond_to? :port
@dispatcher_instance_id = backend.port
elsif backend.respond_to? :socket
@dispatcher_instance_id = backend.socket
else
raise "Unknown thin backend: #{backend}"
end
end # each thin instance
end
if defined?(::Thin) && defined?(::Thin::VERSION) && !@dispatcher
@dispatcher = :thin
@dispatcher_instance_id = default_port
end
end
def check_for_litespeed
if caller.pop =~ /fcgi-bin\/RailsRunner\.rb/
@dispatcher = :litespeed
end
end
def check_for_passenger
if (defined?(::Passenger) && defined?(::Passenger::AbstractServer)) || defined?(::IN_PHUSION_PASSENGER)
@dispatcher = :passenger
end
end
def default_port
require 'optparse'
# If nothing else is found, use the 3000 default
default_port = 3000
OptionParser.new do |opts|
opts.on("-p", "--port=port", String) { | p | default_port = p }
opts.parse(ARGV.clone) rescue nil
end
default_port
end
public
def to_s
s = "LocalEnvironment["
s << @framework.to_s
s << ";dispatcher=#{@dispatcher}" if @dispatcher
s << ";instance=#{@dispatcher_instance_id}" if @dispatcher_instance_id
s << "]"
end
end
end