newrelic_rpm 2.8.11 → 2.9.2
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 +267 -0
- data/LICENSE +1 -1
- data/Manifest +142 -0
- data/README.md +138 -0
- data/Rakefile +10 -28
- data/bin/mongrel_rpm +33 -0
- data/cert/cacert.pem +34 -0
- data/init.rb +38 -0
- data/lib/new_relic/agent/agent.rb +160 -347
- data/lib/new_relic/agent/collection_helper.rb +13 -24
- data/lib/new_relic/agent/error_collector.rb +29 -15
- data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +63 -76
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +90 -48
- data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +72 -47
- data/lib/new_relic/agent/instrumentation/error_instrumentation.rb +14 -0
- data/lib/new_relic/agent/instrumentation/merb/controller.rb +10 -1
- data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +5 -7
- data/lib/new_relic/agent/instrumentation/merb/errors.rb +3 -1
- data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +7 -0
- data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +34 -7
- data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +20 -12
- data/lib/new_relic/agent/instrumentation/rails/errors.rb +5 -4
- data/lib/new_relic/agent/method_tracer.rb +159 -135
- data/lib/new_relic/agent/patch_const_missing.rb +46 -26
- data/lib/new_relic/agent/sampler.rb +12 -0
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +44 -0
- data/lib/new_relic/agent/samplers/memory_sampler.rb +126 -0
- data/lib/new_relic/agent/samplers/mongrel_sampler.rb +22 -0
- data/lib/new_relic/agent/shim_agent.rb +11 -0
- data/lib/new_relic/agent/stats_engine.rb +85 -46
- data/lib/new_relic/agent/transaction_sampler.rb +63 -38
- data/lib/new_relic/agent/worker_loop.rb +8 -18
- data/lib/new_relic/agent.rb +200 -25
- data/lib/new_relic/commands/deployments.rb +9 -9
- data/lib/new_relic/control/merb.rb +22 -0
- data/lib/new_relic/control/rails.rb +141 -0
- data/lib/new_relic/{config → control}/ruby.rb +13 -2
- data/lib/new_relic/control.rb +424 -0
- data/lib/new_relic/local_environment.rb +201 -79
- data/lib/new_relic/metric_data.rb +7 -0
- data/lib/new_relic/metric_parser/action_mailer.rb +9 -0
- data/lib/new_relic/metric_parser/active_merchant.rb +26 -0
- data/lib/new_relic/metric_parser/active_record.rb +11 -0
- data/lib/new_relic/metric_parser/controller.rb +51 -0
- data/lib/new_relic/metric_parser/controller_cpu.rb +38 -0
- data/lib/new_relic/metric_parser/database.rb +23 -0
- data/lib/new_relic/metric_parser/errors.rb +6 -0
- data/lib/new_relic/metric_parser/mem_cache.rb +12 -0
- data/lib/new_relic/metric_parser/view.rb +61 -0
- data/lib/new_relic/metric_parser/web_service.rb +9 -0
- data/lib/new_relic/metric_parser.rb +107 -0
- data/lib/new_relic/metric_spec.rb +5 -0
- data/lib/new_relic/noticed_error.rb +5 -1
- data/lib/new_relic/rack/metric_app.rb +57 -0
- data/lib/new_relic/rack/newrelic.ru +25 -0
- data/lib/new_relic/rack/newrelic.yml +25 -0
- data/lib/new_relic/rack.rb +5 -0
- data/lib/new_relic/recipes.rb +10 -3
- data/lib/new_relic/stats.rb +130 -144
- data/lib/new_relic/transaction_analysis.rb +7 -8
- data/lib/new_relic/transaction_sample.rb +86 -10
- data/lib/new_relic/version.rb +41 -160
- data/lib/new_relic_api.rb +7 -6
- data/lib/newrelic_rpm.rb +30 -17
- data/lib/tasks/{agent_tests.rake → tests.rake} +1 -1
- data/newrelic.yml +115 -62
- data/newrelic_rpm.gemspec +36 -0
- data/test/active_record_fixtures.rb +55 -0
- data/test/config/newrelic.yml +21 -3
- data/test/config/{test_config.rb → test_control.rb} +14 -10
- data/test/new_relic/agent/active_record_instrumentation_test.rb +189 -0
- data/test/new_relic/agent/agent_test.rb +104 -0
- data/test/new_relic/agent/agent_test_controller.rb +18 -1
- data/test/new_relic/agent/classloader_patch_test.rb +56 -0
- data/test/new_relic/agent/{tc_collection_helper.rb → collection_helper_test.rb} +28 -23
- data/test/new_relic/agent/controller_test.rb +107 -0
- data/test/new_relic/agent/dispatcher_instrumentation_test.rb +70 -0
- data/test/new_relic/agent/error_collector_test.rb +155 -0
- data/test/new_relic/agent/{tc_method_tracer.rb → method_tracer_test.rb} +6 -12
- data/test/new_relic/agent/metric_data_test.rb +56 -0
- data/test/new_relic/agent/stats_engine_test.rb +266 -0
- data/test/new_relic/agent/{tc_transaction_sample_builder.rb → transaction_sample_builder_test.rb} +6 -5
- data/test/new_relic/agent/{tc_transaction_sample.rb → transaction_sample_test.rb} +9 -13
- data/test/new_relic/agent/transaction_sampler_test.rb +317 -0
- data/test/new_relic/agent/{tc_worker_loop.rb → worker_loop_test.rb} +1 -1
- data/test/new_relic/control_test.rb +97 -0
- data/test/new_relic/{tc_deployments_api.rb → deployments_api_test.rb} +8 -4
- data/test/new_relic/environment_test.rb +75 -0
- data/test/new_relic/metric_parser_test.rb +142 -0
- data/test/new_relic/{tc_metric_spec.rb → metric_spec_test.rb} +28 -1
- data/test/new_relic/samplers_test.rb +71 -0
- data/test/new_relic/{tc_shim_agent.rb → shim_agent_test.rb} +1 -1
- data/test/new_relic/stats_test.rb +291 -0
- data/test/new_relic/version_number_test.rb +46 -0
- data/test/test_helper.rb +7 -30
- data/test/ui/newrelic_controller_test.rb +14 -0
- data/test/ui/{tc_newrelic_helper.rb → newrelic_helper_test.rb} +16 -7
- data/ui/controllers/newrelic_controller.rb +17 -3
- data/ui/helpers/newrelic_helper.rb +44 -15
- data/ui/views/layouts/newrelic_default.rhtml +7 -8
- data/ui/views/newrelic/_sample.rhtml +5 -2
- data/ui/views/newrelic/_segment.rhtml +1 -1
- data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
- data/ui/views/newrelic/_segment_row.rhtml +4 -4
- data/ui/views/newrelic/_show_sample_detail.rhtml +3 -1
- data/ui/views/newrelic/_show_sample_sql.rhtml +2 -1
- data/ui/views/newrelic/explain_sql.rhtml +2 -5
- data/ui/views/newrelic/images/file_icon.png +0 -0
- data/ui/views/newrelic/images/new_relic_rpm_desktop.gif +0 -0
- data/ui/views/newrelic/index.rhtml +21 -13
- data/ui/views/newrelic/javascript/prototype-scriptaculous.js +7288 -0
- data/ui/views/newrelic/show_sample.rhtml +18 -3
- data/ui/views/newrelic/stylesheets/style.css +39 -0
- data/ui/views/newrelic/threads.rhtml +52 -0
- metadata +192 -70
- data/README +0 -136
- data/lib/new_relic/agent/instrumentation/rails/rails.rb +0 -6
- data/lib/new_relic/agent/samplers/cpu.rb +0 -29
- data/lib/new_relic/agent/samplers/memory.rb +0 -53
- data/lib/new_relic/agent/samplers/mongrel.rb +0 -26
- data/lib/new_relic/agent/synchronize.rb +0 -40
- data/lib/new_relic/config/merb.rb +0 -35
- data/lib/new_relic/config/rails.rb +0 -114
- data/lib/new_relic/config.rb +0 -279
- data/lib/new_relic/shim_agent.rb +0 -96
- data/test/new_relic/agent/model_fixture.rb +0 -15
- data/test/new_relic/agent/tc_active_record.rb +0 -90
- data/test/new_relic/agent/tc_agent.rb +0 -148
- data/test/new_relic/agent/tc_controller.rb +0 -77
- data/test/new_relic/agent/tc_dispatcher_instrumentation.rb +0 -52
- data/test/new_relic/agent/tc_error_collector.rb +0 -127
- data/test/new_relic/agent/tc_stats_engine.rb +0 -218
- data/test/new_relic/agent/tc_synchronize.rb +0 -37
- data/test/new_relic/agent/tc_transaction_sampler.rb +0 -302
- data/test/new_relic/tc_config.rb +0 -36
- data/test/new_relic/tc_environment.rb +0 -94
- data/test/new_relic/tc_stats.rb +0 -141
@@ -1,44 +1,62 @@
|
|
1
1
|
# This class is for debugging purposes only.
|
2
2
|
# It inserts instrumentation into class loading to verify
|
3
3
|
# that no classes are being loaded on the new relic thread,
|
4
|
-
# which can cause problems in the class loader code.
|
4
|
+
# which can cause problems in the class loader code.
|
5
|
+
# It is only loaded by agent.rb when a particular newrelic.yml
|
6
|
+
# option is set.
|
5
7
|
|
6
|
-
module ClassLoadingWatcher
|
8
|
+
module ClassLoadingWatcher # :nodoc: all
|
7
9
|
|
8
10
|
extend self
|
9
11
|
@@background_thread = nil
|
12
|
+
@@flag_const_missing = nil
|
10
13
|
|
11
14
|
def background_thread
|
12
15
|
@@background_thread
|
13
16
|
end
|
17
|
+
def flag_const_missing
|
18
|
+
@@flag_const_missing
|
19
|
+
end
|
20
|
+
def flag_const_missing=(val)
|
21
|
+
@@flag_const_missing = val
|
22
|
+
end
|
14
23
|
|
15
|
-
def
|
24
|
+
def background_thread=(thread)
|
16
25
|
@@background_thread = thread
|
17
26
|
|
18
|
-
# these tests that check is working
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
27
|
+
# these tests verify that check is working
|
28
|
+
=begin
|
29
|
+
@@background_thread = nil
|
30
|
+
bad = ConstMissingInForegroundThread rescue nil
|
31
|
+
@@background_thread = thread
|
32
|
+
bad = ConstMissingInBackgroundThread rescue nil
|
33
|
+
require 'new_relic/agent/patch_const_missing'
|
34
|
+
load 'new_relic/agent/patch_const_missing.rb'
|
35
|
+
=end
|
26
36
|
end
|
27
37
|
module SanityCheck
|
28
|
-
def
|
38
|
+
def nr_check_for_classloading(*args)
|
29
39
|
|
30
40
|
if Thread.current == ClassLoadingWatcher.background_thread
|
31
|
-
|
32
|
-
|
33
|
-
exception = NewRelic::Agent::BackgroundLoadingError.new(msg.clone)
|
34
|
-
exception.set_backtrace(caller)
|
35
|
-
|
36
|
-
NewRelic::Agent.instance.error_collector.notice_error(nil, nil, [], exception)
|
37
|
-
msg << caller.join("\n")
|
38
|
-
|
39
|
-
NewRelic::Config.instance.log.error msg
|
41
|
+
nr_error "Agent background thread shouldn't be loading classes (#{args.inspect})"
|
40
42
|
end
|
41
43
|
end
|
44
|
+
#
|
45
|
+
def nr_check_for_constmissing(*args)
|
46
|
+
if ClassLoadingWatcher.flag_const_missing
|
47
|
+
nr_error "Classes in Agent should not be loaded via const_missing (#{args.inspect})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
private
|
51
|
+
def nr_error(msg)
|
52
|
+
exception = NewRelic::Agent::BackgroundLoadingError.new(msg)
|
53
|
+
backtrace = caller
|
54
|
+
backtrace.shift
|
55
|
+
exception.set_backtrace(backtrace)
|
56
|
+
NewRelic::Agent.instance.error_collector.notice_error(exception, nil)
|
57
|
+
msg << "\n" << backtrace.join("\n")
|
58
|
+
NewRelic::Control.instance.log.error msg
|
59
|
+
end
|
42
60
|
end
|
43
61
|
def enable_warning
|
44
62
|
Object.class_eval do
|
@@ -81,25 +99,27 @@ module ClassLoadingWatcher
|
|
81
99
|
end
|
82
100
|
end
|
83
101
|
|
84
|
-
class Object
|
102
|
+
class Object # :nodoc:
|
85
103
|
include ClassLoadingWatcher::SanityCheck
|
86
104
|
|
87
105
|
def new_relic_require(*args)
|
88
|
-
|
106
|
+
nr_check_for_classloading("Object require", *args)
|
89
107
|
non_new_relic_require(*args)
|
90
108
|
end
|
91
109
|
|
92
110
|
def new_relic_load(*args)
|
93
|
-
|
111
|
+
nr_check_for_classloading("Object load", *args)
|
94
112
|
non_new_relic_load(*args)
|
95
113
|
end
|
96
114
|
end
|
97
115
|
|
98
|
-
|
116
|
+
|
117
|
+
class Module # :nodoc:
|
99
118
|
include ClassLoadingWatcher::SanityCheck
|
100
119
|
|
101
120
|
def new_relic_const_missing(*args)
|
102
|
-
|
121
|
+
nr_check_for_constmissing("Module #{self.name} const_missing", *args)
|
122
|
+
nr_check_for_classloading("Module #{self.name} const_missing", *args)
|
103
123
|
non_new_relic_const_missing(*args)
|
104
124
|
end
|
105
125
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module NewRelic::Agent::Samplers
|
2
|
+
class CpuSampler < NewRelic::Agent::Sampler
|
3
|
+
attr_reader :last_time
|
4
|
+
def initialize
|
5
|
+
super :cpu
|
6
|
+
poll
|
7
|
+
end
|
8
|
+
def user_util_stats
|
9
|
+
@userutil ||= stats_engine.get_stats("CPU/User/Utilization", false)
|
10
|
+
end
|
11
|
+
def system_util_stats
|
12
|
+
@systemutil ||= stats_engine.get_stats("CPU/System/Utilization", false)
|
13
|
+
end
|
14
|
+
def usertime_stats
|
15
|
+
@usertime ||= stats_engine.get_stats("CPU/User Time", false)
|
16
|
+
end
|
17
|
+
def systemtime_stats
|
18
|
+
@systemtime ||= stats_engine.get_stats("CPU/System Time", false)
|
19
|
+
end
|
20
|
+
def poll
|
21
|
+
now = Time.now
|
22
|
+
t = Process.times
|
23
|
+
if @last_time
|
24
|
+
elapsed = now - @last_time
|
25
|
+
return if elapsed < 1 # Causing some kind of math underflow
|
26
|
+
num_processors = NewRelic::Control.instance.local_env.processors || 1
|
27
|
+
usertime = t.utime - @last_utime
|
28
|
+
systemtime = t.stime - @last_stime
|
29
|
+
|
30
|
+
systemtime_stats.record_data_point(systemtime) if systemtime >= 0
|
31
|
+
usertime_stats.record_data_point(usertime) if usertime >= 0
|
32
|
+
|
33
|
+
# Calculate the true utilization by taking cpu times and dividing by
|
34
|
+
# elapsed time X num_processors.
|
35
|
+
user_util_stats.record_data_point usertime / (elapsed * num_processors)
|
36
|
+
system_util_stats.record_data_point systemtime / (elapsed * num_processors)
|
37
|
+
end
|
38
|
+
@last_utime = t.utime
|
39
|
+
@last_stime = t.stime
|
40
|
+
@last_time = now
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module NewRelic::Agent::Samplers
|
2
|
+
|
3
|
+
class MemorySampler < NewRelic::Agent::Sampler
|
4
|
+
def initialize
|
5
|
+
super :memory
|
6
|
+
|
7
|
+
# macos, linux, solaris
|
8
|
+
if defined? Java
|
9
|
+
@sampler = JavaHeapSampler.new
|
10
|
+
elsif platform =~ /linux/
|
11
|
+
@sampler = ProcStatus.new
|
12
|
+
if !@sampler.can_run?
|
13
|
+
NewRelic::Agent.instance.log.warn "Error attempting to use /proc/$$/status file for reading memory. Using ps command instead."
|
14
|
+
@sampler = ShellPS.new("ps -o rsz")
|
15
|
+
else
|
16
|
+
NewRelic::Agent.instance.log.info "Using /proc/$$/status for reading process memory."
|
17
|
+
end
|
18
|
+
elsif platform =~ /darwin/
|
19
|
+
@sampler = ShellPS.new("ps -o rsz")
|
20
|
+
elsif platform =~ /freebsd/
|
21
|
+
@sampler = ShellPS.new("ps -o rss")
|
22
|
+
elsif platform =~ /solaris/
|
23
|
+
@sampler = ShellPS.new("/usr/bin/ps -o rss -p")
|
24
|
+
end
|
25
|
+
|
26
|
+
raise "Unsupported platform for getting memory: #{platform}" if @sampler.nil?
|
27
|
+
raise "Unable to run #{@sampler}" unless @sampler.can_run?
|
28
|
+
end
|
29
|
+
def platform
|
30
|
+
if RUBY_PLATFORM =~ /java/
|
31
|
+
%x[uname -s].downcase
|
32
|
+
else
|
33
|
+
RUBY_PLATFORM.downcase
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def stats
|
38
|
+
@stats ||= stats_engine.get_stats("Memory/Physical", false)
|
39
|
+
end
|
40
|
+
def poll
|
41
|
+
sample = @sampler.get_sample
|
42
|
+
stats.record_data_point sample if sample
|
43
|
+
stats
|
44
|
+
end
|
45
|
+
class Base
|
46
|
+
def can_run?
|
47
|
+
return false if @broken
|
48
|
+
m = get_memory rescue nil
|
49
|
+
m && m > 0
|
50
|
+
end
|
51
|
+
def get_sample
|
52
|
+
return nil if @broken
|
53
|
+
begin
|
54
|
+
m = get_memory
|
55
|
+
if m.nil?
|
56
|
+
NewRelic::Agent.instance.log.error "Unable to get the resident memory for process #{$$}. Disabling memory sampler."
|
57
|
+
@broken = true
|
58
|
+
end
|
59
|
+
return m
|
60
|
+
rescue => e
|
61
|
+
NewRelic::Agent.instance.log.error "Unable to get the resident memory for process #{$$}. (#{e})"
|
62
|
+
NewRelic::Agent.instance.log.debug e.backtrace.join("\n ")
|
63
|
+
NewRelic::Agent.instance.log.error "Disabling memory sampler."
|
64
|
+
@broken = true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class JavaHeapSampler < Base
|
70
|
+
|
71
|
+
def get_memory
|
72
|
+
raise "Can't sample Java heap unless running in JRuby" unless defined? Java
|
73
|
+
java.lang.Runtime.getRuntime.totalMemory / (1024 * 1024).to_f rescue nil
|
74
|
+
end
|
75
|
+
def to_s
|
76
|
+
"JRuby Java heap sampler"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class ShellPS < Base
|
81
|
+
def initialize(command)
|
82
|
+
super()
|
83
|
+
@command = command
|
84
|
+
end
|
85
|
+
# Returns the amount of resident memory this process is using in MB
|
86
|
+
#
|
87
|
+
def get_memory
|
88
|
+
process = $$
|
89
|
+
memory = `#{@command} #{process}`.split("\n")[1].to_f / 1024.0 rescue nil
|
90
|
+
# if for some reason the ps command doesn't work on the resident os,
|
91
|
+
# then don't execute it any more.
|
92
|
+
raise "Faulty command: `#{@command} #{process}`" if memory.nil? || memory <= 0
|
93
|
+
memory
|
94
|
+
end
|
95
|
+
def to_s
|
96
|
+
"shell command sampler: #{@command}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# ProcStatus
|
101
|
+
#
|
102
|
+
# A class that samples memory by reading the file /proc/$$/status, which is specific to linux
|
103
|
+
#
|
104
|
+
class ProcStatus < Base
|
105
|
+
|
106
|
+
# Returns the amount of resident memory this process is using in MB
|
107
|
+
#
|
108
|
+
def get_memory
|
109
|
+
File.open(proc_status_file, "r") do |f|
|
110
|
+
while !f.eof?
|
111
|
+
if f.readline =~ /RSS:\s*(\d+) kB/i
|
112
|
+
return $1.to_f / 1024.0
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
raise "Unable to find RSS in #{proc_status_file}"
|
117
|
+
end
|
118
|
+
def proc_status_file
|
119
|
+
"/proc/#{$$}/status"
|
120
|
+
end
|
121
|
+
def to_s
|
122
|
+
"proc status file sampler: #{proc_status_file}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module NewRelic::Agent::Samplers
|
2
|
+
# NewRelic Instrumentation for Mongrel - tracks the queue length of the mongrel server.
|
3
|
+
class MongrelSampler < NewRelic::Agent::Sampler
|
4
|
+
def initialize
|
5
|
+
super :mongrel
|
6
|
+
end
|
7
|
+
def queue_stats
|
8
|
+
@queue_stats ||= stats_engine.get_stats("Mongrel/Queue Length", false)
|
9
|
+
end
|
10
|
+
def poll
|
11
|
+
mongrel = NewRelic::Control.instance.local_env.mongrel
|
12
|
+
if mongrel
|
13
|
+
# The mongrel workers list includes workers actively processing requests
|
14
|
+
# so you need to subtract what appears to be the active workers from the total
|
15
|
+
# number of workers to get the queue size.
|
16
|
+
qsize = mongrel.workers.list.length - NewRelic::Agent::Instrumentation::DispatcherInstrumentation::BusyCalculator.busy_count
|
17
|
+
qsize = 0 if qsize < 0
|
18
|
+
queue_stats.record_data_point qsize
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# This agent is loaded by the plug when the plug-in is disabled
|
2
|
+
# It recreates just enough of the API to not break any clients that
|
3
|
+
# invoke the Agent.
|
4
|
+
class NewRelic::Agent::ShimAgent < NewRelic::Agent::Agent
|
5
|
+
def self.instance
|
6
|
+
@instance ||= self.new
|
7
|
+
end
|
8
|
+
def ensure_worker_thread_started; end
|
9
|
+
def start *args; end
|
10
|
+
def shutdown; end
|
11
|
+
end
|
@@ -3,32 +3,23 @@ module NewRelic::Agent
|
|
3
3
|
class StatsEngine
|
4
4
|
POLL_PERIOD = 10
|
5
5
|
|
6
|
-
attr_accessor :log
|
7
|
-
|
8
6
|
ScopeStackElement = Struct.new(:name, :children_time, :deduct_call_time_from_parent)
|
9
7
|
|
10
|
-
|
11
|
-
def initialize(stats, &callback)
|
12
|
-
@stats = stats
|
13
|
-
@callback = callback
|
14
|
-
end
|
15
|
-
|
16
|
-
def poll
|
17
|
-
@callback.call @stats
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize(log = Logger.new(STDERR))
|
8
|
+
def initialize
|
22
9
|
@stats_hash = {}
|
23
|
-
@
|
10
|
+
@harvest_samplers = []
|
11
|
+
@periodic_samplers = []
|
24
12
|
@scope_stack_listener = nil
|
25
|
-
@log = log
|
26
13
|
|
27
14
|
# Makes the unit tests happy
|
28
15
|
Thread::current[:newrelic_scope_stack] = nil
|
29
16
|
|
30
17
|
spawn_sampler_thread
|
31
18
|
end
|
19
|
+
|
20
|
+
def log
|
21
|
+
NewRelic::Control.instance.log
|
22
|
+
end
|
32
23
|
|
33
24
|
def spawn_sampler_thread
|
34
25
|
|
@@ -39,20 +30,11 @@ module NewRelic::Agent
|
|
39
30
|
while true do
|
40
31
|
begin
|
41
32
|
sleep POLL_PERIOD
|
42
|
-
@
|
43
|
-
begin
|
44
|
-
sampled_item.poll
|
45
|
-
rescue => e
|
46
|
-
log.error e
|
47
|
-
@sampled_items.delete sampled_item
|
48
|
-
log.error "Removing #{sampled_item} from list"
|
49
|
-
log.debug e.backtrace.to_s
|
50
|
-
end
|
51
|
-
end
|
33
|
+
poll @periodic_samplers
|
52
34
|
end
|
53
35
|
end
|
54
36
|
end
|
55
|
-
|
37
|
+
@sampler_thread['newrelic_label'] = 'Sampler Tasks'
|
56
38
|
@sampler_process = $$
|
57
39
|
end
|
58
40
|
|
@@ -106,9 +88,19 @@ module NewRelic::Agent
|
|
106
88
|
scope_stack.last
|
107
89
|
end
|
108
90
|
|
109
|
-
|
110
|
-
|
111
|
-
|
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}"
|
112
104
|
end
|
113
105
|
|
114
106
|
# set the name of the transaction for the current thread, which will be used
|
@@ -144,33 +136,65 @@ module NewRelic::Agent
|
|
144
136
|
stats
|
145
137
|
end
|
146
138
|
|
147
|
-
|
139
|
+
# This version allows a caller to pass a stat class to use
|
140
|
+
#
|
141
|
+
def get_custom_stats(metric_name, stat_class)
|
148
142
|
stats = @stats_hash[metric_name]
|
149
143
|
if stats.nil?
|
150
|
-
stats =
|
144
|
+
stats = stat_class.new
|
151
145
|
@stats_hash[metric_name] = stats
|
152
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
153
|
|
154
|
-
if
|
154
|
+
if scoped_metric_only
|
155
155
|
spec = NewRelic::MetricSpec.new metric_name, transaction_name
|
156
156
|
|
157
|
-
|
158
|
-
if
|
159
|
-
|
160
|
-
@stats_hash[spec] =
|
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
|
161
167
|
end
|
162
168
|
|
163
|
-
|
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
|
164
180
|
end
|
165
|
-
|
181
|
+
|
182
|
+
stats
|
166
183
|
end
|
167
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
|
+
# ---
|
168
190
|
# Note: this is not synchronized. There is still some risk in this and
|
169
191
|
# we will revisit later to see if we can make this more robust without
|
170
192
|
# sacrificing efficiency.
|
193
|
+
# +++
|
171
194
|
def harvest_timeslice_data(previous_timeslice_data, metric_ids)
|
172
195
|
timeslice_data = {}
|
173
|
-
@
|
196
|
+
poll @harvest_samplers
|
197
|
+
@stats_hash.keys.each do | metric_spec |
|
174
198
|
|
175
199
|
|
176
200
|
# get a copy of the stats collected since the last harvest, and clear
|
@@ -194,12 +218,11 @@ module NewRelic::Agent
|
|
194
218
|
# data
|
195
219
|
previous_metric_data = previous_timeslice_data[metric_spec]
|
196
220
|
stats_copy.merge! previous_metric_data.stats unless previous_metric_data.nil?
|
197
|
-
|
198
221
|
stats_copy.round!
|
199
222
|
|
200
223
|
# don't bother collecting and reporting stats that have zero-values for this timeslice.
|
201
224
|
# significant performance boost and storage savings.
|
202
|
-
unless stats_copy.
|
225
|
+
unless stats_copy.is_reset?
|
203
226
|
|
204
227
|
metric_spec_for_transport = (metric_ids[metric_spec].nil?) ? metric_spec : nil
|
205
228
|
|
@@ -212,12 +235,10 @@ module NewRelic::Agent
|
|
212
235
|
timeslice_data
|
213
236
|
end
|
214
237
|
|
215
|
-
|
216
238
|
def start_transaction
|
217
239
|
Thread::current[:newrelic_scope_stack] = []
|
218
240
|
end
|
219
241
|
|
220
|
-
|
221
242
|
# Try to clean up gracefully, otherwise we leave things hanging around on thread locals
|
222
243
|
#
|
223
244
|
def end_transaction
|
@@ -231,10 +252,28 @@ module NewRelic::Agent
|
|
231
252
|
Thread::current[:newrelic_transaction_name] = nil
|
232
253
|
end
|
233
254
|
|
255
|
+
def clear_stats # :nodoc: for test code only
|
256
|
+
@stats_hash.clear
|
257
|
+
end
|
234
258
|
private
|
235
259
|
|
236
|
-
|
237
|
-
|
260
|
+
# Call poll on each of the samplers. Remove
|
261
|
+
# the sampler if it raises.
|
262
|
+
def poll(samplers)
|
263
|
+
samplers.delete_if do |sampled_item|
|
264
|
+
begin
|
265
|
+
sampled_item.poll
|
266
|
+
false # it's okay. don't delete it.
|
267
|
+
rescue => e
|
268
|
+
log.error "Removing #{sampled_item} from list"
|
269
|
+
log.error e
|
270
|
+
log.debug e.backtrace.to_s
|
271
|
+
true # remove the sampler
|
272
|
+
end
|
238
273
|
end
|
274
|
+
end
|
275
|
+
def scope_stack
|
276
|
+
Thread::current[:newrelic_scope_stack] ||= []
|
277
|
+
end
|
239
278
|
end
|
240
279
|
end
|