newrelic_rpm 2.8.11 → 2.9.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of newrelic_rpm might be problematic. Click here for more details.

Files changed (137) hide show
  1. data/CHANGELOG +267 -0
  2. data/LICENSE +1 -1
  3. data/Manifest +142 -0
  4. data/README.md +138 -0
  5. data/Rakefile +10 -28
  6. data/bin/mongrel_rpm +33 -0
  7. data/cert/cacert.pem +34 -0
  8. data/init.rb +38 -0
  9. data/lib/new_relic/agent/agent.rb +160 -347
  10. data/lib/new_relic/agent/collection_helper.rb +13 -24
  11. data/lib/new_relic/agent/error_collector.rb +29 -15
  12. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +63 -76
  13. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +90 -48
  14. data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +72 -47
  15. data/lib/new_relic/agent/instrumentation/error_instrumentation.rb +14 -0
  16. data/lib/new_relic/agent/instrumentation/merb/controller.rb +10 -1
  17. data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +5 -7
  18. data/lib/new_relic/agent/instrumentation/merb/errors.rb +3 -1
  19. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +7 -0
  20. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +34 -7
  21. data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +20 -12
  22. data/lib/new_relic/agent/instrumentation/rails/errors.rb +5 -4
  23. data/lib/new_relic/agent/method_tracer.rb +159 -135
  24. data/lib/new_relic/agent/patch_const_missing.rb +46 -26
  25. data/lib/new_relic/agent/sampler.rb +12 -0
  26. data/lib/new_relic/agent/samplers/cpu_sampler.rb +44 -0
  27. data/lib/new_relic/agent/samplers/memory_sampler.rb +126 -0
  28. data/lib/new_relic/agent/samplers/mongrel_sampler.rb +22 -0
  29. data/lib/new_relic/agent/shim_agent.rb +11 -0
  30. data/lib/new_relic/agent/stats_engine.rb +85 -46
  31. data/lib/new_relic/agent/transaction_sampler.rb +63 -38
  32. data/lib/new_relic/agent/worker_loop.rb +8 -18
  33. data/lib/new_relic/agent.rb +200 -25
  34. data/lib/new_relic/commands/deployments.rb +9 -9
  35. data/lib/new_relic/control/merb.rb +22 -0
  36. data/lib/new_relic/control/rails.rb +141 -0
  37. data/lib/new_relic/{config → control}/ruby.rb +13 -2
  38. data/lib/new_relic/control.rb +424 -0
  39. data/lib/new_relic/local_environment.rb +201 -79
  40. data/lib/new_relic/metric_data.rb +7 -0
  41. data/lib/new_relic/metric_parser/action_mailer.rb +9 -0
  42. data/lib/new_relic/metric_parser/active_merchant.rb +26 -0
  43. data/lib/new_relic/metric_parser/active_record.rb +11 -0
  44. data/lib/new_relic/metric_parser/controller.rb +51 -0
  45. data/lib/new_relic/metric_parser/controller_cpu.rb +38 -0
  46. data/lib/new_relic/metric_parser/database.rb +23 -0
  47. data/lib/new_relic/metric_parser/errors.rb +6 -0
  48. data/lib/new_relic/metric_parser/mem_cache.rb +12 -0
  49. data/lib/new_relic/metric_parser/view.rb +61 -0
  50. data/lib/new_relic/metric_parser/web_service.rb +9 -0
  51. data/lib/new_relic/metric_parser.rb +107 -0
  52. data/lib/new_relic/metric_spec.rb +5 -0
  53. data/lib/new_relic/noticed_error.rb +5 -1
  54. data/lib/new_relic/rack/metric_app.rb +57 -0
  55. data/lib/new_relic/rack/newrelic.ru +25 -0
  56. data/lib/new_relic/rack/newrelic.yml +25 -0
  57. data/lib/new_relic/rack.rb +5 -0
  58. data/lib/new_relic/recipes.rb +10 -3
  59. data/lib/new_relic/stats.rb +130 -144
  60. data/lib/new_relic/transaction_analysis.rb +7 -8
  61. data/lib/new_relic/transaction_sample.rb +86 -10
  62. data/lib/new_relic/version.rb +41 -160
  63. data/lib/new_relic_api.rb +7 -6
  64. data/lib/newrelic_rpm.rb +30 -17
  65. data/lib/tasks/{agent_tests.rake → tests.rake} +1 -1
  66. data/newrelic.yml +115 -62
  67. data/newrelic_rpm.gemspec +36 -0
  68. data/test/active_record_fixtures.rb +55 -0
  69. data/test/config/newrelic.yml +21 -3
  70. data/test/config/{test_config.rb → test_control.rb} +14 -10
  71. data/test/new_relic/agent/active_record_instrumentation_test.rb +189 -0
  72. data/test/new_relic/agent/agent_test.rb +104 -0
  73. data/test/new_relic/agent/agent_test_controller.rb +18 -1
  74. data/test/new_relic/agent/classloader_patch_test.rb +56 -0
  75. data/test/new_relic/agent/{tc_collection_helper.rb → collection_helper_test.rb} +28 -23
  76. data/test/new_relic/agent/controller_test.rb +107 -0
  77. data/test/new_relic/agent/dispatcher_instrumentation_test.rb +70 -0
  78. data/test/new_relic/agent/error_collector_test.rb +155 -0
  79. data/test/new_relic/agent/{tc_method_tracer.rb → method_tracer_test.rb} +6 -12
  80. data/test/new_relic/agent/metric_data_test.rb +56 -0
  81. data/test/new_relic/agent/stats_engine_test.rb +266 -0
  82. data/test/new_relic/agent/{tc_transaction_sample_builder.rb → transaction_sample_builder_test.rb} +6 -5
  83. data/test/new_relic/agent/{tc_transaction_sample.rb → transaction_sample_test.rb} +9 -13
  84. data/test/new_relic/agent/transaction_sampler_test.rb +317 -0
  85. data/test/new_relic/agent/{tc_worker_loop.rb → worker_loop_test.rb} +1 -1
  86. data/test/new_relic/control_test.rb +97 -0
  87. data/test/new_relic/{tc_deployments_api.rb → deployments_api_test.rb} +8 -4
  88. data/test/new_relic/environment_test.rb +75 -0
  89. data/test/new_relic/metric_parser_test.rb +142 -0
  90. data/test/new_relic/{tc_metric_spec.rb → metric_spec_test.rb} +28 -1
  91. data/test/new_relic/samplers_test.rb +71 -0
  92. data/test/new_relic/{tc_shim_agent.rb → shim_agent_test.rb} +1 -1
  93. data/test/new_relic/stats_test.rb +291 -0
  94. data/test/new_relic/version_number_test.rb +46 -0
  95. data/test/test_helper.rb +7 -30
  96. data/test/ui/newrelic_controller_test.rb +14 -0
  97. data/test/ui/{tc_newrelic_helper.rb → newrelic_helper_test.rb} +16 -7
  98. data/ui/controllers/newrelic_controller.rb +17 -3
  99. data/ui/helpers/newrelic_helper.rb +44 -15
  100. data/ui/views/layouts/newrelic_default.rhtml +7 -8
  101. data/ui/views/newrelic/_sample.rhtml +5 -2
  102. data/ui/views/newrelic/_segment.rhtml +1 -1
  103. data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
  104. data/ui/views/newrelic/_segment_row.rhtml +4 -4
  105. data/ui/views/newrelic/_show_sample_detail.rhtml +3 -1
  106. data/ui/views/newrelic/_show_sample_sql.rhtml +2 -1
  107. data/ui/views/newrelic/explain_sql.rhtml +2 -5
  108. data/ui/views/newrelic/images/file_icon.png +0 -0
  109. data/ui/views/newrelic/images/new_relic_rpm_desktop.gif +0 -0
  110. data/ui/views/newrelic/index.rhtml +21 -13
  111. data/ui/views/newrelic/javascript/prototype-scriptaculous.js +7288 -0
  112. data/ui/views/newrelic/show_sample.rhtml +18 -3
  113. data/ui/views/newrelic/stylesheets/style.css +39 -0
  114. data/ui/views/newrelic/threads.rhtml +52 -0
  115. metadata +192 -70
  116. data/README +0 -136
  117. data/lib/new_relic/agent/instrumentation/rails/rails.rb +0 -6
  118. data/lib/new_relic/agent/samplers/cpu.rb +0 -29
  119. data/lib/new_relic/agent/samplers/memory.rb +0 -53
  120. data/lib/new_relic/agent/samplers/mongrel.rb +0 -26
  121. data/lib/new_relic/agent/synchronize.rb +0 -40
  122. data/lib/new_relic/config/merb.rb +0 -35
  123. data/lib/new_relic/config/rails.rb +0 -114
  124. data/lib/new_relic/config.rb +0 -279
  125. data/lib/new_relic/shim_agent.rb +0 -96
  126. data/test/new_relic/agent/model_fixture.rb +0 -15
  127. data/test/new_relic/agent/tc_active_record.rb +0 -90
  128. data/test/new_relic/agent/tc_agent.rb +0 -148
  129. data/test/new_relic/agent/tc_controller.rb +0 -77
  130. data/test/new_relic/agent/tc_dispatcher_instrumentation.rb +0 -52
  131. data/test/new_relic/agent/tc_error_collector.rb +0 -127
  132. data/test/new_relic/agent/tc_stats_engine.rb +0 -218
  133. data/test/new_relic/agent/tc_synchronize.rb +0 -37
  134. data/test/new_relic/agent/tc_transaction_sampler.rb +0 -302
  135. data/test/new_relic/tc_config.rb +0 -36
  136. data/test/new_relic/tc_environment.rb +0 -94
  137. data/test/new_relic/tc_stats.rb +0 -141
