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.
- data/CHANGELOG +47 -2
- data/lib/new_relic/agent.rb +5 -5
- data/lib/new_relic/agent/agent.rb +88 -177
- data/lib/new_relic/agent/beacon_configuration.rb +33 -47
- data/lib/new_relic/agent/browser_monitoring.rb +26 -33
- data/lib/new_relic/agent/configuration/defaults.rb +21 -13
- data/lib/new_relic/agent/configuration/manager.rb +28 -14
- data/lib/new_relic/agent/configuration/server_source.rb +8 -5
- data/lib/new_relic/agent/database.rb +37 -22
- data/lib/new_relic/agent/error_collector.rb +32 -31
- data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +4 -3
- data/lib/new_relic/agent/new_relic_service.rb +21 -19
- data/lib/new_relic/agent/pipe_channel_manager.rb +13 -13
- data/lib/new_relic/agent/sql_sampler.rb +9 -28
- data/lib/new_relic/agent/stats_engine/gc_profiler.rb +21 -24
- data/lib/new_relic/agent/stats_engine/transactions.rb +20 -12
- data/lib/new_relic/agent/transaction_sample_builder.rb +5 -3
- data/lib/new_relic/agent/transaction_sampler.rb +43 -47
- data/lib/new_relic/control/frameworks/rails.rb +9 -4
- data/lib/new_relic/control/frameworks/rails3.rb +10 -0
- data/lib/new_relic/noticed_error.rb +18 -8
- data/lib/new_relic/rack.rb +4 -0
- data/lib/new_relic/rack/browser_monitoring.rb +2 -0
- data/lib/new_relic/rack/error_collector.rb +56 -0
- data/lib/new_relic/version.rb +3 -3
- data/newrelic.yml +0 -12
- data/newrelic_rpm.gemspec +6 -3
- data/test/new_relic/agent/agent/connect_test.rb +78 -113
- data/test/new_relic/agent/agent/start_test.rb +2 -2
- data/test/new_relic/agent/agent/start_worker_thread_test.rb +6 -33
- data/test/new_relic/agent/agent_test.rb +20 -6
- data/test/new_relic/agent/agent_test_controller_test.rb +7 -5
- data/test/new_relic/agent/beacon_configuration_test.rb +54 -60
- data/test/new_relic/agent/browser_monitoring_test.rb +88 -74
- data/test/new_relic/agent/configuration/manager_test.rb +21 -21
- data/test/new_relic/agent/configuration/server_source_test.rb +21 -4
- data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +2 -2
- data/test/new_relic/agent/mock_scope_listener.rb +3 -0
- data/test/new_relic/agent/new_relic_service_test.rb +56 -17
- data/test/new_relic/agent/pipe_channel_manager_test.rb +4 -1
- data/test/new_relic/agent/rpm_agent_test.rb +1 -0
- data/test/new_relic/agent/stats_engine_test.rb +12 -7
- data/test/new_relic/agent/transaction_sampler_test.rb +106 -102
- data/test/new_relic/agent_test.rb +10 -9
- data/test/new_relic/control_test.rb +1 -17
- data/test/new_relic/rack/browser_monitoring_test.rb +11 -5
- data/test/new_relic/rack/error_collector_test.rb +74 -0
- data/test/test_helper.rb +1 -1
- 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
|
-
|
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.
|
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
|
-
|
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
|
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]
|
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[:
|
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 =
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
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)
|
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
|
-
|
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
|
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
|
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 @
|
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 %
|
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
|
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 &&
|
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
|
379
|
+
# specified segment_limit to save memory and bandwith
|
384
380
|
# transmitting samples to the server.
|
385
|
-
def harvest(previous
|
386
|
-
return [] if
|
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
|
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
|
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
|
-
|
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
|