newrelic_rpm 3.1.1 → 3.1.2.beta1

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 (30) hide show
  1. data/CHANGELOG +6 -0
  2. data/lib/new_relic/agent/agent.rb +3 -11
  3. data/lib/new_relic/agent/instrumentation/active_merchant.rb +1 -1
  4. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +3 -2
  5. data/lib/new_relic/agent/instrumentation/net.rb +1 -1
  6. data/lib/new_relic/agent/instrumentation/rails/active_record_instrumentation.rb +1 -0
  7. data/lib/new_relic/agent/instrumentation/rails3/active_record_instrumentation.rb +1 -0
  8. data/lib/new_relic/agent/samplers/memory_sampler.rb +1 -1
  9. data/lib/new_relic/agent/shim_agent.rb +4 -4
  10. data/lib/new_relic/agent/stats_engine/metric_stats.rb +11 -24
  11. data/lib/new_relic/control/frameworks/rails.rb +7 -2
  12. data/lib/new_relic/control/frameworks/rails3.rb +5 -2
  13. data/lib/new_relic/control/instance_methods.rb +9 -2
  14. data/lib/new_relic/data_serialization.rb +35 -34
  15. data/lib/new_relic/language_support.rb +66 -0
  16. data/lib/new_relic/transaction_sample.rb +9 -7
  17. data/lib/new_relic/transaction_sample/segment.rb +28 -22
  18. data/lib/new_relic/version.rb +2 -2
  19. data/newrelic_rpm.gemspec +4 -3
  20. data/test/new_relic/agent/agent/start_test.rb +7 -7
  21. data/test/new_relic/agent/agent_test.rb +20 -1
  22. data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +20 -18
  23. data/test/new_relic/agent/instrumentation/instrumentation_test.rb +1 -1
  24. data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +6 -6
  25. data/test/new_relic/agent/transaction_sample_builder_test.rb +1 -1
  26. data/test/new_relic/data_serialization_test.rb +73 -3
  27. data/test/new_relic/transaction_sample/segment_test.rb +26 -16
  28. data/test/new_relic/transaction_sample_test.rb +11 -17
  29. data/test/test_helper.rb +12 -2
  30. metadata +66 -98
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ v3.1.2
2
+ * Fixed some thread safety issues
3
+ * Work around for Ruby 1.8.7 Marshal crash bug
4
+ * Numerous community patches (Gabriel Horner, Bradley Harris, Diego Garcia,
5
+ Tommy Sullivan, Greg Hazel, John Thomas Marino, Paul Elliott, Pan Thomakos)
6
+
1
7
  v3.1.1
2
8
  * Support for Rails 3.1 (thanks to Ben Hoskings via github)
3
9
  * Support for Rubinius
@@ -402,16 +402,6 @@ module NewRelic
402
402
  NewRelic::Agent.disable_all_tracing { connect(:keep_retrying => false) }
403
403
  end
404
404
 
405
- # Are we in boss mode, using rubinius?
406
- def using_rubinius?
407
- RUBY_VERSION =~ /rubinius/i
408
- end
409
-
410
- # Is this really a world-within-a-world, running JRuby?
411
- def using_jruby?
412
- defined?(JRuby)
413
- end
414
-
415
405
  # If we're using sinatra, old versions run in an at_exit
416
406
  # block so we should probably know that
417
407
  def using_sinatra?
@@ -421,7 +411,9 @@ module NewRelic
421
411
  # we should not set an at_exit block if people are using
422
412
  # these as they don't do standard at_exit behavior per MRI/YARV
423
413
  def weird_ruby?
424
- using_rubinius? || using_jruby? || using_sinatra?
414
+ NewRelic::LanguageSupport.using_rubinius? ||
415
+ NewRelic::LanguageSupport.using_jruby? ||
416
+ using_sinatra?
425
417
  end
426
418
 
427
419
  # Installs our exit handler, which exploits the weird
@@ -1,6 +1,6 @@
1
1
  DependencyDetection.defer do
2
2
  depends_on do
