newrelic_rpm 2.9.9 → 2.10.3

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 (120) hide show
  1. data/CHANGELOG +117 -49
  2. data/bin/mongrel_rpm +2 -2
  3. data/install.rb +42 -33
  4. data/lib/new_relic/agent.rb +149 -39
  5. data/lib/new_relic/agent/agent.rb +139 -122
  6. data/lib/new_relic/agent/busy_calculator.rb +91 -0
  7. data/lib/new_relic/agent/collection_helper.rb +11 -2
  8. data/lib/new_relic/agent/error_collector.rb +33 -27
  9. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +30 -26
  10. data/lib/new_relic/agent/instrumentation/authlogic.rb +8 -0
  11. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +316 -105
  12. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +22 -0
  13. data/lib/new_relic/agent/instrumentation/memcache.rb +18 -12
  14. data/lib/new_relic/agent/instrumentation/merb/errors.rb +2 -1
  15. data/lib/new_relic/agent/instrumentation/metric_frame.rb +258 -0
  16. data/lib/new_relic/agent/instrumentation/net.rb +7 -11
  17. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +1 -1
  18. data/lib/new_relic/agent/instrumentation/rack.rb +109 -0
  19. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +6 -5
  20. data/lib/new_relic/agent/instrumentation/rails/errors.rb +7 -7
  21. data/lib/new_relic/agent/instrumentation/sinatra.rb +46 -0
  22. data/lib/new_relic/agent/method_tracer.rb +305 -150
  23. data/lib/new_relic/agent/sampler.rb +34 -0
  24. data/lib/new_relic/agent/samplers/cpu_sampler.rb +11 -1
  25. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +37 -0
  26. data/lib/new_relic/agent/samplers/memory_sampler.rb +22 -10
  27. data/lib/new_relic/agent/samplers/object_sampler.rb +24 -0
  28. data/lib/new_relic/agent/shim_agent.rb +10 -0
  29. data/lib/new_relic/agent/stats_engine.rb +16 -278
  30. data/lib/new_relic/agent/stats_engine/metric_stats.rb +118 -0
  31. data/lib/new_relic/agent/stats_engine/samplers.rb +81 -0
  32. data/lib/new_relic/agent/stats_engine/transactions.rb +149 -0
  33. data/lib/new_relic/agent/transaction_sampler.rb +73 -67
  34. data/lib/new_relic/agent/worker_loop.rb +69 -68
  35. data/lib/new_relic/commands/deployments.rb +4 -6
  36. data/lib/new_relic/control.rb +121 -60
  37. data/lib/new_relic/control/external.rb +13 -0
  38. data/lib/new_relic/control/merb.rb +2 -0
  39. data/lib/new_relic/control/rails.rb +16 -6
  40. data/lib/new_relic/control/ruby.rb +8 -5
  41. data/lib/new_relic/control/sinatra.rb +18 -0
  42. data/lib/new_relic/delayed_job_injection.rb +25 -0
  43. data/lib/new_relic/histogram.rb +89 -0
  44. data/lib/new_relic/local_environment.rb +64 -30
  45. data/lib/new_relic/metric_data.rb +15 -6
  46. data/lib/new_relic/metric_parser.rb +14 -1
  47. data/lib/new_relic/metric_parser/active_record.rb +14 -0
  48. data/lib/new_relic/metric_parser/controller.rb +5 -2
  49. data/lib/new_relic/metric_parser/external.rb +50 -0
  50. data/lib/new_relic/metric_parser/other_transaction.rb +15 -0
  51. data/lib/new_relic/metric_parser/web_frontend.rb +14 -0
  52. data/lib/new_relic/metric_spec.rb +39 -20
  53. data/lib/new_relic/metrics.rb +9 -7
  54. data/lib/new_relic/noticed_error.rb +6 -8
  55. data/lib/new_relic/rack/metric_app.rb +5 -4
  56. data/lib/new_relic/rack/{newrelic.ru → mongrel_rpm.ru} +4 -4
  57. data/lib/new_relic/rack/newrelic.yml +1 -0
  58. data/lib/new_relic/{rack.rb → rack_app.rb} +0 -0
  59. data/lib/new_relic/recipes.rb +1 -1
  60. data/lib/new_relic/stats.rb +40 -26
  61. data/lib/new_relic/transaction_analysis.rb +5 -2
  62. data/lib/new_relic/transaction_sample.rb +134 -55
  63. data/lib/new_relic/version.rb +27 -20
  64. data/lib/new_relic_api.rb +67 -47
  65. data/lib/newrelic_rpm.rb +5 -5
  66. data/lib/tasks/tests.rake +2 -0
  67. data/newrelic.yml +69 -29
  68. data/test/active_record_fixtures.rb +2 -2
  69. data/test/config/newrelic.yml +4 -7
  70. data/test/config/test_control.rb +1 -2
  71. data/test/new_relic/agent/active_record_instrumentation_test.rb +115 -31
  72. data/test/new_relic/agent/agent_controller_test.rb +274 -0
  73. data/test/new_relic/agent/agent_test_controller.rb +42 -6
  74. data/test/new_relic/agent/busy_calculator_test.rb +79 -0
  75. data/test/new_relic/agent/collection_helper_test.rb +10 -3
  76. data/test/new_relic/agent/error_collector_test.rb +35 -17
  77. data/test/new_relic/agent/method_tracer_test.rb +60 -20
  78. data/test/new_relic/agent/metric_data_test.rb +2 -2
  79. data/test/new_relic/agent/metric_frame_test.rb +51 -0
  80. data/test/new_relic/agent/net_instrumentation_test.rb +77 -0
  81. data/test/new_relic/agent/{agent_test.rb → rpm_agent_test.rb} +26 -5
  82. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +79 -0
  83. data/test/new_relic/{samplers_test.rb → agent/stats_engine/samplers_test.rb} +23 -22
  84. data/test/new_relic/agent/{stats_engine_test.rb → stats_engine/stats_engine_test.rb} +19 -101
  85. data/test/new_relic/agent/task_instrumentation_test.rb +176 -0
  86. data/test/new_relic/agent/transaction_sample_builder_test.rb +2 -2
  87. data/test/new_relic/agent/transaction_sample_test.rb +53 -38
  88. data/test/new_relic/agent/transaction_sampler_test.rb +101 -33
  89. data/test/new_relic/agent/worker_loop_test.rb +16 -14
  90. data/test/new_relic/control_test.rb +26 -13
  91. data/test/new_relic/metric_parser_test.rb +31 -1
  92. data/test/new_relic/metric_spec_test.rb +2 -2
  93. data/test/new_relic/stats_test.rb +0 -8
  94. data/test/new_relic/version_number_test.rb +31 -1
  95. data/test/test_helper.rb +37 -1
  96. data/ui/controllers/newrelic_controller.rb +19 -11
  97. data/ui/helpers/google_pie_chart.rb +5 -11
  98. data/ui/helpers/newrelic_helper.rb +40 -35
  99. data/ui/views/layouts/newrelic_default.rhtml +7 -7
  100. data/ui/views/newrelic/_sample.rhtml +5 -1
  101. data/ui/views/newrelic/images/new-relic-rpm-desktop.gif +0 -0
  102. data/ui/views/newrelic/images/textmate.png +0 -0
  103. data/ui/views/newrelic/index.rhtml +13 -1
  104. data/ui/views/newrelic/show_sample.rhtml +5 -2
  105. data/ui/views/newrelic/stylesheets/style.css +54 -3
  106. metadata +65 -145
  107. data/Manifest +0 -143
  108. data/Rakefile +0 -22
  109. data/init.rb +0 -38
  110. data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +0 -127
  111. data/lib/new_relic/agent/instrumentation/error_instrumentation.rb +0 -14
  112. data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +0 -13
  113. data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +0 -38
  114. data/lib/new_relic/agent/patch_const_missing.rb +0 -125
  115. data/lib/new_relic/agent/samplers/mongrel_sampler.rb +0 -22
  116. data/lib/new_relic/metric_parser/database.rb +0 -23
  117. data/newrelic_rpm.gemspec +0 -35
  118. data/test/new_relic/agent/classloader_patch_test.rb +0 -56
  119. data/test/new_relic/agent/controller_test.rb +0 -107
  120. data/test/new_relic/agent/dispatcher_instrumentation_test.rb +0 -70
