newrelic_rpm 3.4.2.1 → 3.5.0

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 (49) hide show
  1. data/CHANGELOG +47 -2
  2. data/lib/new_relic/agent.rb +5 -5
  3. data/lib/new_relic/agent/agent.rb +88 -177
  4. data/lib/new_relic/agent/beacon_configuration.rb +33 -47
  5. data/lib/new_relic/agent/browser_monitoring.rb +26 -33
  6. data/lib/new_relic/agent/configuration/defaults.rb +21 -13
  7. data/lib/new_relic/agent/configuration/manager.rb +28 -14
  8. data/lib/new_relic/agent/configuration/server_source.rb +8 -5
  9. data/lib/new_relic/agent/database.rb +37 -22
  10. data/lib/new_relic/agent/error_collector.rb +32 -31
  11. data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +4 -3
  12. data/lib/new_relic/agent/new_relic_service.rb +21 -19
  13. data/lib/new_relic/agent/pipe_channel_manager.rb +13 -13
  14. data/lib/new_relic/agent/sql_sampler.rb +9 -28
  15. data/lib/new_relic/agent/stats_engine/gc_profiler.rb +21 -24
  16. data/lib/new_relic/agent/stats_engine/transactions.rb +20 -12
  17. data/lib/new_relic/agent/transaction_sample_builder.rb +5 -3
  18. data/lib/new_relic/agent/transaction_sampler.rb +43 -47
  19. data/lib/new_relic/control/frameworks/rails.rb +9 -4
  20. data/lib/new_relic/control/frameworks/rails3.rb +10 -0
  21. data/lib/new_relic/noticed_error.rb +18 -8
  22. data/lib/new_relic/rack.rb +4 -0
  23. data/lib/new_relic/rack/browser_monitoring.rb +2 -0
  24. data/lib/new_relic/rack/error_collector.rb +56 -0
  25. data/lib/new_relic/version.rb +3 -3
  26. data/newrelic.yml +0 -12
  27. data/newrelic_rpm.gemspec +6 -3
  28. data/test/new_relic/agent/agent/connect_test.rb +78 -113
  29. data/test/new_relic/agent/agent/start_test.rb +2 -2
  30. data/test/new_relic/agent/agent/start_worker_thread_test.rb +6 -33
  31. data/test/new_relic/agent/agent_test.rb +20 -6
  32. data/test/new_relic/agent/agent_test_controller_test.rb +7 -5
  33. data/test/new_relic/agent/beacon_configuration_test.rb +54 -60
  34. data/test/new_relic/agent/browser_monitoring_test.rb +88 -74
  35. data/test/new_relic/agent/configuration/manager_test.rb +21 -21
  36. data/test/new_relic/agent/configuration/server_source_test.rb +21 -4
  37. data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -2
  38. data/test/new_relic/agent/mock_scope_listener.rb +3 -0
  39. data/test/new_relic/agent/new_relic_service_test.rb +56 -17
  40. data/test/new_relic/agent/pipe_channel_manager_test.rb +4 -1
  41. data/test/new_relic/agent/rpm_agent_test.rb +1 -0
  42. data/test/new_relic/agent/stats_engine_test.rb +12 -7
  43. data/test/new_relic/agent/transaction_sampler_test.rb +106 -102
  44. data/test/new_relic/agent_test.rb +10 -9
  45. data/test/new_relic/control_test.rb +1 -17
  46. data/test/new_relic/rack/browser_monitoring_test.rb +11 -5
  47. data/test/new_relic/rack/error_collector_test.rb +74 -0
  48. data/test/test_helper.rb +1 -1
  49. metadata +9 -7
@@ -12,7 +12,7 @@ module NewRelic
12
12
  def self.capture
13
13
  @profiler.capture if @profiler
14
14
  end
15
-
15
+
16
16
  class Profiler
17
17
  def initialize
18
18
  if self.class.enabled?
@@ -20,55 +20,52 @@ module NewRelic
20
20
  @last_count = call_count
21
21
  end
22
22
  end
23
-
23
+
24
24
  def capture
25
25
  return unless self.class.enabled?
26
26
  return if !scope_stack.empty? && scope_stack.last.name == "GC/cumulative"
27
-
27
+
28
28
  num_calls = call_count - @last_count
29
- elapsed = (call_time - @last_timestamp).to_f
29
+ # microseconds to seconds
30
+ elapsed = (call_time - @last_timestamp).to_f / 1_000_000.0
30
31
  @last_timestamp = call_time
31
32
  @last_count = call_count
32
33
  reset