@@ -6,10 +6,10 @@ require 'yaml'
6
6
  require 'net/http'
7
7
  require 'rexml/document'
8
8
 
9
- # We need to use the Config object but we don't want to load
9
+ # We need to use the Control object but we don't want to load
10
10
  # the rails/merb environment. The defined? clause is so that
11
11
  # it won't load it twice, something it does when run inside a test
12
- require 'new_relic/config' unless defined? NewRelic::Config
12
+ require 'new_relic/control' unless defined? NewRelic::Control
13
13
 
14
14
  module NewRelic
15
15
  module Commands
@@ -39,7 +39,7 @@ module NewRelic
39
39
  # Will throw CommandFailed exception if there's any error.
40
40
  #
41
41
  def initialize command_line_args
42
- @config = NewRelic::Config.instance
42
+ @config = NewRelic::Control.instance
43
43
  @user = ENV['USER']
44
44
  if Hash === command_line_args
45
45
  # command line args is an options hash
@@ -55,7 +55,7 @@ module NewRelic
55
55
  @description = options.parse(command_line_args).join " "
56
56
  end
57
57
  config.env = @environment if @environment
58
- @appname ||= config.app_name || config.env || 'development'
58
+ @appname ||= config.app_names[0] || config.env || 'development'
59
59
  end