3
- defined?(ActiveMerchant)
3
+ defined?(ActiveMerchant) && defined?(ActiveMerchant::Billing)
4
4
  end
5
5
 
6
6
  executes do
@@ -6,7 +6,8 @@ DependencyDetection.defer do
6
6
  end
7
7
 
8
8
  depends_on do
9
- defined?(::Delayed) && defined?(::Delayed::Job)
9
+ defined?(::Delayed) && defined?(::Delayed::Job) &&
10
+ Delayed::Job.respond_to?(:invoke_job)
10
11
  end
11
12
 
12
13
  executes do
@@ -16,7 +17,7 @@ DependencyDetection.defer do
16
17
  executes do
17
18
  Delayed::Job.class_eval do
18
19
  include NewRelic::Agent::Instrumentation::ControllerInstrumentation
19
- if self.instance_methods.include?('name')
20
+ if self.instance_methods.include?('name') || self.instance_methods.include?(:name)
20
21
  add_transaction_tracer "invoke_job", :category => 'OtherTransaction/DelayedJob', :path => '#{self.name}'
21
22
  else
22
23
  add_transaction_tracer "invoke_job", :category => 'OtherTransaction/DelayedJob'
@@ -10,7 +10,7 @@ DependencyDetection.defer do
10
10
  executes do
11
11
  Net::HTTP.class_eval do
12
12
  def request_with_newrelic_trace(*args, &block)
13
- metrics = ["External/#{@address}/Net::HTTP/#{args[0].method}","External/#{@address}/all"]
13
+ metrics = ["External/#{@address}/Net::HTTP/#{args[0].method}", "External/#{@address}/all", "External/all"]
14
14
  if NewRelic::Agent::Instrumentation::MetricFrame.recording_web_transaction?
15
15
  metrics << "External/allWeb"
16
16
  else
@@ -17,6 +17,7 @@ module NewRelic
17
17
 
18
18
  # Capture db config if we are going to try to get the explain plans
19
19
  if (defined?(ActiveRecord::ConnectionAdapters::MysqlAdapter) && self.is_a?(ActiveRecord::ConnectionAdapters::MysqlAdapter)) ||
20
+ (defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) && self.is_a?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)) ||
20
21
  (defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) && self.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter))
21
22
  supported_config = @config
22
23
  end
@@ -21,6 +21,7 @@ module NewRelic
21
21
 
22
22
  # Capture db config if we are going to try to get the explain plans
23
23
  if (defined?(ActiveRecord::ConnectionAdapters::MysqlAdapter) && self.is_a?(ActiveRecord::ConnectionAdapters::MysqlAdapter)) ||
24
+ (defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) && self.is_a?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)) ||
24
25
  (defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) && self.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter))
25
26
  supported_config = @config
26
27
  end
@@ -22,7 +22,7 @@ module NewRelic
22
22
  end
23
23
  elsif platform =~ /darwin9/ # 10.5
24
24
  @sampler = ShellPS.new("ps -o rsz")
25
- elsif platform =~ /darwin10/ # 10.6
25
+ elsif platform =~ /darwin1[01]/ # 10.6 & 10.7
26
26
  @sampler = ShellPS.new("ps -o rss")
27
27
  elsif platform =~ /freebsd/
28
28
  @sampler = ShellPS.new("ps -o rss")
@@ -16,11 +16,11 @@ module NewRelic
16
16
  end
17
17
  def after_fork *args; end
18
18
  def start *args; end
19
- def shutdown; end
19
+ def shutdown *args; end
20
20
  def serialize; end
21
- def merge_data_from(*args); end
22
- def push_trace_execution_flag(*args); end
23
- def pop_trace_execution_flag(*args); end
21
+ def merge_data_from *args; end
22
+ def push_trace_execution_flag *args; end
23
+ def pop_trace_execution_flag *args; end
24
24
  def browser_timing_header; "" end
25
25
  def browser_timing_footer; "" end
26
26
  end
@@ -1,3 +1,6 @@
1
+ require 'sync'
2
+ require 'new_relic/language_support'
3
+
1
4
  module NewRelic