33
-
34
+
34
35
  record_gc_metric(num_calls, elapsed)
36
+ elapsed
35
37
  end
36
38
 
37
39
  def reset; end
38
-
40
+
39
41
  protected
40
-
42
+
41
43
  def record_gc_metric(num_calls, elapsed)
42
44
  if num_calls > 0
43
- # microseconds to seconds
44
- elapsed = elapsed / 1_000_000.0
45
- # Allocate the GC time to a scope as if the GC just ended
46
- # right now.
47
- time = Time.now.to_f
48
- gc_scope = NewRelic::Agent.instance.stats_engine.push_scope("GC/cumulative", time - elapsed)
49
45
  # GC stats are collected into a blamed metric which allows
50
46
  # us to show the stats controller by controller
51
- gc_stats = NewRelic::Agent.get_stats(gc_scope.name, true)
47
+ gc_stats = NewRelic::Agent.instance.stats_engine \
48
+ .get_stats('GC/cumulative', true, false,
49
+ NewRelic::Agent::TransactionInfo.get.transaction_name)
52
50
  gc_stats.record_multiple_data_points(elapsed, num_calls)
53
- NewRelic::Agent.instance.stats_engine.pop_scope(gc_scope, elapsed, time)
54
51
  end
55
52
  end
56
53
 
57
54
  def scope_stack
58
55
  Thread::current[:newrelic_scope_stack] ||= []
59
- end
56
+ end
60
57
  end
61
-
58
+
62
59
  class RailsBench < Profiler
63
60
  def self.enabled?
64
61
  ::GC.respond_to?(:time) && ::GC.respond_to?(:collections)
65
62
  end
66
-
63
+
67
64
  # microseconds spent in GC
68
65
  def call_time
69
66
  ::GC.time # this should already be microseconds
70
67
  end
71
-
68
+
72
69
  def call_count
73
70
  ::GC.collections
74
71
  end
@@ -77,22 +74,22 @@ module NewRelic
77
74
  ::GC.clear_stats
78
75
  end
79
76
  end
80
-
77
+
81
78
  class Ruby19 < Profiler
82
79
  def self.enabled?
83
80
  defined?(::GC::Profiler) && ::GC::Profiler.enabled?
84
81
  end
85
-
82
+
86
83
  # microseconds spent in GC
87
84
  # 1.9 total_time returns seconds. Don't trust the docs. It's seconds.
88
85
  def call_time
89
86
  ::GC::Profiler.total_time * 1_000_000.0 # convert seconds to microseconds
90
87
  end
91
-
88
+
92
89
  def call_count
93
90
  ::GC.count
94
91
  end
95
-
92
+
96
93
  def reset
97
94
  ::GC::Profiler.clear
98
95
  @last_timestamp = 0
@@ -119,7 +116,7 @@ module NewRelic
119
116
  agent = ::Rubinius::Agent.loopback
120
117
  agent.get('system.gc.young.count')[1] +
121
118
  agent.get('system.gc.full.count')[1]
122
- end
119
+ end
123
120
  end
124
121
  end
125
122
  end
@@ -13,11 +13,11 @@ module Agent
13
13
  @children_time = 0
14
14
  end
15
15
  end
16
-
16
+
17
17
  # Handles pushing and popping elements onto an internal stack that
18
18
  # tracks where time should be allocated in Transaction Traces
19
19
  module Transactions
20
-
20
+
21
21
  # Defines methods that stub out the stats engine methods
22
22
  # when the agent is disabled
23
23
  module Shim # :nodoc:
@@ -29,34 +29,32 @@ module Agent
29
29
  def scope_name; end
30
30
  def pop_scope(*args); end
31
31
  end
32
-
32
+
33
33
  # add a new transaction sampler, unless we're currently in a
34
34
  # transaction (then we fail)
35
35
  def transaction_sampler= sampler
36
36
  fail "Can't add a scope listener midflight in a transaction" if scope_stack.any?
37
37
  @transaction_sampler = sampler
38
38
  end
39
-
39
+
40
40
  # removes a transaction sampler
41
41
  def remove_transaction_sampler(l)
42
42
  @transaction_sampler = nil
43
43
  end
44
-
44
+
45
45
  # Pushes a scope onto the transaction stack - this generates a
46
46
  # TransactionSample::Segment at the end of transaction execution
47
47
  def push_scope(metric, time = Time.now.to_f, deduct_call_time_from_parent = true)
48
48
  stack = scope_stack