60
60
 
61
61
  # Run the Deployment upload in RPM via Active Resource.
@@ -119,18 +119,18 @@ module NewRelic
119
119
  o.separator "OPTIONS:"
120
120
  o.on("-a", "--appname=DIR", String,
121
121
  "Set the application name.",
122
- "Default is app_name setting in newrelic.yml") { |@appname| }
122
+ "Default is app_name setting in newrelic.yml") { | e | @appname = e }
123
123
  o.on("-e", "--environment=name", String,
124
124
  "Override the (RAILS|MERB|RUBY)_ENV setting",
125
- "currently: #{config.env}") { | @environment| }
125
+ "currently: #{config.env}") { | e | @environment = e }
126
126
  o.on("-u", "--user=USER", String,
127
127
  "Specify the user deploying.",
128
- "Default: #{@user}") { |@user| }
128
+ "Default: #{@user}") { | u | @user = u }
129
129
  o.on("-r", "--revision=REV", String,
130
- "Specify the revision being deployed") { |@revision | }
130
+ "Specify the revision being deployed") { | r | @revision = r }
131
131
  o.on("-c", "--changes",
132
132
  "Read in a change log from the standard input") { @changelog = STDIN.read }
133
- o.on("-?", "Print this help") { raise CommandFailure.new(o.help, 0) }
133
+ o.on("-h", "--help", "Print this help") { raise CommandFailure.new(o.help, 0) }
134
134
  o.separator ""
135
135
  o.separator 'description = "short text"'
136
136
  end