2
5
  module Agent
3
6
  class StatsEngine
@@ -5,40 +8,24 @@ module NewRelic
5
8
  module MetricStats
6
9
  # A simple mutex-synchronized hash to make sure our statistics
7
10
  # are internally consistent even in truly-threaded rubies like JRuby
8
- class SynchronizedHash < Hash
9
- def initialize(*args)
10
- @mutex = Mutex.new
11
- super
12
- end
13
-
11
+ class SynchronizedHash < ::Hash
12
+ include NewRelic::LanguageSupport::SynchronizedHash
13
+ include Sync_m
14
+
14
15
  def []=(*args)
15
- @mutex.synchronize {
16
- super
17
- }
18
- end
19
-
20
- def [](*args)
21
- @mutex.synchronize {
22
- super
23
- }
16
+ sync_synchronize { super }
24
17
  end
25
18
 
26
19
  def clear(*args)
27
- @mutex.synchronize {
28
- super
29
- }
20
+ sync_synchronize { super }
30
21
  end
31
22
 
32
23
  def delete(*args)
33
- @mutex.synchronize {
34
- super
35
- }
24
+ sync_synchronize { super }
36
25
  end
37
26
 
38
27
  def delete_if(*args)
39
- @mutex.synchronize {
40
- super
41
- }
28
+ sync_synchronize { super }
42
29
  end
43
30
  end
44
31
 
@@ -86,13 +86,18 @@ module NewRelic
86
86
  end
87
87
 
88
88
  def log!(msg, level=:info)
89
- super unless should_log?
90
- logger.send(level, msg)
89
+ if should_log?
90
+ logger = ::Rails.respond_to?(:logger) ? Rails.logger : ::RAILS_DEFAULT_LOGGER
91
+ logger.send(level, msg)
92
+ else
93
+ super
94
+ end
91
95
  rescue Exception => e
92
96
  super
93
97
  end
94
98
 
95
99
  def to_stdout(message)
100
+ logger = ::Rails.respond_to?(:logger) ? Rails.logger : ::RAILS_DEFAULT_LOGGER
96
101
  logger.info(message)
97
102
  rescue Exception => e
98
103
  super
@@ -32,8 +32,11 @@ module NewRelic
32
32
 
33
33
 
34
34
  def log!(msg, level=:info)
35
- super unless should_log?
36
- logger.send(level, msg)
35
+ if should_log?
36
+ logger.send(level, msg)
37
+ else
38
+ super
39
+ end
37
40
  rescue Exception => e
38
41
  super
39
42
  end
@@ -1,5 +1,9 @@
1
+ require 'new_relic/language_support'
2
+
1
3
  module NewRelic
2
4
  class Control
5
+ include NewRelic::LanguageSupport::Control
6
+
3
7
  # Contains methods that relate to the runtime usage of the control
4
8
  # object. Note that these are subject to override in the
5
9
  # NewRelic::Control::Framework classes that are actually instantiated
@@ -147,8 +151,7 @@ module NewRelic
147
151
  puts "Cannot find or read #{newrelic_file}"
148
152
  @yaml = {}
149
153
  else
150
- YAML::ENGINE.yamler = 'syck' if defined?(YAML::ENGINE)
151
- @yaml = YAML.load(ERB.new(File.read(newrelic_file)).result(binding))
154
+ @yaml = load_newrelic_yml(newrelic_file, binding)
152
155
  end
153
156
  rescue ScriptError, StandardError => e
154
157
  puts e
@@ -156,6 +159,10 @@ module NewRelic
156
159
  raise "Error reading newrelic.yml file: #{e}"
157
160
  end
158
161
 
162
+ def load_newrelic_yml(path, binding)
163
+ YAML.load(ERB.new(File.read(path)).result(binding))
164
+ end
165
+
159
166
  def root
160
167
  '.'
161
168
  end
@@ -1,15 +1,20 @@
1
1
  require 'fileutils'
2
+ require 'new_relic/language_support'
3
+
2
4
  module NewRelic