49
- stack.empty? ? GCProfiler.init : GCProfiler.capture
50
- @transaction_sampler.notice_push_scope metric, time if @transaction_sampler
49
+ @transaction_sampler.notice_push_scope metric, time if sampler_enabled?
51
50
  scope = ScopeStackElement.new(metric, deduct_call_time_from_parent)
52
51
  stack.push scope
53
52
  scope
54
53
  end
55
-
54
+
56
55
  # Pops a scope off the transaction stack - this updates the
57
56
  # transaction sampler that we've finished execution of a traced method
58
57
  def pop_scope(expected_scope, duration, time=Time.now.to_f)
59
- GCProfiler.capture
60
58
  stack = scope_stack
61
59
  scope = stack.pop
62
60
  fail "unbalanced pop from blame stack, got #{scope ? scope.name : 'nil'}, expected #{expected_scope ? expected_scope.name : 'nil'}" if scope != expected_scope
@@ -68,10 +66,14 @@ module Agent
68
66
  stack.last.children_time += scope.children_time
69
67
  end
70
68
  end
71
- @transaction_sampler.notice_pop_scope(scope.name, time) if @transaction_sampler
69
+ @transaction_sampler.notice_pop_scope(scope.name, time) if sampler_enabled?
72
70
  scope
73
71
  end
74
72
 
73
+ def sampler_enabled?
74
+ @transaction_sampler && Agent.config[:'transaction_tracer.enabled']
75
+ end
76
+
75
77
  # Returns the latest ScopeStackElement
76
78
  def peek_scope
77
79
  scope_stack.last
@@ -88,7 +90,7 @@ module Agent
88
90
  def scope_name=(transaction)
89
91
  Thread::current[:newrelic_scope_name] = transaction
90
92
  end
91
-
93
+
92
94
  # Returns the current scope name from the thread local
93
95
  def scope_name
94
96
  Thread::current[:newrelic_scope_name]
@@ -98,6 +100,7 @@ module Agent
98
100
  def start_transaction(name = nil)
99
101
  Thread::current[:newrelic_scope_stack] ||= []
100
102
  self.scope_name = name if name
103
+ GCProfiler.init
101
104
  end
102
105
 
103
106
  # Try to clean up gracefully, otherwise we leave things hanging around on thread locals.
@@ -105,6 +108,11 @@ module Agent
105
108
  # and is ignored.
106
109
  #
107
110
  def end_transaction
111
+ elapsed = GCProfiler.capture
112
+ if @transaction_sampler && @transaction_sampler.last_sample
113
+ @transaction_sampler.last_sample.params[:custom_params] ||= {}
114
+ @transaction_sampler.last_sample.params[:custom_params][:gc_time] = elapsed
115
+ end
108
116
  stack = scope_stack
109
117
 
110
118
  if stack && stack.empty?
@@ -114,7 +122,7 @@ module Agent
114
122
  end
115
123
 
116
124
  private
117
-
125
+
118
126
  # Returns the current scope stack, memoized to a thread local variable
119
127
  def scope_stack
120
128
  Thread::current[:newrelic_scope_stack] ||= []
@@ -61,8 +61,9 @@ module NewRelic
61
61
  return
62
62
  end
63
63
  @sample.root_segment.end_trace(time.to_f - @sample_start)
64
- @sample.params[:custom_params] = normalize_params(NewRelic::Agent::Instrumentation::MetricFrame.custom_parameters)
65
-
64
+ @sample.params[:custom_params] ||= {}
65
+ @sample.params[:custom_params].merge!(normalize_params(NewRelic::Agent::Instrumentation::MetricFrame.custom_parameters))
66
+
66
67
  @sample.force_persist = NewRelic::Agent::TransactionInfo.get.force_persist_sample?(sample)
67
68
  @sample.freeze
68
69
  @current_segment = nil
@@ -102,7 +103,8 @@ module NewRelic
102
103
  end
103
104
 
104
105
  def set_transaction_cpu_time(cpu_time)
105
- @sample.params[:cpu_time] = cpu_time
106
+ @sample.params[:custom_params] ||= {}
107
+ @sample.params[:custom_params][:cpu_time] = cpu_time
106
108
  end
107
109
 
108
110
  def sample
@@ -20,7 +20,6 @@ module NewRelic
20
20
  BUILDER_KEY = :transaction_sample_builder
21
21
 
22
22
  attr_accessor :random_sampling, :sampling_rate
23
- attr_accessor :explain_threshold, :explain_enabled, :transaction_threshold
24
23
  attr_accessor :slow_capture_threshold
25
24
  attr_reader :samples, :last_sample, :disabled
26
25
 
@@ -36,53 +35,41 @@ module NewRelic
36
35
  # sampling - we pull 1 @random_sample in every @sampling_rate harvests
