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.
- data/CHANGELOG +117 -49
- data/bin/mongrel_rpm +2 -2
- data/install.rb +42 -33
- data/lib/new_relic/agent.rb +149 -39
- data/lib/new_relic/agent/agent.rb +139 -122
- data/lib/new_relic/agent/busy_calculator.rb +91 -0
- data/lib/new_relic/agent/collection_helper.rb +11 -2
- data/lib/new_relic/agent/error_collector.rb +33 -27
- data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +30 -26
- data/lib/new_relic/agent/instrumentation/authlogic.rb +8 -0
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +316 -105
- data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +22 -0
- data/lib/new_relic/agent/instrumentation/memcache.rb +18 -12
- data/lib/new_relic/agent/instrumentation/merb/errors.rb +2 -1
- data/lib/new_relic/agent/instrumentation/metric_frame.rb +258 -0
- data/lib/new_relic/agent/instrumentation/net.rb +7 -11
- data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +1 -1
- data/lib/new_relic/agent/instrumentation/rack.rb +109 -0
- data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +6 -5
- data/lib/new_relic/agent/instrumentation/rails/errors.rb +7 -7
- data/lib/new_relic/agent/instrumentation/sinatra.rb +46 -0
- data/lib/new_relic/agent/method_tracer.rb +305 -150
- data/lib/new_relic/agent/sampler.rb +34 -0
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +11 -1
- data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +37 -0
- data/lib/new_relic/agent/samplers/memory_sampler.rb +22 -10
- data/lib/new_relic/agent/samplers/object_sampler.rb +24 -0
- data/lib/new_relic/agent/shim_agent.rb +10 -0
- data/lib/new_relic/agent/stats_engine.rb +16 -278
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +118 -0
- data/lib/new_relic/agent/stats_engine/samplers.rb +81 -0
- data/lib/new_relic/agent/stats_engine/transactions.rb +149 -0
- data/lib/new_relic/agent/transaction_sampler.rb +73 -67
- data/lib/new_relic/agent/worker_loop.rb +69 -68
- data/lib/new_relic/commands/deployments.rb +4 -6
- data/lib/new_relic/control.rb +121 -60
- data/lib/new_relic/control/external.rb +13 -0
- data/lib/new_relic/control/merb.rb +2 -0
- data/lib/new_relic/control/rails.rb +16 -6
- data/lib/new_relic/control/ruby.rb +8 -5
- data/lib/new_relic/control/sinatra.rb +18 -0
- data/lib/new_relic/delayed_job_injection.rb +25 -0
- data/lib/new_relic/histogram.rb +89 -0
- data/lib/new_relic/local_environment.rb +64 -30
- data/lib/new_relic/metric_data.rb +15 -6
- data/lib/new_relic/metric_parser.rb +14 -1
- data/lib/new_relic/metric_parser/active_record.rb +14 -0
- data/lib/new_relic/metric_parser/controller.rb +5 -2
- data/lib/new_relic/metric_parser/external.rb +50 -0
- data/lib/new_relic/metric_parser/other_transaction.rb +15 -0
- data/lib/new_relic/metric_parser/web_frontend.rb +14 -0
- data/lib/new_relic/metric_spec.rb +39 -20
- data/lib/new_relic/metrics.rb +9 -7
- data/lib/new_relic/noticed_error.rb +6 -8
- data/lib/new_relic/rack/metric_app.rb +5 -4
- data/lib/new_relic/rack/{newrelic.ru → mongrel_rpm.ru} +4 -4
- data/lib/new_relic/rack/newrelic.yml +1 -0
- data/lib/new_relic/{rack.rb → rack_app.rb} +0 -0
- data/lib/new_relic/recipes.rb +1 -1
- data/lib/new_relic/stats.rb +40 -26
- data/lib/new_relic/transaction_analysis.rb +5 -2
- data/lib/new_relic/transaction_sample.rb +134 -55
- data/lib/new_relic/version.rb +27 -20
- data/lib/new_relic_api.rb +67 -47
- data/lib/newrelic_rpm.rb +5 -5
- data/lib/tasks/tests.rake +2 -0
- data/newrelic.yml +69 -29
- data/test/active_record_fixtures.rb +2 -2
- data/test/config/newrelic.yml +4 -7
- data/test/config/test_control.rb +1 -2
- data/test/new_relic/agent/active_record_instrumentation_test.rb +115 -31
- data/test/new_relic/agent/agent_controller_test.rb +274 -0
- data/test/new_relic/agent/agent_test_controller.rb +42 -6
- data/test/new_relic/agent/busy_calculator_test.rb +79 -0
- data/test/new_relic/agent/collection_helper_test.rb +10 -3
- data/test/new_relic/agent/error_collector_test.rb +35 -17
- data/test/new_relic/agent/method_tracer_test.rb +60 -20
- data/test/new_relic/agent/metric_data_test.rb +2 -2
- data/test/new_relic/agent/metric_frame_test.rb +51 -0
- data/test/new_relic/agent/net_instrumentation_test.rb +77 -0
- data/test/new_relic/agent/{agent_test.rb → rpm_agent_test.rb} +26 -5
- data/test/new_relic/agent/stats_engine/metric_stats_test.rb +79 -0
- data/test/new_relic/{samplers_test.rb → agent/stats_engine/samplers_test.rb} +23 -22
- data/test/new_relic/agent/{stats_engine_test.rb → stats_engine/stats_engine_test.rb} +19 -101
- data/test/new_relic/agent/task_instrumentation_test.rb +176 -0
- data/test/new_relic/agent/transaction_sample_builder_test.rb +2 -2
- data/test/new_relic/agent/transaction_sample_test.rb +53 -38
- data/test/new_relic/agent/transaction_sampler_test.rb +101 -33
- data/test/new_relic/agent/worker_loop_test.rb +16 -14
- data/test/new_relic/control_test.rb +26 -13
- data/test/new_relic/metric_parser_test.rb +31 -1
- data/test/new_relic/metric_spec_test.rb +2 -2
- data/test/new_relic/stats_test.rb +0 -8
- data/test/new_relic/version_number_test.rb +31 -1
- data/test/test_helper.rb +37 -1
- data/ui/controllers/newrelic_controller.rb +19 -11
- data/ui/helpers/google_pie_chart.rb +5 -11
- data/ui/helpers/newrelic_helper.rb +40 -35
- data/ui/views/layouts/newrelic_default.rhtml +7 -7
- data/ui/views/newrelic/_sample.rhtml +5 -1
- data/ui/views/newrelic/images/new-relic-rpm-desktop.gif +0 -0
- data/ui/views/newrelic/images/textmate.png +0 -0
- data/ui/views/newrelic/index.rhtml +13 -1
- data/ui/views/newrelic/show_sample.rhtml +5 -2
- data/ui/views/newrelic/stylesheets/style.css +54 -3
- metadata +65 -145
- data/Manifest +0 -143
- data/Rakefile +0 -22
- data/init.rb +0 -38
- data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +0 -127
- data/lib/new_relic/agent/instrumentation/error_instrumentation.rb +0 -14
- data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +0 -13
- data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +0 -38
- data/lib/new_relic/agent/patch_const_missing.rb +0 -125
- data/lib/new_relic/agent/samplers/mongrel_sampler.rb +0 -22
- data/lib/new_relic/metric_parser/database.rb +0 -23
- data/newrelic_rpm.gemspec +0 -35
- data/test/new_relic/agent/classloader_patch_test.rb +0 -56
- data/test/new_relic/agent/controller_test.rb +0 -107
- data/test/new_relic/agent/dispatcher_instrumentation_test.rb +0 -70
@@ -1,6 +1,39 @@
|
|
1
|
+
# A Sampler is used to capture meaningful metrics in a background thread
|
2
|
+
# periodically. They will either be invoked once a minute just before the
|
3
|
+
# data is sent to the agent (default) or every 10 seconds, when #use_harvest_sampler?
|
4
|
+
# returns false.
|
5
|
+
#
|
6
|
+
# Samplers can be added to New Relic by subclassing NewRelic::Agent::Sampler.
|
7
|
+
# Instances are created when the agent is enabled and installed. Subclasses
|
8
|
+
# are registered for instantiation automatically.
|
1
9
|
class NewRelic::Agent::Sampler
|
10
|
+
|
11
|
+
# Exception denotes a sampler is not available and it will not be registered.
|
12
|
+
class Unsupported < StandardError; end
|
13
|
+
|
2
14
|
attr_accessor :stats_engine
|
3
15
|
attr_reader :id
|
16
|
+
@sampler_classes = []
|
17
|
+
|
18
|
+
def self.inherited(subclass)
|
19
|
+
@sampler_classes << subclass
|
20
|
+
end
|
21
|
+
|
22
|
+
# Override with check. Called before instantiating.
|
23
|
+
def self.supported_on_this_platform?
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
# Override to use the periodic sampler instead of running the sampler on the
|
28
|
+
# minute during harvests.
|
29
|
+
def self.use_harvest_sampler?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.sampler_classes
|
34
|
+
@sampler_classes
|
35
|
+
end
|
36
|
+
|
4
37
|
def initialize(id)
|
5
38
|
@id = id
|
6
39
|
end
|
@@ -9,4 +42,5 @@ class NewRelic::Agent::Sampler
|
|
9
42
|
raise "Implement in the subclass"
|
10
43
|
end
|
11
44
|
|
45
|
+
|
12
46
|
end
|
@@ -1,10 +1,13 @@
|
|
1
|
-
module NewRelic
|
1
|
+
module NewRelic
|
2
|
+
module Agent
|
3
|
+
module Samplers
|
2
4
|
class CpuSampler < NewRelic::Agent::Sampler
|
3
5
|
attr_reader :last_time
|
4
6
|
def initialize
|
5
7
|
super :cpu
|
6
8
|
poll
|
7
9
|
end
|
10
|
+
|
8
11
|
def user_util_stats
|
9
12
|
stats_engine.get_stats_no_scope("CPU/User/Utilization")
|
10
13
|
end
|
@@ -17,6 +20,11 @@ module NewRelic::Agent::Samplers
|
|
17
20
|
def systemtime_stats
|
18
21
|
stats_engine.get_stats_no_scope("CPU/System Time")
|
19
22
|
end
|
23
|
+
|
24
|
+
def self.supported_on_this_platform?
|
25
|
+
not defined?(JRuby)
|
26
|
+
end
|
27
|
+
|
20
28
|
def poll
|
21
29
|
now = Time.now
|
22
30
|
t = Process.times
|
@@ -41,4 +49,6 @@ module NewRelic::Agent::Samplers
|
|
41
49
|
end
|
42
50
|
end
|
43
51
|
end
|
52
|
+
end
|
53
|
+
end
|
44
54
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module NewRelic
|
2
|
+
module Agent
|
3
|
+
module Samplers
|
4
|
+
class DelayedJobLockSampler < NewRelic::Agent::Sampler
|
5
|
+
def initialize
|
6
|
+
super :delayed_job_lock
|
7
|
+
raise Unsupported, "DJ instrumentation disabled" if NewRelic::Control.instance['disable_dj']
|
8
|
+
raise Unsupported, "No DJ worker present" if NewRelic::DelayedJobInjection.worker_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def stats
|
12
|
+
stats_engine.get_stats("Custom/DJ Locked Jobs", false)
|
13
|
+
end
|
14
|
+
|
15
|
+
def local_env
|
16
|
+
NewRelic::Control.instance.local_env
|
17
|
+
end
|
18
|
+
|
19
|
+
def worker_name
|
20
|
+
local_env.dispatcher_instance_id
|
21
|
+
end
|
22
|
+
|
23
|
+
def locked_jobs
|
24
|
+
Delayed::Job.count(:conditions => {:locked_by => NewRelic::DelayedJobInjection.worker_name})
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.supported_on_this_platform?
|
28
|
+
defined?(Delayed::Job)
|
29
|
+
end
|
30
|
+
|
31
|
+
def poll
|
32
|
+
stats.record_data_point locked_jobs
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,21 +1,22 @@
|
|
1
|
-
module NewRelic
|
1
|
+
module NewRelic
|
2
|
+
module Agent
|
3
|
+
module Samplers
|
2
4
|
|
3
5
|
class MemorySampler < NewRelic::Agent::Sampler
|
4
6
|
attr_accessor :sampler
|
5
7
|
|
6
8
|
def initialize
|
7
9
|
super :memory
|
8
|
-
|
9
10
|
# macos, linux, solaris
|
10
|
-
if defined?
|
11
|
+
if defined? JRuby
|
11
12
|
@sampler = JavaHeapSampler.new
|
12
13
|
elsif platform =~ /linux/
|
13
14
|
@sampler = ProcStatus.new
|
14
15
|
if !@sampler.can_run?
|
15
|
-
NewRelic::Agent.instance.
|
16
|
+
NewRelic::Agent.instance.warn.debug "Error attempting to use /proc/#{$$}/status file for reading memory. Using ps command instead."
|
16
17
|
@sampler = ShellPS.new("ps -o rsz")
|
17
18
|
else
|
18
|
-
NewRelic::Agent.instance.log.
|
19
|
+
NewRelic::Agent.instance.log.debug "Using /proc/#{$$}/status for reading process memory."
|
19
20
|
end
|
20
21
|
elsif platform =~ /darwin9/ # 10.5
|
21
22
|
@sampler = ShellPS.new("ps -o rsz")
|
@@ -27,17 +28,25 @@ module NewRelic::Agent::Samplers
|
|
27
28
|
@sampler = ShellPS.new("/usr/bin/ps -o rss -p")
|
28
29
|
end
|
29
30
|
|
30
|
-
raise "Unsupported platform for getting memory: #{platform}" if @sampler.nil?
|
31
|
-
raise "Unable to run #{@sampler}" unless @sampler.can_run?
|
31
|
+
raise Unsupported, "Unsupported platform for getting memory: #{platform}" if @sampler.nil?
|
32
|
+
raise Unsupported, "Unable to run #{@sampler}" unless @sampler.can_run?
|
32
33
|
end
|
33
|
-
|
34
|
+
|
35
|
+
def self.supported_on_this_platform?
|
36
|
+
defined?(JRuby) or platform =~ /linux|darwin9|darwin10|freebsd|solaris/
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.platform
|
34
40
|
if RUBY_PLATFORM =~ /java/
|
35
41
|
%x[uname -s].downcase
|
36
42
|
else
|
37
43
|
RUBY_PLATFORM.downcase
|
38
44
|
end
|
39
45
|
end
|
40
|
-
|
46
|
+
def platform
|
47
|
+
NewRelic::Agent::Samplers::MemorySampler.platform
|
48
|
+
end
|
49
|
+
|
41
50
|
def stats
|
42
51
|
stats_engine.get_stats("Memory/Physical", false)
|
43
52
|
end
|
@@ -66,6 +75,7 @@ module NewRelic::Agent::Samplers
|
|
66
75
|
NewRelic::Agent.instance.log.debug e.backtrace.join("\n ")
|
67
76
|
NewRelic::Agent.instance.log.error "Disabling memory sampler."
|
68
77
|
@broken = true
|
78
|
+
return nil
|
69
79
|
end
|
70
80
|
end
|
71
81
|
end
|
@@ -73,7 +83,7 @@ module NewRelic::Agent::Samplers
|
|
73
83
|
class JavaHeapSampler < Base
|
74
84
|
|
75
85
|
def get_memory
|
76
|
-
raise "Can't sample Java heap unless running in JRuby" unless defined?
|
86
|
+
raise "Can't sample Java heap unless running in JRuby" unless defined? JRuby
|
77
87
|
java.lang.Runtime.getRuntime.totalMemory / (1024 * 1024).to_f rescue nil
|
78
88
|
end
|
79
89
|
def to_s
|
@@ -128,3 +138,5 @@ module NewRelic::Agent::Samplers
|
|
128
138
|
end
|
129
139
|
end
|
130
140
|
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module NewRelic
|
2
|
+
module Agent
|
3
|
+
module Samplers
|
4
|
+
class ObjectSampler < NewRelic::Agent::Sampler
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
super :objects
|
8
|
+
end
|
9
|
+
|
10
|
+
def stats
|
11
|
+
stats_engine.get_stats_no_scope("GC/objects")
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.supported_on_this_platform?
|
15
|
+
defined?(ObjectSpace) && ObjectSpace.respond_to?(:live_objects)
|
16
|
+
end
|
17
|
+
|
18
|
+
def poll
|
19
|
+
stats.record_data_point(ObjectSpace.live_objects)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -5,7 +5,17 @@ class NewRelic::Agent::ShimAgent < NewRelic::Agent::Agent
|
|
5
5
|
def self.instance
|
6
6
|
@instance ||= self.new
|
7
7
|
end
|
8
|
+
def initialize
|
9
|
+
super
|
10
|
+
@histogram.extend NewRelic::Histogram::Shim
|
11
|
+
@stats_engine.extend NewRelic::Agent::StatsEngine::Shim
|
12
|
+
@stats_engine.extend NewRelic::Agent::StatsEngine::Transactions::Shim
|
13
|
+
@transaction_sampler.extend NewRelic::Agent::TransactionSampler::Shim
|
14
|
+
@error_collector.extend NewRelic::Agent::ErrorCollector::Shim
|
15
|
+
end
|
8
16
|
def ensure_worker_thread_started; end
|
9
17
|
def start *args; end
|
10
18
|
def shutdown; end
|
19
|
+
def push_trace_execution_flag(*args); end
|
20
|
+
def pop_trace_execution_flag(*args); end
|
11
21
|
end
|
@@ -1,286 +1,24 @@
|
|
1
|
+
require 'new_relic/agent/stats_engine/metric_stats'
|
2
|
+
require 'new_relic/agent/stats_engine/samplers'
|
3
|
+
require 'new_relic/agent/stats_engine/transactions'
|
1
4
|
|
2
|
-
module NewRelic
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@periodic_samplers = []
|
12
|
-
@scope_stack_listener = nil
|
13
|
-
|
14
|
-
# Makes the unit tests happy
|
15
|
-
Thread::current[:newrelic_scope_stack] = nil
|
16
|
-
|
17
|
-
spawn_sampler_thread
|
18
|
-
end
|
19
|
-
|
20
|
-
def log
|
21
|
-
NewRelic::Control.instance.log
|
22
|
-
end
|
23
|
-
|
24
|
-
def spawn_sampler_thread
|
25
|
-
|
26
|
-
return if !@sampler_process.nil? && @sampler_process == $$
|
27
|
-
|
28
|
-
# start up a thread that will periodically poll for metric samples
|
29
|
-
@sampler_thread = Thread.new do
|
30
|
-
while true do
|
31
|
-
begin
|
32
|
-
sleep POLL_PERIOD
|
33
|
-
poll @periodic_samplers
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
@sampler_thread['newrelic_label'] = 'Sampler Tasks'
|
38
|
-
@sampler_process = $$
|
39
|
-
end
|
40
|
-
|
41
|
-
def add_scope_stack_listener(l)
|
42
|
-
fail "Can't add a scope listener midflight in a transaction" if scope_stack.any?
|
43
|
-
@scope_stack_listener = l
|
44
|
-
end
|
45
|
-
|
46
|
-
def remove_scope_stack_listener(l)
|
47
|
-
@scope_stack_listener = nil
|
48
|
-
end
|
49
|
-
|
50
|
-
def push_scope(metric, time = Time.now.to_f, deduct_call_time_from_parent = true)
|
51
|
-
|
52
|
-
stack = (Thread::current[:newrelic_scope_stack] ||= [])
|
53
|
-
|
54
|
-
if @scope_stack_listener
|
55
|
-
@scope_stack_listener.notice_first_scope_push(time) if stack.empty?
|
56
|
-
@scope_stack_listener.notice_push_scope metric, time
|
57
|
-
end
|
58
|
-
|
59
|
-
scope = ScopeStackElement.new(metric, 0, deduct_call_time_from_parent)
|
60
|
-
stack.push scope
|
61
|
-
|
62
|
-
scope
|
63
|
-
end
|
64
|
-
|
65
|
-
def pop_scope(expected_scope, duration, time=Time.now.to_f)
|
66
|
-
|
67
|
-
stack = Thread::current[:newrelic_scope_stack]
|
68
|
-
|
69
|
-
scope = stack.pop
|
70
|
-
|
71
|
-
fail "unbalanced pop from blame stack: #{scope.name} != #{expected_scope.name}" if scope != expected_scope
|
72
|
-
|
73
|
-
stack.last.children_time += duration unless (stack.empty? || !scope.deduct_call_time_from_parent)
|
74
|
-
|
75
|
-
if !scope.deduct_call_time_from_parent && !stack.empty?
|
76
|
-
stack.last.children_time += scope.children_time
|
77
|
-
end
|
78
|
-
|
79
|
-
if @scope_stack_listener
|
80
|
-
@scope_stack_listener.notice_pop_scope(scope.name, time)
|
81
|
-
@scope_stack_listener.notice_scope_empty(time) if stack.empty?
|
82
|
-
end
|
83
|
-
|
84
|
-
scope
|
85
|
-
end
|
86
|
-
|
87
|
-
def peek_scope
|
88
|
-
scope_stack.last
|
89
|
-
end
|
90
|
-
|
91
|
-
# Add an instance of Sampler to be invoked about every 10 seconds on a background
|
92
|
-
# thread.
|
93
|
-
def add_sampler sampler
|
94
|
-
@periodic_samplers << sampler
|
95
|
-
sampler.stats_engine = self
|
96
|
-
log.debug "Adding sampler #{sampler.id.to_s}"
|
97
|
-
end
|
98
|
-
|
99
|
-
# Add a sampler to be invoked just before each harvest.
|
100
|
-
def add_harvest_sampler sampler
|
101
|
-
@harvest_samplers << sampler
|
102
|
-
sampler.stats_engine = self
|
103
|
-
log.debug "Adding harvest time sampler: #{sampler.id.to_s}"
|
104
|
-
end
|
105
|
-
|
106
|
-
# set the name of the transaction for the current thread, which will be used
|
107
|
-
# to define the scope of all traced methods called on this thread until the
|
108
|
-
# scope stack is empty.
|
109
|
-
#
|
110
|
-
# currently the transaction name is the name of the controller action that
|
111
|
-
# is invoked via the dispatcher, but conceivably we could use other transaction
|
112
|
-
# names in the future if the traced application does more than service http request
|
113
|
-
# via controller actions
|
114
|
-
def transaction_name=(transaction)
|
115
|
-
Thread::current[:newrelic_transaction_name] = transaction
|
116
|
-
end
|
117
|
-
|
118
|
-
def transaction_name
|
119
|
-
Thread::current[:newrelic_transaction_name]
|
120
|
-
end
|
121
|
-
|
122
|
-
|
123
|
-
def lookup_stat(metric_name)
|
124
|
-
return @stats_hash[metric_name]
|
125
|
-
end
|
126
|
-
def metrics
|
127
|
-
return @stats_hash.keys
|
128
|
-
end
|
129
|
-
|
130
|
-
def get_stats_no_scope(metric_name)
|
131
|
-
stats = @stats_hash[metric_name]
|
132
|
-
if stats.nil?
|
133
|
-
stats = NewRelic::MethodTraceStats.new
|
134
|
-
@stats_hash[metric_name] = stats
|
135
|
-
end
|
136
|
-
stats
|
137
|
-
end
|
138
|
-
|
139
|
-
# This version allows a caller to pass a stat class to use
|
140
|
-
#
|
141
|
-
def get_custom_stats(metric_name, stat_class)
|
142
|
-
stats = @stats_hash[metric_name]
|
143
|
-
if stats.nil?
|
144
|
-
stats = stat_class.new
|
145
|
-
@stats_hash[metric_name] = stats
|
146
|
-
end
|
147
|
-
stats
|
148
|
-
end
|
149
|
-
|
150
|
-
# If use_scope is true, two chained metrics are created, one with scope and one without
|
151
|
-
# If scoped_metric_only is true, only a scoped metric is created (used by rendering metrics which by definition are per controller only)
|
152
|
-
def get_stats(metric_name, use_scope = true, scoped_metric_only = false)
|
153
|
-
|
154
|
-
if scoped_metric_only
|
155
|
-
spec = NewRelic::MetricSpec.new metric_name, transaction_name
|
156
|
-
|
157
|
-
stats = @stats_hash[spec]
|
158
|
-
if stats.nil?
|
159
|
-
stats = NewRelic::MethodTraceStats.new
|
160
|
-
@stats_hash[spec] = stats
|
161
|
-
end
|
162
|
-
else
|
163
|
-
stats = @stats_hash[metric_name]
|
164
|
-
if stats.nil?
|
165
|
-
stats = NewRelic::MethodTraceStats.new
|
166
|
-
@stats_hash[metric_name] = stats
|
167
|
-
end
|
168
|
-
|
169
|
-
if use_scope && transaction_name
|
170
|
-
spec = NewRelic::MetricSpec.new metric_name, transaction_name
|
171
|
-
|
172
|
-
scoped_stats = @stats_hash[spec]
|
173
|
-
if scoped_stats.nil?
|
174
|
-
scoped_stats = NewRelic::ScopedMethodTraceStats.new stats
|
175
|
-
@stats_hash[spec] = scoped_stats
|
176
|
-
end
|
177
|
-
|
178
|
-
stats = scoped_stats
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
stats
|
183
|
-
end
|
184
|
-
|
185
|
-
# Harvest the timeslice data. First recombine current statss
|
186
|
-
# with any previously
|
187
|
-
# unsent metrics, clear out stats cache, and return the current
|
188
|
-
# stats.
|
189
|
-
# ---
|
190
|
-
# Note: this is not synchronized. There is still some risk in this and
|
191
|
-
# we will revisit later to see if we can make this more robust without
|
192
|
-
# sacrificing efficiency.
|
193
|
-
# +++
|
194
|
-
def harvest_timeslice_data(previous_timeslice_data, metric_ids)
|
195
|
-
timeslice_data = {}
|
196
|
-
poll @harvest_samplers
|
197
|
-
@stats_hash.keys.each do | metric_spec |
|
198
|
-
|
199
|
-
|
200
|
-
# get a copy of the stats collected since the last harvest, and clear
|
201
|
-
# the stats inside our hash table for the next time slice.
|
202
|
-
stats = @stats_hash[metric_spec]
|
203
|
-
|
204
|
-
# we have an optimization for unscoped metrics
|
205
|
-
if !(metric_spec.is_a? NewRelic::MetricSpec)
|
206
|
-
metric_spec = NewRelic::MetricSpec.new metric_spec
|
207
|
-
end
|
208
|
-
|
209
|
-
if stats.nil?
|
210
|
-
raise "Nil stats for #{metric_spec.name} (#{metric_spec.scope})"
|
211
|
-
end
|
212
|
-
|
213
|
-
stats_copy = stats.clone
|
214
|
-
stats.reset
|
215
|
-
|
216
|
-
# if the previous timeslice data has not been reported (due to an error of some sort)
|
217
|
-
# then we need to merge this timeslice with the previously accumulated - but not sent
|
218
|
-
# data
|
219
|
-
previous_metric_data = previous_timeslice_data[metric_spec]
|
220
|
-
stats_copy.merge! previous_metric_data.stats unless previous_metric_data.nil?
|
221
|
-
stats_copy.round!
|
222
|
-
|
223
|
-
# don't bother collecting and reporting stats that have zero-values for this timeslice.
|
224
|
-
# significant performance boost and storage savings.
|
225
|
-
unless stats_copy.is_reset?
|
226
|
-
|
227
|
-
metric_spec_for_transport = (metric_ids[metric_spec].nil?) ? metric_spec : nil
|
228
|
-
|
229
|
-
metric_data = NewRelic::MetricData.new(metric_spec_for_transport, stats_copy, metric_ids[metric_spec])
|
230
|
-
|
231
|
-
timeslice_data[metric_spec] = metric_data
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
timeslice_data
|
236
|
-
end
|
237
|
-
|
238
|
-
def start_transaction
|
239
|
-
Thread::current[:newrelic_scope_stack] = []
|
240
|
-
end
|
241
|
-
|
242
|
-
# Try to clean up gracefully, otherwise we leave things hanging around on thread locals
|
243
|
-
#
|
244
|
-
def end_transaction
|
245
|
-
stack = Thread::current[:newrelic_scope_stack]
|
246
|
-
|
247
|
-
if stack
|
248
|
-
@scope_stack_listener.notice_scope_empty(Time.now) if @scope_stack_listener && !stack.empty?
|
5
|
+
module NewRelic
|
6
|
+
module Agent
|
7
|
+
class StatsEngine
|
8
|
+
include MetricStats
|
9
|
+
include Samplers
|
10
|
+
include Transactions
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
# Makes the unit tests happy
|
249
14
|
Thread::current[:newrelic_scope_stack] = nil
|
15
|
+
spawn_sampler_thread
|
250
16
|
end
|
251
17
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
# Remove all stats. For test code only.
|
256
|
-
def clear_stats
|
257
|
-
@stats_hash.clear
|
258
|
-
end
|
259
|
-
|
260
|
-
# Reset each of the stats, such as when a new passenger instance starts up.
|
261
|
-
def reset_stats
|
262
|
-
@stats_hash.values.each { |s| s.reset }
|
263
|
-
end
|
264
|
-
|
265
|
-
private
|
266
|
-
|
267
|
-
# Call poll on each of the samplers. Remove
|
268
|
-
# the sampler if it raises.
|
269
|
-
def poll(samplers)
|
270
|
-
samplers.delete_if do |sampled_item|
|
271
|
-
begin
|
272
|
-
sampled_item.poll
|
273
|
-
false # it's okay. don't delete it.
|
274
|
-
rescue => e
|
275
|
-
log.error "Removing #{sampled_item} from list"
|
276
|
-
log.error e
|
277
|
-
log.debug e.backtrace.to_s
|
278
|
-
true # remove the sampler
|
279
|
-
end
|
18
|
+
def log
|
19
|
+
NewRelic::Control.instance.log
|
280
20
|
end
|
281
|
-
|
282
|
-
def scope_stack
|
283
|
-
Thread::current[:newrelic_scope_stack] ||= []
|
21
|
+
|
284
22
|
end
|
285
23
|
end
|
286
24
|
end
|