@@ -1,4 +1,5 @@
1
- module NewRelic::Agent
1
+ module NewRelic
2
+ module Agent
2
3
 
3
4
  # A worker loop executes a set of registered tasks on a single thread.
4
5
  # A task is a proc or block with a specified call period in seconds.
@@ -13,7 +14,7 @@ module NewRelic::Agent
13
14
  @should_run = true
14
15
  @pid = $$
15
16
  end
16
-
17
+
17
18
  # Run infinitely, calling the registered tasks at their specified
18
19
  # call periods. The caller is responsible for creating the thread
19
20
  # that runs this worker loop
@@ -30,89 +31,89 @@ module NewRelic::Agent
30
31
  def stop
31
32
  @should_run = false
32
33
  end
33
-
34
+
34
35
  MIN_CALL_PERIOD = 0.1
35
36
 
36
37
  # add a task to the worker loop. The task will be called approximately once
37
38
  # every call_period seconds. The task is passed as a block
38
- def add_task(call_period, &task_proc)
39
+ def add_task(call_period, desc="", &task_proc)
39
40
  if call_period < MIN_CALL_PERIOD
40
41
  raise ArgumentError, "Invalid Call Period (must be > #{MIN_CALL_PERIOD}): #{call_period}"
41
42
  end
42
- @tasks << LoopTask.new(call_period, &task_proc)
43
+ @tasks << LoopTask.new(call_period, desc, &task_proc)
43
44
  end