37
36
  @harvest_count = 0
38
37
  @random_sample = nil
39
- @sampling_rate = 10
40
- @slow_capture_threshold = 2.0
41
- configure!
38
+ @sampling_rate = Agent.config[:sample_rate]
42
39
 
43
40
  # This lock is used to synchronize access to the @last_sample
44
41
  # and related variables. It can become necessary on JRuby or
45
42
  # any 'honest-to-god'-multithreaded system
46
43
  @samples_lock = Mutex.new
47
- end
48
44
 
49
- def configure!
50
- # @segment_limit and @stack_trace_threshold come from the
51
- # configuration file, with built-in defaults that should
52
- # suffice for most customers
53
- @explain_threshold = Agent.config[:'transaction_tracer.explain_threshold']
54
- @explain_enabled = Agent.config[:'transaction_tracer.explain_enabled']
55
- @transaction_threshold = Agent.config[:'transaction_tracer.transation_threshold']
45
+ Agent.config.register_callback(:'transaction_tracer.enabled') do |enabled|
46
+ if enabled
47
+ threshold = Agent.config[:'transaction_tracer.transaction_threshold']
48
+ log.debug "Transaction tracing threshold is #{threshold} seconds."
49
+ else
50
+ log.debug "Transaction traces will not be sent to the New Relic service."
51
+ end
52
+ end
56
53
 
57
- # Configure the sample storage policy. Create a list of methods to be called.
58
- @store_sampler_methods = [ :store_random_sample, :store_slowest_sample ]
59
- if Agent.config[:developer_mode]
60
- @store_sampler_methods << :store_sample_for_developer_mode
54
+ Agent.config.register_callback(:'transaction_tracer.record_sql') do |config|
55
+ if config == 'raw'
56
+ log.warn("Agent is configured to send raw SQL to the service")
57
+ end
61
58
  end
62
59
  end
63
60
 
61
+ def log
62
+ NewRelic::Control.instance.log
63
+ end
64
+
64
65
  # Returns the current sample id, delegated from `builder`
65
66
  def current_sample_id
66
67
  b=builder
67
68
  b and b.sample_id
68
69
  end
69
70
 
70
- # Enable the transaction sampler - this also registers it with
71
- # the statistics engine.
72
- def enable
73
- @disabled = false
74
- NewRelic::Agent.instance.stats_engine.transaction_sampler = self
75
- end
76
-
77
- # Disable the transaction sampler - this also deregisters it
78
- # with the statistics engine.
79
- def disable
80
- @disabled = true
81
- NewRelic::Agent.instance.stats_engine.remove_transaction_sampler(self)
82
- end
83
-
84
71
  def enabled?
85
- !@disabled
72
+ Agent.config[:'transaction_tracer.enabled'] || Agent.config[:developer_mode]
86
73
  end
87
74
 
88
75
  # Set with an integer value n, this takes one in every n
@@ -98,7 +85,7 @@ module NewRelic
98
85
  # transaction sampler is disabled. Takes a time parameter for
99
86
  # the start of the transaction sample
100
87
  def notice_first_scope_push(time)
101
- start_builder(time.to_f) unless disabled
88
+ start_builder(time.to_f) if enabled?
102
89
  end
103
90
 
104
91
  # This delegates to the builder to create a new open transaction
@@ -177,20 +164,26 @@ module NewRelic
177
164
  # @samples array, and the @slowest_sample variable if it is
178
165
  # slower than the current occupant of that slot
179
166
  def store_sample(sample)
180
- @store_sampler_methods.each{|sym| send sym, sample}
167
+ sampler_methods = [ :store_slowest_sample ]
168
+ if Agent.config[:developer_mode]
169
+ sampler_methods << :store_sample_for_developer_mode
170
+ end
171
+ if Agent.config[:'transaction_tracer.random_sample']
172
+ sampler_methods << :store_random_sample
173
+ end
174
+
175
+ sampler_methods.each{|sym| send(sym, sample) }
176
+
181
177
  if NewRelic::Agent::TransactionInfo.get.force_persist_sample?(sample)
182
178
  store_force_persist(sample)
183
179
  end
184
-
185
180
  end
186
181
 
187
182
  # Only active when random sampling is true - this is very rarely
188
183
  # used. Always store the most recent sample so that random
189
184
  # sampling can pick a few of the samples to store, upon harvest
190
185
  def store_random_sample(sample)
191
- if @random_sampling
192
- @random_sample = sample
193
- end
186
+ @random_sample = sample if Agent.config[:'transaction_tracer.random_sample']
194
187
  end