@@ -0,0 +1,22 @@
1
+ class NewRelic::Control::Merb < NewRelic::Control
2
+
3
+ def env
4
+ @env ||= ::Merb.env
5
+ end
6
+ def root
7
+ ::Merb.root
8
+ end
9
+
10
+ def to_stdout(msg)
11
+ STDOUT.puts "NewRelic ~ " + msg
12
+ end
13
+
14
+ def init_config options={}
15
+ ::Merb::Plugins.add_rakefiles File.join(newrelic_root,"lib/tasks/all.rb")
16
+
17
+ # Merb gives you a Merb::Plugins.config hash...feel free to put your stuff in your piece of it
18
+ ::Merb::Plugins.config[:newrelic] = {
19
+ :config => self
20
+ }
21
+ end
22
+ end
@@ -0,0 +1,141 @@
1
+ # Control subclass instantiated when Rails is detected. Contains
2
+ # Rails specific configuration, instrumentation, environment values,
3
+ # etc.
4
+ class NewRelic::Control::Rails < NewRelic::Control
5
+
6
+ def env
7
+ @env ||= RAILS_ENV.dup
8
+ end
9
+ def root
10
+ RAILS_ROOT
11
+ end
12
+
13
+ def log_path
14
+ path = ::RAILS_DEFAULT_LOGGER.instance_eval do
15
+ File.dirname(@log.path) rescue File.dirname(@logdev.filename)
16
+ end rescue "#{root}/log"
17
+ File.expand_path(path)
18
+ end
19
+ # In versions of Rails prior to 2.0, the rails config was only available to
20
+ # the init.rb, so it had to be passed on from there.
21
+ def init_config(options={})
22
+ rails_config=options[:config]
23
+ if !agent_enabled?
24
+ RAILS_DEFAULT_LOGGER.info "New Relic Agent not running"
25
+ else
26
+ RAILS_DEFAULT_LOGGER.info "Starting the New Relic Agent"
27
+ install_developer_mode rails_config if developer_mode?
28
+ end
29
+ end
30
+
31
+ def install_developer_mode(rails_config)
32
+ return if @installed
33
+ @installed = true
34
+ controller_path = File.expand_path(File.join(newrelic_root, 'ui', 'controllers'))
35
+ helper_path = File.expand_path(File.join(newrelic_root, 'ui', 'helpers'))
36
+
37
+ if defined? ActiveSupport::Dependencies
38
+ Dir["#{helper_path}/*.rb"].each { |f| require f }
39
+ Dir["#{controller_path}/*.rb"].each { |f| require f }
40
+ elsif defined? Dependencies.load_paths
41
+ Dependencies.load_paths << controller_path
42
+ Dependencies.load_paths << helper_path
43
+ else
44
+ to_stdout "ERROR: Rails version #{Rails::VERSION::STRING} too old for developer mode to work."
45
+ return
46
+ end
47
+ install_devmode_route
48
+
49
+ # If we have the config object then add the controller path to the list.
50
+ # Otherwise we have to assume the controller paths have already been
51
+ # set and we can just append newrelic.
52
+
53
+ if rails_config
54
+ rails_config.controller_paths << controller_path
55
+ else
56
+ current_paths = ActionController::Routing.controller_paths
57
+ if current_paths.nil? || current_paths.empty?
58
+ to_stdout "WARNING: Unable to modify the routes in this version of Rails. Developer mode not available."
59
+ end
60
+ current_paths << controller_path
61
+ end
62
+
63
+ #ActionController::Routing::Routes.reload! unless NewRelic::Control.instance['skip_developer_route']
64
+
65
+ # inform user that the dev edition is available if we are running inside
66
+ # a webserver process
67
+ if @local_env.dispatcher_instance_id
68
+ port = @local_env.dispatcher_instance_id.to_s =~ /^\d+/ ? ":#{local_env.dispatcher_instance_id}" : ":port"
69
+ to_stdout "NewRelic Agent Developer Mode enabled."
70
+ to_stdout "To view performance information, go to http://localhost#{port}/newrelic"
71
+ end
72
+ end
73
+
74
+ def rails_version
75
+ @rails_version ||= NewRelic::VersionNumber.new(::Rails::VERSION::STRING)
76
+ end
77
+
78
+ protected
79
+
80
+ def install_devmode_route
81
+ # This is a monkey patch to inject the developer tool route into the
82
+ # parent app without requiring users to modify their routes. Of course this
83
+ # has the effect of adding a route indiscriminately which is frowned upon by
84
+ # some: http://www.ruby-forum.com/topic/126316#563328
85
+ ActionController::Routing::RouteSet.class_eval do
86
+ next if self.instance_methods.include? 'draw_with_newrelic_map'
87
+ def draw_with_newrelic_map
88
+ draw_without_newrelic_map do | map |
89
+ map.named_route 'newrelic_developer', '/newrelic/:action/:id', :controller => 'newrelic' unless NewRelic::Control.instance['skip_developer_route']
90
+ yield map
91
+ end
92
+ end
93
+ alias_method_chain :draw, :newrelic_map
94
+ end
95
+ end
96
+
97
+ def rails_vendor_root
98
+ File.join(root,'vendor','rails')
99
+ end
100
+
101
+ # Collect the Rails::Info into an associative array as well as the list of plugins
102
+ def append_environment_info
103
+ local_env.append_environment_value('Rails version'){ ::Rails::VERSION::STRING }
104
+ if rails_version >= NewRelic::VersionNumber.new('2.2.0')
105
+ local_env.append_environment_value('Rails threadsafe') do
106
+ ::Rails.configuration.action_controller.allow_concurrency == true
107
+ end
108
+ end
109
+ if rails_version >= NewRelic::VersionNumber.new('2.1.0')
110
+ local_env.append_gem_list do
111
+ ::Rails.configuration.gems.map do | gem |
112
+ version = (gem.respond_to?(:version) && gem.version) ||
113
+ (gem.specification.respond_to?(:version) && gem.specification.version)
114
+ gem.name + (version ? "(#{version})" : "")
115
+ end
116
+ end
117
+ # The plugins is configured manually. If it's nil, it loads everything non-deterministically
118
+ if ::Rails.configuration.plugins
119
+ local_env.append_plugin_list { ::Rails.configuration.plugins }
120
+ else
121
+ ::Rails.configuration.plugin_paths.each do |path|
122
+ local_env.append_plugin_list { Dir[File.join(path, '*')].collect{ |p| File.basename p if File.directory? p }.compact }
123
+ end
124
+ end
125
+ else
126
+ # Rails prior to 2.1, can't get the gems. Find plugins in the default location
127
+ local_env.append_plugin_list do
128
+ Dir[File.join(root, 'vendor', 'plugins', '*')].collect{ |p| File.basename p if File.directory? p }.compact
129
+ end
130
+ end
131
+ end
132
+
133
+ def install_shim
134
+ super
135
+ require 'new_relic/agent/instrumentation/controller_instrumentation'
136
+ require 'new_relic/agent/instrumentation/error_instrumentation'
137
+ ActionController::Base.send :include, NewRelic::Agent::Instrumentation::ControllerInstrumentation::Shim
138
+ Object.send :include, NewRelic::Agent::Instrumentation::ErrorInstrumentation::Shim
139
+ end
140
+
141
+ end
@@ -1,5 +1,10 @@
1
- class NewRelic::Config::Ruby < NewRelic::Config
2
- def app; :ruby; end
1
+ # A control used when no framework is detected.
2
+ # Looks for a newrelic.yml file in several locations
3
+ # including ./, ./config, $HOME/.newrelic and $HOME/.
4
+ # It loads the settings from the newrelic.yml section
5
+ # based on the value of RUBY_ENV or RAILS_ENV.
6
+ class NewRelic::Control::Ruby < NewRelic::Control
7
+
3
8
  def env