44
-
45
+
45
46
  private
46
- def next_task
47
- @tasks.inject do |soonest, task|
48
- (task.next_invocation_time < soonest.next_invocation_time) ? task : soonest
49
- end
47
+ def next_task
48
+ @tasks.inject do |soonest, task|
49
+ (task.next_invocation_time < soonest.next_invocation_time) ? task : soonest
50
50
  end
51
+ end
51
52
 
52
- def run_next_task
53
- if @tasks.empty?
54
- sleep 5.0
55
- return
56
- end
57
-
58
- # get the next task to be executed, which is the task with the lowest (ie, soonest)
59
- # next invocation time.
60
- task = next_task
61
-
62
- # sleep in chunks no longer than 1 second
63
- while Time.now < task.next_invocation_time
64
-
65
- # sleep until this next task's scheduled invocation time
66
- sleep_time = [task.next_invocation_time - Time.now, 0.000001].max
67
- sleep_time = (sleep_time > 1) ? 1 : sleep_time
68
-
69
- sleep sleep_time
70
-
71
- return if !keep_running
72
- end
73
-
74
- begin
75
- # wrap task execution in a block that won't collect a TT
76
- NewRelic::Agent.disable_transaction_tracing do
77
- task.execute
78
- end
79
- rescue ServerError => e
80
- log.debug "Server Error: #{e}"
81
- rescue RuntimeError => e
82
- # This is probably a server error which has been logged in the server along
83
- # with your account name. Check and see if the agent listener is in the
84
- # stack trace and log it quietly if it is.
85
- message = "Error running task in worker loop, likely a server error (#{e})"
86
- if e.backtrace.grep(/agent_listener/).empty?
87
- log.error message
88
- else
89
- log.debug message
90
- log.debug e.backtrace.join("\n")
91
- end
92
- rescue Timeout::Error, NewRelic::Agent::IgnoreSilentlyException
93
- # Want to ignore these because they are handled already
94
- rescue ScriptError, StandardError => e
95
- log.error "Error running task in Agent Worker Loop (#{e.class}): #{e} "
96
- log.debug e.backtrace.join("\n")
97
- end
53
+ def run_next_task
54
+ if @tasks.empty?
55
+ sleep 5.0
56
+ return
98
57
  end