3
5
  # Handles serialization of data to disk, to save on contacting the
4
6
  # server. Lowers both server and client overhead, if the disk is not overloaded
5
7
  class DataSerialization
8
+ include NewRelic::LanguageSupport::DataSerialization
9
+
6
10
  module ClassMethods
7
11
  # Check whether the store is too large, too old, or the
8
- # semaphore file is too old. If so, we should send the data
12
+ # pid file is too old. If so, we should send the data
9
13
  # right away. If not, we presumably store it for later sending
10
14
  # (handled elsewhere)
11
15
  def should_send_data?
12
- NewRelic::Control.instance.disable_serialization? || store_too_large? || store_too_old? || semaphore_too_old?
16
+ NewRelic::Control.instance.disable_serialization? || store_too_large? ||
17
+ store_too_old? || pid_too_old?
13
18
  rescue Exception => e
14
19
  NewRelic::Control.instance.disable_serialization = true
15
20
  NewRelic::Control.instance.log.warn("Disabling serialization: #{e.message}")
@@ -32,40 +37,32 @@ module NewRelic
32
37
  # touches the age file that determines whether we should send
33
38
  # data now or not
34
39
  def update_last_sent!
35
- FileUtils.touch(semaphore_path)
36
- rescue Errno::ENOENT => e
37
- NewRelic::Control.instance.log.warn(e.message)
40
+ FileUtils.touch(pid_file_path)
38
41
  end
39
42
 
40
- private
41
-
42
- def store_too_large?
43
- size = File.size(file_path) > max_size
44
- NewRelic::Control.instance.log.debug("Store was oversize, sending data") if size
45
- size
46
- rescue Errno::ENOENT
47
- FileUtils.touch(file_path)
48
- retry
43
+ def pid_too_old?
44
+ create_pid_file unless File.exists?(pid_file_path)
45
+ age = (Time.now.to_i - File.mtime(pid_file_path).to_i)
46
+ NewRelic::Control.instance.log.debug("Pid was #{age} seconds old, sending data") if age > 60
47
+ age > 60
49
48
  end
50
-
49
+
51
50
  def store_too_old?
51
+ FileUtils.touch(file_path) unless File.exists?(file_path)
52
52
  age = (Time.now.to_i - File.mtime(file_path).to_i)
53
53
  NewRelic::Control.instance.log.debug("Store was #{age} seconds old, sending data") if age > 60
54
54
  age > 50
55
- rescue Errno::ENOENT
56
- FileUtils.touch(file_path)
57
- retry
58
- end
59
-
60
- def semaphore_too_old?
61
- age = (Time.now.to_i - File.mtime(semaphore_path).to_i)
62
- NewRelic::Control.instance.log.debug("Pid was #{age} seconds old, sending data") if age > 60
63
- age > 60
64
- rescue Errno::ENOENT
65
- FileUtils.touch(semaphore_path)
66
- retry
55
+ end
56
+
57
+ def store_too_large?
58
+ FileUtils.touch(file_path) unless File.exists?(file_path)
59
+ size = File.size(file_path) > max_size
60
+ NewRelic::Control.instance.log.debug("Store was oversize, sending data") if size
61
+ size
67
62
  end
68
-
63
+
64
+ private
65
+
69
66
  def open_arguments
70
67
  if defined?(Encoding)
71
68
  [file_path, File::RDWR | File::CREAT, {:internal_encoding => nil}]
@@ -122,11 +119,11 @@ module NewRelic
122
119
  end
123
120
 
124
121
  def dump(object)
125
- Marshal.dump(object)
122
+ Marshal.dump(object.clone)
126
123
  end
127
-
124
+
128
125
  def load(dump)
129
- if dump.size == 0
126
+ if dump.respond_to?(:size) && dump.size == 0
130
127
  NewRelic::Control.instance.log.debug("Spool file empty.")
131
128
  return nil
132
129
  end
@@ -141,13 +138,17 @@ module NewRelic
141
138
  FileUtils.touch(file_path)
142
139
  File.truncate(file_path, 0)
143
140
  end