195
188
 
196
189
  def store_force_persist(sample)
@@ -237,7 +230,7 @@ module NewRelic
237
230
  # Delegates to the builder to store the path, uri, and
238
231
  # parameters if the sampler is active
239
232
  def notice_transaction(path, uri=nil, params={})
240
- builder.set_transaction_info(path, uri, params) if !disabled && builder
233
+ builder.set_transaction_info(path, uri, params) if enabled? && builder
241
234
  end
242
235
 
243
236
  # Tells the builder to ignore a transaction, if we are currently
@@ -337,9 +330,10 @@ module NewRelic
337
330
  #
338
331
  # random sampling is very, very seldom used
339
332
  def add_random_sample_to(result)
340
- return unless @random_sampling && @sampling_rate && @sampling_rate.to_i > 0
333
+ return unless @random_sample &&
334
+ Agent.config[:sample_rate] && Agent.config[:sample_rate].to_i > 0
341
335
  @harvest_count += 1
342
- if (@harvest_count.to_i % @sampling_rate.to_i) == 0
336
+ if (@harvest_count.to_i % Agent.config[:sample_rate].to_i) == 0
343
337
  result << @random_sample if @random_sample
344
338
  @harvest_count = 0
345
339
  end
@@ -355,7 +349,7 @@ module NewRelic
355
349
  # elements - one element unless random sampling is enabled. The
356
350
  # sample returned will be the slowest sample among those
357
351
  # available during this harvest
358
- def add_samples_to(result, slow_threshold)
352
+ def add_samples_to(result)
359
353
  # pull out force persist
360
354
  force_persist = result.select {|sample| sample.force_persist} || []
361
355
  result.reject! {|sample| sample.force_persist}
@@ -364,7 +358,9 @@ module NewRelic
364
358
 
365
359
 
366
360
  # Now get the slowest sample
367
- if @slowest_sample && @slowest_sample.duration >= slow_threshold
361
+ if @slowest_sample &&
362
+ @slowest_sample.duration >=
363
+ Agent.config[:'transaction_tracer.transaction_threshold']
368
364
  result << @slowest_sample
369
365
  end
370
366
 
@@ -380,14 +376,14 @@ module NewRelic
380
376
 
381
377
  # get the set of collected samples, merging into previous samples,
382
378
  # and clear the collected sample list. Truncates samples to a
383
- # specified @segment_limit to save memory and bandwith
379
+ # specified segment_limit to save memory and bandwith
384
380
  # transmitting samples to the server.
385
- def harvest(previous = [], slow_threshold = 2.0)
386
- return [] if disabled
381
+ def harvest(previous=[])
382
+ return [] if !enabled?
387
383
  result = Array(previous)
388
384
 
389
385
  @samples_lock.synchronize do
390
- result = add_samples_to(result, slow_threshold)
386
+ result = add_samples_to(result)
391
387
 
392
388
  # clear previous transaction samples
393
389
  @slowest_sample = nil
@@ -437,7 +433,7 @@ module NewRelic
437
433
  # new transaction sample builder with the stated time as a
438
434
  # starting point and saves it in the thread local variable
439
435
  def start_builder(time=nil)
440
- if disabled || !NewRelic::Agent.is_transaction_traced? || !NewRelic::Agent.is_execution_traced?
436
+ if !enabled? || !NewRelic::Agent.is_transaction_traced? || !NewRelic::Agent.is_execution_traced?
441
437
  clear_builder
442
438
  else
443
439
  Thread::current[BUILDER_KEY] ||= TransactionSampleBuilder.new(time)
@@ -21,14 +21,19 @@ module NewRelic
21
21
  ::RAILS_DEFAULT_LOGGER
22
22
  end
23
23
 
24
+ def rails_config
25
+ if defined?(::Rails) && ::Rails.respond_to?(:configuration)
26
+ ::Rails.configuration
27
+ else
28
+ @config
29
+ end
30
+ end
31
+
24
32
  # In versions of Rails prior to 2.0, the rails config was only available to
25
33
  # the init.rb, so it had to be passed on from there. This is a best effort to
26
34
  # find a config and use that.
27
35
  def init_config(options={})
28
- rails_config = options[:config]
29
- if !rails_config && defined?(::Rails) && ::Rails.respond_to?(:configuration)
30
- rails_config = ::Rails.configuration
31
- end
36
+ @config = options[:config]
32
37
  # Install the dependency detection,
33
38
  if rails_config && ::Rails.configuration.respond_to?(:after_initialize)
34
39
  rails_config.after_initialize do