99
58
 
100
- class LoopTask
59
+ # get the next task to be executed, which is the task with the lowest (ie, soonest)
60
+ # next invocation time.
61
+ task = next_task
101
62
 
102
- def initialize(call_period, &task_proc)
103
- @call_period = call_period
104
- @last_invocation_time = Time.now
105
- @task = task_proc
106
- end
63
+ while Time.now < task.next_invocation_time
64
+
65
+ # sleep until this next task's scheduled invocation time
66
+ sleep_time = task.next_invocation_time - Time.now
67
+ sleep sleep_time if sleep_time > 0
68
+ return if !keep_running
69
+ end
107
70
 
108
- def next_invocation_time
109
- @last_invocation_time + @call_period
71
+ begin
72
+ task.execute if keep_running
73
+ rescue ServerError => e
74
+ log.debug "Server Error: #{e}"
75
+ rescue NewRelic::Agent::ForceRestartException => e
76
+ # blow out the loop
77
+ raise
78
+ rescue RuntimeError => e
79
+ # This is probably a server error which has been logged in the server along
80
+ # with your account name. Check and see if the agent listener is in the
81
+ # stack trace and log it quietly if it is.
82
+ message = "Error running task in worker loop, likely a server error (#{e})"
83
+ if e.backtrace.grep(/agent_listener/).empty?
84
+ log.error message
85
+ else
86
+ log.debug message
87
+ log.debug e.backtrace.join("\n")
110
88
  end
89
+ rescue Timeout::Error, NewRelic::Agent::IgnoreSilentlyException
90
+ # Want to ignore these because they are handled already
91
+ rescue ScriptError, StandardError => e
92
+ log.error "Error running task in Agent Worker Loop '#{e}': #{e.backtrace.first}"
93
+ log.debug e.backtrace.join("\n")
94
+ end
95
+ end
96
+
97
+ class LoopTask
111
98
 
112
- def execute
113
- @last_invocation_time = Time.now
114
- @task.call
115
- end
99
+ def initialize(call_period, desc="", &task_proc)
100
+ @call_period = call_period
101
+ @last_invocation_time = Time.now
102
+ @task = task_proc
103
+ @desc = desc
116
104
  end
105
+ def to_s
106
+ "Task[#{@desc}]"
107
+ end
108
+ def next_invocation_time
109
+ @last_invocation_time + @call_period
110
+ end
111
+
112
+ def execute
113
+ @last_invocation_time = Time.now
114
+ @task.call
115
+ end
116
+ end
117
117
  end
118
118
  end
119
+ end
@@ -87,7 +87,7 @@ module NewRelic
87
87
  response = http.request(request)
88
88
 
89
89
  if response.is_a? Net::HTTPSuccess
90
- info "Recorded deployment to NewRelic RPM (#{@description || Time.now })"
90
+ info "Recorded deployment to '#{@appname}' (#{@description || Time.now })"
91
91
  else
92
92
  err_string = [ "Unexpected response from server: #{response.code}: #{response.message}" ]
93
93
  begin
@@ -107,7 +107,7 @@ module NewRelic
107
107
  raise
108
108
  rescue Exception => e
109
109
  err "Unexpected error attempting to connect to #{config.api_server}"
110
- info e.backtrace.join("\n")
110
+ info "#{e}: #{e.backtrace.join("\n ")}"
111
111
  raise CommandFailure.new(e.to_s, -1)