144
-
141
+
142
+ def create_pid_file
143
+ File.open(pid_file_path, 'w') {|f| f.write $$ }
144
+ end
145
+
145
146
  def file_path
146
147
  "#{NewRelic::Control.instance.log_path}/newrelic_agent_store.db"
147
148
  end
148
149
 
149
- def semaphore_path
150
- "#{NewRelic::Control.instance.log_path}/newrelic_agent_store.age"
150
+ def pid_file_path
151
+ "#{NewRelic::Control.instance.log_path}/newrelic_agent_store.pid"
151
152
  end
152
153
  end
153
154
  extend ClassMethods
@@ -0,0 +1,66 @@
1
+ module NewRelic::LanguageSupport
2
+ extend self
3
+
4
+ module DataSerialization
5
+ def self.included(base)
6
+ # need to disable GC during marshal load in 1.8.7
7
+ if ::RUBY_VERSION == '1.8.7' &&
8
+ !NewRelic::LanguageSupport.using_jruby? &&
9
+ !NewRelic::LanguageSupport.using_rubinius?
10
+ base.class_eval do
11
+ def self.load(*args)
12
+ if defined?(::GC) && ::GC.respond_to?(:disable)
13
+ ::GC.disable
14
+ val = super
15
+ ::GC.enable
16
+ val
17
+ else
18
+ super
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ module Control
27
+ def self.included(base)
28
+ # need to use syck rather than psych when possible
29
+ if defined?(::YAML::ENGINE)
30
+ base.class_eval do
31
+ def load_newrelic_yml(*args)
32
+ yamler = ::YAML::ENGINE.yamler
33
+ ::YAML::ENGINE.yamler = 'syck'
34
+ val = super
35
+ ::YAML::ENGINE.yamler = yamler
36
+ val
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ module SynchronizedHash
44
+ def self.included(base)
45
+ # need to lock iteration of stats hash in 1.9.x
46
+ if ::RUBY_VERSION.split('.')[0,2] == ['1','9'] ||
47
+ NewRelic::LanguageSupport.using_jruby?
48
+ base.class_eval do
49
+ def each(*args, &block)
50
+ sync_synchronize(:SH) { super }
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ # Are we in boss mode, using rubinius?
58
+ def using_rubinius?
59
+ defined?(::RUBY_ENGINE) && ::RUBY_ENGINE == 'rbx'
60
+ end
61
+
62
+ # Is this really a world-within-a-world, running JRuby?
63
+ def using_jruby?
64
+ defined?(::RUBY_ENGINE) && ::RUBY_ENGINE == 'jruby'
65
+ end
66
+ end
@@ -268,19 +268,21 @@ module NewRelic
268
268
 
269
269
  target_segment.add_called_segment target_called_segment
270
270
  source_called_segment.params.each do |k,v|
271
- case k
271
+ case k
272
272
  when :backtrace
273
273
  target_called_segment[k]=v if options[:keep_backtraces]
274
274
  when :sql
275
275
  # run an EXPLAIN on this sql if specified.
276
- if options[:record_sql] && options[:record_sql] && options[:explain_sql] && source_called_segment.duration > options[:explain_sql].to_f
277
- target_called_segment[:explanation] = source_called_segment.explain_sql
276
+ if options[:record_sql] && options[:record_sql] &&
277
+ options[:explain_sql] &&
278
+ source_called_segment.duration > options[:explain_sql].to_f
279
+ target_called_segment[:explain_plan] = source_called_segment.explain_sql
278
280
  end
279
-
281
+
280
282
  target_called_segment[:sql] = case options[:record_sql]
281
- when :raw then v
282
- when :obfuscated then TransactionSample.obfuscate_sql(v)
283
- else raise "Invalid value for record_sql: #{options[:record_sql]}"
283
+ when :raw then v
284
+ when :obfuscated then TransactionSample.obfuscate_sql(v)
285
+ else raise "Invalid value for record_sql: #{options[:record_sql]}"
284
286
  end if options[:record_sql]
285
287
  when :connection_config
286
288
  # don't copy it