4
9
  @env ||= ENV['RUBY_ENV'] || ENV['RAILS_ENV'] || 'development'
5
10
  end
@@ -18,5 +23,11 @@ class NewRelic::Config::Ruby < NewRelic::Config
18
23
  end
19
24
  return File.expand_path(files.first)
20
25
  end
26
+ def to_stdout(msg)
27
+ STDOUT.puts msg
28
+ end
29
+
30
+ def init_config(options={})
31
+ end
21
32
 
22
33
  end
@@ -0,0 +1,424 @@
1
+ require 'yaml'
2
+ require 'new_relic/local_environment'
3
+ require 'singleton'
4
+ require 'erb'
5
+ require 'socket'
6
+ require 'net/https'
7
+ require 'logger'
8
+
9
+
10
+ module NewRelic
11
+
12
+ # The Control is a singleton responsible for the startup and
13
+ # initialization sequence. The initializer uses a LocalEnvironment to
14
+ # detect the framework and instantiates the framework specific
15
+ # subclass.
16
+ #
17
+ # The Control also implements some of the public API for the agent.
18
+ #
19
+ class Control
20
+
21
+ attr_accessor :log_file
22
+ # The env is the setting used to identify which section of the newrelic.yml
23
+ # to load. This defaults to a framework specific value, such as ENV['RAILS_ENV']
24
+ # but can be overridden as long as you set it before calling #init_plugin
25
+ attr_writer :env
26
+ attr_reader :local_env
27
+
28
+ # Structs holding info for the remote server and proxy server
29
+ class Server < Struct.new :name, :port, :ip #:nodoc:
30
+ def to_s; "#{name}:#{port}"; end
31
+ end
32
+
33
+ ProxyServer = Struct.new :name, :port, :user, :password #:nodoc:
34
+
35
+ # Access the Control singleton, lazy initialized
36
+ def self.instance
37
+ @instance ||= new_instance
38
+ end
39
+
40
+ # Initialize the plugin/gem and start the agent. This does the necessary configuration based on the
41
+ # framework environment and determines whether or not to start the agent. If the
42
+ # agent is not going to be started then it loads the agent shim which has stubs
43
+ # for all the external api.
44
+ #
45
+ # This may be invoked multiple times, as long as you don't attempt to uninstall
46
+ # the agent after it has been started.
47
+ #
48
+ # If the plugin is initialized and it determines that the agent is not enabled, it
49
+ # will skip starting it and install the shim. But if you later call this with
50
+ # <tt>:agent_enabled => true</tt>, then it will install the real agent and start it.
51
+ #
52
+ # What determines whether the agent is launched is the result of calling agent_enabled?
53
+ # This will indicate whether the instrumentation should/will be installed. If we're
54
+ # in a mode where tracers are not installed then we should not start the agent.
55
+ #
56
+ # Subclasses are not allowed to override, but must implement init_config({}) which
57
+ # is called at most once.
58
+ #
59
+ def init_plugin(options={})
60
+ require 'new_relic/agent'
61
+ # Merge the stringified options into the config as overrides:
62
+ logger_override = options.delete(:log)
63
+ environment_name = options.delete(:env)
64
+ self.env = environment_name if environment_name
65
+ # Clear out the settings, if they've already been loaded. It may be that
66
+ # between calling init_plugin the first time and the second time, the env
67
+ # has been overridden
68
+ @settings = nil
69
+
70
+ options.each { |sym, val | self[sym.to_s] = val unless sym == :config }
71
+ if logger_override
72
+ @log = logger_override
73
+ # Try to grab the log filename
74
+ @log_file = @log.instance_eval { @logdev.filename rescue nil }
75
+ end
76
+ init_config(options)
77
+ if agent_enabled? && !@started
78
+ setup_log unless logger_override
79
+ start_agent
80
+ install_instrumentation
81
+ load_samplers unless self['disable_samplers']
82
+ local_env.gather_environment_info
83
+ append_environment_info
84
+ @started = true
85
+ elsif !agent_enabled?
86
+ install_shim
87
+ end
88
+ end
89
+
90
+ # Install the real agent into the Agent module, and issue the start command.
91
+ def start_agent
92
+ NewRelic::Agent.agent = NewRelic::Agent::Agent.instance
93
+ NewRelic::Agent.agent.start
94
+ end
95
+
96
+ def [](key)
97
+ fetch(key)
98
+ end
99
+
100
+ def settings
101
+ unless @settings
102
+ @settings = (@yaml && merge_defaults(@yaml[env])) || {}
103
+ # At the time we bind the settings, we also need to run this little piece
104
+ # of magic which allows someone to augment the id with the app name, necessary
105
+ @local_env.dispatcher_instance_id << ":#{app_names.first}" if self['multi_homed'] && app_names.size > 0
106
+ end
107
+ @settings
108
+ end
109
+
110
+ def []=(key, value)
111
+ settings[key] = value
112
+ end
113
+
114
+ def fetch(key, default=nil)
115
+ settings.fetch(key, default)
116
+ end
117
+ # Add your own environment value to track for change detection.
118
+ # The name and value should be stable and not vary across app processes on
119
+ # the same host.
120
+ def append_environment_info(name, value)
121
+ local_env.record_environment_info(name,value)
122
+ end
123
+
124
+ ###################################
125
+ # Agent config conveniences
126
+
127
+ def license_key
128
+ fetch('license_key')
129
+ end
130
+ def capture_params
131
+ fetch('capture_params')
132
+ end
133
+ # True if we are sending data to the server, monitoring production
134
+ def monitor_mode?
135
+ fetch('enabled', nil)
136
+ end
137
+ # True if we are capturing data and displaying in /newrelic
138
+ def developer_mode?
139
+ fetch('developer', nil)
140
+ end
141
+ # True if dev mode or monitor mode are enabled, and we are running
142
+ # inside a valid dispatcher like mongrel or passenger. Can be overridden
143
+ # by NEWRELIC_ENABLE env variable, monitor_daemons config option when true, or
144
+ # agent_enabled config option when true or false.
145
+ def agent_enabled?
146
+ return false if !developer_mode? && !monitor_mode?
147
+ return self['agent_enabled'].to_s =~ /true|on|yes/i if self['agent_enabled'] && self['agent_enabled'] != 'auto'
148
+ return false if ENV['NEWRELIC_ENABLE'].to_s =~ /false|off|no/i
149
+ return true if self['monitor_daemons'].to_s =~ /true|on|yes/i
150
+ return true if ENV['NEWRELIC_ENABLE'].to_s =~ /true|on|yes/i
151
+ # When in 'auto' mode the agent is enabled if there is a known
152
+ # dispatcher running
153
+ return true if @local_env.dispatcher != nil
154
+ end
155
+
156
+ def app
157
+ @local_env.framework
158
+ end
159
+ alias framework app
160
+
161
+ def dispatcher_instance_id
162
+ self['dispatcher_instance_id'] || @local_env.dispatcher_instance_id
163
+ end
164
+ def dispatcher
165
+ self['dispatcher'] || @local_env.dispatcher
166
+ end
167
+ def app_names
168
+ self['app_name'] ? self['app_name'].split(';') : []
169
+ end
170
+
171
+ def use_ssl?
172
+ @use_ssl ||= fetch('ssl', false)
173
+ end
174
+
175
+ def verify_certificate?
176
+ #this can only be on when SSL is enabled
177
+ @verify_certificate ||= ( use_ssl? ? fetch('verify_certificate', false) : false)
178
+ end
179
+
180
+ def server
181
+ @remote_server ||= server_from_host(nil)
182
+ end
183
+
184
+ def api_server
185
+ api_host = self['api_host'] || 'rpm.newrelic.com'
186
+ @api_server ||=
187
+ NewRelic::Control::Server.new \
188
+ api_host,
189
+ (self['api_port'] || self['port'] || (use_ssl? ? 443 : 80)).to_i,
190
+ nil
191
+ end
192
+
193
+ def proxy_server
194
+ @proxy_server ||=
195
+ NewRelic::Control::ProxyServer.new self['proxy_host'], self['proxy_port'], self['proxy_user'], self['proxy_pass']
196
+ end
197
+
198
+ def server_from_host(hostname=nil)
199
+ host = hostname || self['host'] || 'collector.newrelic.com'
200
+
201
+ # if the host is not an IP address, turn it into one
202
+ NewRelic::Control::Server.new host, (self['port'] || (use_ssl? ? 443 : 80)).to_i, convert_to_ip_address(host)
203
+ end
204
+
205
+ # Return the Net::HTTP with proxy configuration given the NewRelic::Control::Server object.
206
+ # Default is the collector but for api calls you need to pass api_server
207
+ #
208
+ # Experimental support for SSL verification:
209
+ # swap 'VERIFY_NONE' for 'VERIFY_PEER' line to try it out
210
+ # If verification fails, uncomment the 'http.ca_file' line
211
+ # and it will use the included certificate.
212
+ def http_connection(host = nil)
213
+ host ||= server
214
+ # Proxy returns regular HTTP if @proxy_host is nil (the default)
215
+ http_class = Net::HTTP::Proxy(proxy_server.name, proxy_server.port,
216
+ proxy_server.user, proxy_server.password)
217
+ http = http_class.new(host.ip || host.name, host.port)
218
+ if use_ssl?
219
+ http.use_ssl = true
220
+ if verify_certificate?
221
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
222
+ http.ca_file = File.join(File.dirname(__FILE__), '..', '..', 'cert', 'cacert.pem')
223
+ else
224
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
225
+ end
226
+ end
227
+ http
228
+ end
229
+ def to_s
230
+ "Control[#{self.app}]"
231
+ end
232
+
233
+ def log
234
+ # If we try to get a log before one has been set up, return a stdout log
235
+ unless @log
236
+ l = Logger.new(STDOUT)
237
+ l.level = Logger::INFO
238
+ return l
239
+ end
240
+ @log
241
+ end
242
+
243
+ # send the given message to STDOUT so that it shows
244
+ # up in the console. This should be used for important informational messages at boot.
245
+ # The to_stdout may be implemented differently by different config subclasses.
246
+ # This will NOT print anything if tracers are not enabled
247
+ def log!(msg, level=:info)
248
+ return if @settings && !agent_enabled?
249
+ to_stdout msg
250
+ log.send level, msg if @log
251
+ end
252
+
253
+ # Install stubs to the proper location so the app code will not fail
254
+ # if the agent is not running.
255
+ def install_shim
256
+ # Once we install instrumentation, you can't undo that by installing the shim.
257
+ raise "Cannot install the Agent shim after instrumentation has already been installed!" if @instrumented
258
+ NewRelic::Agent.agent = NewRelic::Agent::ShimAgent.instance
259
+ Module.send :include, NewRelic::Agent::MethodTracerShim
260
+ end
261
+
262
+ def install_instrumentation
263
+ return if @instrumented
264
+
265
+ @instrumented = true
266
+
267
+ Module.send :include, NewRelic::Agent::MethodTracer
268
+
269
+ # Instrumentation for the key code points inside rails for monitoring by NewRelic.
270
+ # note this file is loaded only if the newrelic agent is enabled (through config/newrelic.yml)
271
+ instrumentation_path = File.join(File.dirname(__FILE__), 'agent','instrumentation')
272
+ instrumentation_files = [ ] <<
273
+ File.join(instrumentation_path, '*.rb') <<
274
+ File.join(instrumentation_path, app.to_s, '*.rb')
275
+ instrumentation_files.each do | pattern |
276
+ Dir.glob(pattern) do |file|
277
+ begin
278
+ log.debug "Processing instrumentation file '#{file}'"
279
+ require file
280
+ rescue => e
281
+ log.error "Error loading instrumentation file '#{file}': #{e}"
282
+ log.debug e.backtrace.join("\n")
283
+ end
284
+ end
285
+ end
286
+
287
+ log.debug "Finished instrumentation"
288
+ end
289
+
290
+ def load_samplers
291
+ agent = NewRelic::Agent.instance
292
+ agent.stats_engine.add_sampler NewRelic::Agent::Samplers::MongrelSampler.new if local_env.mongrel
293
+ agent.stats_engine.add_harvest_sampler NewRelic::Agent::Samplers::CpuSampler.new unless defined? Java
294
+ agent.stats_engine.add_sampler NewRelic::Agent::Samplers::MemorySampler.new
295
+ end
296
+
297
+ protected
298
+
299
+ # Append framework specific environment information for uploading to
300
+ # the server for change detection. Override in subclasses
301
+ def append_environment_info; end
302
+
303
+ # Look up the ip address of the host using the pure ruby lookup
304
+ # to prevent blocking. If that fails, fall back to the regular
305
+ # IPSocket library. Return nil if we can't find the host ip
306
+ # address and don't have a good default.
307
+ def convert_to_ip_address(host)
308
+ # here we leave it as a host name since the cert verification
309
+ # needs it in host form
310
+ return host if verify_certificate?
311
+ return nil if host.nil? || host.downcase == "localhost"
312
+ # Fall back to known ip address in the common case
313
+ ip_address = '65.74.177.195' if host.downcase == 'collector.newrelic.com'
314
+ begin
315
+ ip_address = Resolv.getaddress(host)
316
+ log.info "Resolved #{host} to #{ip_address}"
317
+ rescue => e
318
+ log.warn "DNS Error caching IP address: #{e}"
319
+ log.debug e.backtrace.join("\n ")
320
+ ip_address = IPSocket::getaddress host rescue ip_address
321
+ end
322
+ ip_address
323
+ end
324
+
325
+ def merge_defaults(settings_hash)
326
+ s = {
327
+ 'host' => 'collector.newrelic.com',
328
+ 'ssl' => false,
329
+ 'log_level' => 'info',
330
+ 'apdex_t' => 1.0
331
+ }
332
+ s.merge! settings_hash if settings_hash
333
+ # monitor_daemons replaced with agent_enabled
334
+ s['agent_enabled'] = s.delete('monitor_daemons') if s['agent_enabled'].nil? && s.include?('monitor_daemons')
335
+ s
336
+ end
337
+ # Control subclasses may override this, but it can be called multiple times.
338
+ def setup_log
339
+ @log_file = "#{log_path}/newrelic_agent.log"
340
+ @log = Logger.new @log_file
341
+
342
+ # change the format just for our logger
343
+
344
+ def @log.format_message(severity, timestamp, progname, msg)
345
+ "[#{timestamp.strftime("%m/%d/%y %H:%M:%S %z")} #{Socket.gethostname} (#{$$})] #{severity} : #{msg}\n"
346
+ end
347
+
348
+ # set the log level as specified in the config file
349
+ case fetch("log_level","info").downcase
350
+ when "debug"; @log.level = Logger::DEBUG
351
+ when "info"; @log.level = Logger::INFO
352
+ when "warn"; @log.level = Logger::WARN
353
+ when "error"; @log.level = Logger::ERROR
354
+ when "fatal"; @log.level = Logger::FATAL
355
+ else @log.level = Logger::INFO
356
+ end
357
+ @log
358
+ end
359
+
360
+ def to_stdout(msg)
361
+ STDOUT.puts "** [NewRelic] " + msg
362
+ end
363
+
364
+ def config_file
365
+ File.expand_path(File.join(root,"config","newrelic.yml"))
366
+ end
367
+
368
+ def log_path
369
+ path = File.join(root,'log')
370
+ unless File.directory? path
371
+ path = '.'
372
+ end
373
+ File.expand_path(path)
374
+ end
375
+
376
+ # Create the concrete class for environment specific behavior:
377
+ def self.new_instance
378
+ @local_env = NewRelic::LocalEnvironment.new
379
+ case @local_env.framework
380
+ when :test
381
+ require File.join(newrelic_root, "test", "config", "test_control.rb")
382
+ NewRelic::Control::Test.new @local_env
383
+ when :merb
384
+ require 'new_relic/control/merb'
385
+ NewRelic::Control::Merb.new @local_env
386
+ when :rails
387
+ require 'new_relic/control/rails'
388
+ NewRelic::Control::Rails.new @local_env
389
+ when :ruby
390
+ require 'new_relic/control/ruby'
391
+ NewRelic::Control::Ruby.new @local_env
392
+ else
393
+ raise "Unknown framework: #{@local_env.framework}"
394
+ end
395
+ end
396
+
397
+ def initialize local_env
398
+ @local_env = local_env
399
+ newrelic_file = config_file
400
+ # Next two are for populating the newrelic.yml via erb binding, necessary
401
+ # when using the default newrelic.yml file
402
+ generated_for_user = ''
403
+ license_key=''
404
+ if !File.exists?(config_file)
405
+ log! "Cannot find newrelic.yml file at #{config_file}."
406
+ @yaml = {}
407
+ else
408
+ @yaml = YAML.load(ERB.new(File.read(config_file)).result(binding))
409
+ end
410
+ rescue ScriptError, StandardError => e
411
+ puts e
412
+ puts e.backtrace.join("\n")
413
+ raise "Error reading newrelic.yml file: #{e}"
414
+ end
415
+
416
+ # The root directory for the plugin or gem
417
+ def self.newrelic_root
418
+ File.expand_path(File.join(File.dirname(__FILE__),"..",".."))
419
+ end
420
+ def newrelic_root
421
+ self.class.newrelic_root
422
+ end
423
+ end
424
+ end