112
112
  end
113
113
  end
@@ -115,9 +115,9 @@ module NewRelic
115
115
  private
116
116
 
117
117
  def options
118
- OptionParser.new "Usage: #{self.class.command} [OPTIONS] [description] ", 40 do |o|
118
+ OptionParser.new %Q{Usage: #{$0} [OPTIONS] ["description"] }, 40 do |o|
119
119
  o.separator "OPTIONS:"
120
- o.on("-a", "--appname=DIR", String,
120
+ o.on("-a", "--appname=NAME", String,
121
121
  "Set the application name.",
122
122
  "Default is app_name setting in newrelic.yml") { | e | @appname = e }
123
123
  o.on("-e", "--environment=name", String,
@@ -131,8 +131,6 @@ module NewRelic
131
131
  o.on("-c", "--changes",
132
132
  "Read in a change log from the standard input") { @changelog = STDIN.read }
133
133
  o.on("-h", "--help", "Print this help") { raise CommandFailure.new(o.help, 0) }
134
- o.separator ""
135
- o.separator 'description = "short text"'
136
134
  end
137
135
  end
138
136
 
@@ -6,7 +6,6 @@ require 'socket'
6
6
  require 'net/https'
7
7
  require 'logger'
8
8
 
9
-
10
9
  module NewRelic
11
10
 
12
11
  # The Control is a singleton responsible for the startup and
@@ -18,20 +17,38 @@ module NewRelic
18
17
  #
19
18
  class Control
20
19
 
20
+ # A flag used in dev mode to indicate if profiling is available
21
+ def profiling?
22
+ @profiling
23
+ end
24
+
25
+ def profiling_available?
26
+ @profiling_available ||=
27
+ begin
28
+ require 'ruby-prof'
29
+ true
30
+ rescue LoadError; end
31
+ end
32
+ # Set the flag for capturing profiles in dev mode. If RubyProf is not
33
+ # loaded a true value is ignored.
34
+ def profiling=(val)
35
+ @profiling = profiling_available? && val && defined?(RubyProf)
36
+ end
37
+
21
38
  attr_accessor :log_file
22
39
  # The env is the setting used to identify which section of the newrelic.yml
23
40
  # to load. This defaults to a framework specific value, such as ENV['RAILS_ENV']
24
41
  # but can be overridden as long as you set it before calling #init_plugin
25
42
  attr_writer :env
26
43
  attr_reader :local_env
27
-
28
- # Structs holding info for the remote server and proxy server
44
+
45
+ # Structs holding info for the remote server and proxy server
29
46
  class Server < Struct.new :name, :port, :ip #:nodoc:
30
47
  def to_s; "#{name}:#{port}"; end
31
48
  end
32
49
 
33
50
  ProxyServer = Struct.new :name, :port, :user, :password #:nodoc:
34
-
51
+
35
52
  # Access the Control singleton, lazy initialized
36
53
  def self.instance
37
54
  @instance ||= new_instance
@@ -57,11 +74,15 @@ module NewRelic
57
74
  # is called at most once.
58
75
  #
59
76
  def init_plugin(options={})
77
+ options['app_name'] = ENV['NEWRELIC_APP_NAME'] if ENV['NEWRELIC_APP_NAME']
78
+
60
79
  require 'new_relic/agent'
61
80
  # Merge the stringified options into the config as overrides:
62
81
  logger_override = options.delete(:log)
63
- environment_name = options.delete(:env)
64
- self.env = environment_name if environment_name
82
+ environment_name = options.delete(:env) and self.env = environment_name
83
+ dispatcher = options.delete(:dispatcher) and @local_env.dispatcher = dispatcher
84
+ dispatcher_instance_id = options.delete(:dispatcher_instance_id) and @local_env.dispatcher_instance_id = dispatcher_instance_id
85
+
65
86
  # Clear out the settings, if they've already been loaded. It may be that
66
87
  # between calling init_plugin the first time and the second time, the env
67
88
  # has been overridden
@@ -73,6 +94,10 @@ module NewRelic
73
94
  # Try to grab the log filename
74
95
  @log_file = @log.instance_eval { @logdev.filename rescue nil }
75
96
  end
97
+ # An artifact of earlier implementation, we put both #add_method_tracer and #trace_execution
98
+ # methods in the module methods.
99
+ Module.send :include, NewRelic::Agent::MethodTracer::ClassMethods
100
+ Module.send :include, NewRelic::Agent::MethodTracer::InstanceMethods
76
101
  init_config(options)
77
102
  if agent_enabled? && !@started
78
103
  setup_log unless logger_override
@@ -102,7 +127,14 @@ module NewRelic
102
127
  @settings = (@yaml && merge_defaults(@yaml[env])) || {}
103
128
  # At the time we bind the settings, we also need to run this little piece
104
129
  # 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
130
+ if self['multi_homed'] && app_names.size > 0
131
+ if @local_env.dispatcher_instance_id
132
+ @local_env.dispatcher_instance_id << ":#{app_names.first}"
133
+ else
134
+ @local_env.dispatcher_instance_id = app_names.first
135
+ end
136
+ end
137
+
106
138
  end
107
139
  @settings
108
140
  end
@@ -124,6 +156,10 @@ module NewRelic
124
156
  ###################################
125
157
  # Agent config conveniences
126
158
 
159
+ def apdex_t
160
+ # Always initialized with a default
161
+ fetch('apdex_t').to_f
162
+ end
127
163
  def license_key
128
164
  fetch('license_key')
129
165
  end
@@ -132,11 +168,22 @@ module NewRelic
132
168
  end
133
169
  # True if we are sending data to the server, monitoring production
134
170
  def monitor_mode?
135
- fetch('enabled', nil)
171
+ fetch('monitor_mode', fetch('enabled'))
136
172
  end
137
173
  # True if we are capturing data and displaying in /newrelic
138
174
  def developer_mode?
139
- fetch('developer', nil)
175
+ fetch('developer_mode', fetch('developer'))
176
+ end
177
+ # True if the app runs in multi-threaded mode
178
+ def multi_threaded?
179
+ fetch('multi_threaded')
180
+ end
181
+ # True if we should view files in textmate
182
+ def use_textmate?
183
+ fetch('textmate')
184
+ end
185
+ def post_size_limit
186
+ fetch('post_size_limit', 2 * 1024 * 1024)
140
187
  end
141
188
  # True if dev mode or monitor mode are enabled, and we are running
142
189
  # inside a valid dispatcher like mongrel or passenger. Can be overridden
@@ -144,7 +191,7 @@ module NewRelic
144
191
  # agent_enabled config option when true or false.
145
192
  def agent_enabled?
146
193
  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'
194
+ return self['agent_enabled'].to_s =~ /true|on|yes/i if !self['agent_enabled'].nil? && self['agent_enabled'] != 'auto'
148
195
  return false if ENV['NEWRELIC_ENABLE'].to_s =~ /false|off|no/i
149
196
  return true if self['monitor_daemons'].to_s =~ /true|on|yes/i
150
197
  return true if ENV['NEWRELIC_ENABLE'].to_s =~ /true|on|yes/i
@@ -184,15 +231,15 @@ module NewRelic
184
231
  def api_server
185
232
  api_host = self['api_host'] || 'rpm.newrelic.com'
186
233
  @api_server ||=
187
- NewRelic::Control::Server.new \
188
- api_host,
189
- (self['api_port'] || self['port'] || (use_ssl? ? 443 : 80)).to_i,
190
- nil
234
+ NewRelic::Control::Server.new \
235
+ api_host,
236
+ (self['api_port'] || self['port'] || (use_ssl? ? 443 : 80)).to_i,
237
+ nil
191
238
  end
192
239
 
193
240
  def proxy_server
194
241
  @proxy_server ||=
195
- NewRelic::Control::ProxyServer.new self['proxy_host'], self['proxy_port'], self['proxy_user'], self['proxy_pass']
242
+ NewRelic::Control::ProxyServer.new self['proxy_host'], self['proxy_port'], self['proxy_user'], self['proxy_pass']
196
243
  end
197
244
 
198
245
  def server_from_host(hostname=nil)
@@ -215,6 +262,7 @@ module NewRelic
215
262
  http_class = Net::HTTP::Proxy(proxy_server.name, proxy_server.port,
216
263
  proxy_server.user, proxy_server.password)
217
264
  http = http_class.new(host.ip || host.name, host.port)
265
+ log.debug("Http Connection opened to #{host.ip||host.name}:#{host.port}")
218
266
  if use_ssl?
219
267
  http.use_ssl = true
220
268
  if verify_certificate?
@@ -256,50 +304,57 @@ module NewRelic
256
304
  # Once we install instrumentation, you can't undo that by installing the shim.
257
305
  raise "Cannot install the Agent shim after instrumentation has already been installed!" if @instrumented
258
306
  NewRelic::Agent.agent = NewRelic::Agent::ShimAgent.instance
259
- Module.send :include, NewRelic::Agent::MethodTracerShim
260
307
  end
261
308
 
309
+ # Add instrumentation. Don't call this directly. Use NewRelic::Agent#add_instrumentation.
310
+ # This will load the file synchronously if we've already loaded the default
311
+ # instrumentation.
312
+ #
313
+ def add_instrumentation pattern
314
+ if @instrumented
315
+ load_instrumentation_files pattern
316
+ else
317
+ @instrumentation_files << pattern
318
+ end
319
+ end
262
320
  def install_instrumentation
263
321
  return if @instrumented
264
322
 
265
323
  @instrumented = true
266
324
 
267
- Module.send :include, NewRelic::Agent::MethodTracer
268
-
269
325
  # Instrumentation for the key code points inside rails for monitoring by NewRelic.
270
326
  # note this file is loaded only if the newrelic agent is enabled (through config/newrelic.yml)
271
327
  instrumentation_path = File.join(File.dirname(__FILE__), 'agent','instrumentation')
272
- instrumentation_files = [ ] <<
328
+ @instrumentation_files <<
273
329
  File.join(instrumentation_path, '*.rb') <<
274
330
  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
-
331
+ @instrumentation_files.each { | pattern | load_instrumentation_files pattern }
287
332
  log.debug "Finished instrumentation"
288
333
  end
289
-
334
+
290
335
  def load_samplers
291
336
  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
- begin
295
- agent.stats_engine.add_sampler NewRelic::Agent::Samplers::MemorySampler.new
296
- rescue RuntimeError => e
297
- log.error "Cannot add memory sampling: #{e}"
337
+ NewRelic::Agent::Sampler.sampler_classes.each do | subclass |
338
+ begin
339
+ log.debug "#{subclass.name} not supported on this platform." and next if not subclass.supported_on_this_platform?
340
+ sampler = subclass.new
341
+ if subclass.use_harvest_sampler?
342
+ agent.stats_engine.add_harvest_sampler sampler
343
+ log.debug "Registered #{subclass.name} for harvest time sampling"
344
+ else
345
+ agent.stats_engine.add_sampler sampler
346
+ log.debug "Registered #{subclass.name} for periodic sampling"
347
+ end
348
+ rescue NewRelic::Agent::Sampler::Unsupported => e
349
+ log.info "#{subclass} sampler not available: #{e}"
350
+ rescue => e
351
+ log.error "Error registering sampler: #{e}, #{e.backtrace.join("\n")}"
352
+ end
298
353
  end
299
354
  end
300
-
355
+
301
356
  protected
302
-
357
+
303
358
  # Append framework specific environment information for uploading to
304
359
  # the server for change detection. Override in subclasses
305
360
  def append_environment_info; end
@@ -325,7 +380,7 @@ module NewRelic
325
380
  end
326
381
  ip_address
327
382
  end
328
-
383
+
329
384
  def merge_defaults(settings_hash)
330
385
  s = {
331
386
  'host' => 'collector.newrelic.com',
@@ -338,6 +393,7 @@ module NewRelic
338
393
  s['agent_enabled'] = s.delete('monitor_daemons') if s['agent_enabled'].nil? && s.include?('monitor_daemons')
339
394
  s
340
395
  end
396
+
341
397
  # Control subclasses may override this, but it can be called multiple times.
342
398
  def setup_log
343
399
  @log_file = "#{log_path}/newrelic_agent.log"
@@ -348,14 +404,14 @@ module NewRelic
348
404
  def @log.format_message(severity, timestamp, progname, msg)
349
405
  "[#{timestamp.strftime("%m/%d/%y %H:%M:%S %z")} #{Socket.gethostname} (#{$$})] #{severity} : #{msg}\n"
350
406
  end
351
-
407
+
352
408
  # set the log level as specified in the config file
353
409
  case fetch("log_level","info").downcase
354
- when "debug"; @log.level = Logger::DEBUG
355
- when "info"; @log.level = Logger::INFO
356
- when "warn"; @log.level = Logger::WARN
357
- when "error"; @log.level = Logger::ERROR
358
- when "fatal"; @log.level = Logger::FATAL
410
+ when "debug" then @log.level = Logger::DEBUG
411
+ when "info" then @log.level = Logger::INFO
412
+ when "warn" then @log.level = Logger::WARN
413
+ when "error" then @log.level = Logger::ERROR
414
+ when "fatal" then @log.level = Logger::FATAL
359
415
  else @log.level = Logger::INFO
360
416
  end
361
417
  @log
@@ -380,26 +436,18 @@ module NewRelic
380
436
  # Create the concrete class for environment specific behavior:
381
437
  def self.new_instance
382
438
  @local_env = NewRelic::LocalEnvironment.new
383
- case @local_env.framework
384
- when :test
439
+ if @local_env.framework == :test
385
440
  require File.join(newrelic_root, "test", "config", "test_control.rb")
386
441
  NewRelic::Control::Test.new @local_env
387
- when :merb
388
- require 'new_relic/control/merb'
389
- NewRelic::Control::Merb.new @local_env
390
- when :rails
391
- require 'new_relic/control/rails'
392
- NewRelic::Control::Rails.new @local_env
393
- when :ruby
394
- require 'new_relic/control/ruby'
395
- NewRelic::Control::Ruby.new @local_env
396
- else
397
- raise "Unknown framework: #{@local_env.framework}"
442
+ else
443
+ require "new_relic/control/#{@local_env.framework}.rb"
444
+ NewRelic::Control.const_get(@local_env.framework.to_s.capitalize).new @local_env
398
445
  end
399
446
  end
400
447
 
401
448
  def initialize local_env
402
449
  @local_env = local_env
450
+ @instrumentation_files = []
403
451
  newrelic_file = config_file
404
452
  # Next two are for populating the newrelic.yml via erb binding, necessary
405
453
  # when using the default newrelic.yml file
@@ -424,5 +472,18 @@ module NewRelic
424
472
  def newrelic_root
425
473
  self.class.newrelic_root
426
474
  end
475
+
476
+ def load_instrumentation_files pattern
477
+ Dir.glob(pattern) do |file|
478
+ begin
479
+ log.debug "Processing instrumentation file '#{file}'"
480
+ require file
481
+ rescue => e
482
+ log.error "Error loading instrumentation file '#{file}': #{e}"
483
+ log.debug e.backtrace.join("\n")
484
+ end
485
+ end
486
+ end
487
+
427
488
  end
428
489
  end