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.

Files changed (120) hide show
  1. data/CHANGELOG +117 -49
  2. data/bin/mongrel_rpm +2 -2
  3. data/install.rb +42 -33
  4. data/lib/new_relic/agent.rb +149 -39
  5. data/lib/new_relic/agent/agent.rb +139 -122
  6. data/lib/new_relic/agent/busy_calculator.rb +91 -0
  7. data/lib/new_relic/agent/collection_helper.rb +11 -2
  8. data/lib/new_relic/agent/error_collector.rb +33 -27
  9. data/lib/new_relic/agent/instrumentation/active_record_instrumentation.rb +30 -26
  10. data/lib/new_relic/agent/instrumentation/authlogic.rb +8 -0
  11. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +316 -105
  12. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +22 -0
  13. data/lib/new_relic/agent/instrumentation/memcache.rb +18 -12
  14. data/lib/new_relic/agent/instrumentation/merb/errors.rb +2 -1
  15. data/lib/new_relic/agent/instrumentation/metric_frame.rb +258 -0
  16. data/lib/new_relic/agent/instrumentation/net.rb +7 -11
  17. data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +1 -1
  18. data/lib/new_relic/agent/instrumentation/rack.rb +109 -0
  19. data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +6 -5
  20. data/lib/new_relic/agent/instrumentation/rails/errors.rb +7 -7
  21. data/lib/new_relic/agent/instrumentation/sinatra.rb +46 -0
  22. data/lib/new_relic/agent/method_tracer.rb +305 -150
  23. data/lib/new_relic/agent/sampler.rb +34 -0
  24. data/lib/new_relic/agent/samplers/cpu_sampler.rb +11 -1
  25. data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +37 -0
  26. data/lib/new_relic/agent/samplers/memory_sampler.rb +22 -10
  27. data/lib/new_relic/agent/samplers/object_sampler.rb +24 -0
  28. data/lib/new_relic/agent/shim_agent.rb +10 -0
  29. data/lib/new_relic/agent/stats_engine.rb +16 -278
  30. data/lib/new_relic/agent/stats_engine/metric_stats.rb +118 -0
  31. data/lib/new_relic/agent/stats_engine/samplers.rb +81 -0
  32. data/lib/new_relic/agent/stats_engine/transactions.rb +149 -0
  33. data/lib/new_relic/agent/transaction_sampler.rb +73 -67
  34. data/lib/new_relic/agent/worker_loop.rb +69 -68
  35. data/lib/new_relic/commands/deployments.rb +4 -6
  36. data/lib/new_relic/control.rb +121 -60
  37. data/lib/new_relic/control/external.rb +13 -0
  38. data/lib/new_relic/control/merb.rb +2 -0
  39. data/lib/new_relic/control/rails.rb +16 -6
  40. data/lib/new_relic/control/ruby.rb +8 -5
  41. data/lib/new_relic/control/sinatra.rb +18 -0
  42. data/lib/new_relic/delayed_job_injection.rb +25 -0
  43. data/lib/new_relic/histogram.rb +89 -0
  44. data/lib/new_relic/local_environment.rb +64 -30
  45. data/lib/new_relic/metric_data.rb +15 -6
  46. data/lib/new_relic/metric_parser.rb +14 -1
  47. data/lib/new_relic/metric_parser/active_record.rb +14 -0
  48. data/lib/new_relic/metric_parser/controller.rb +5 -2
  49. data/lib/new_relic/metric_parser/external.rb +50 -0
  50. data/lib/new_relic/metric_parser/other_transaction.rb +15 -0
  51. data/lib/new_relic/metric_parser/web_frontend.rb +14 -0
  52. data/lib/new_relic/metric_spec.rb +39 -20
  53. data/lib/new_relic/metrics.rb +9 -7
  54. data/lib/new_relic/noticed_error.rb +6 -8
  55. data/lib/new_relic/rack/metric_app.rb +5 -4
  56. data/lib/new_relic/rack/{newrelic.ru → mongrel_rpm.ru} +4 -4
  57. data/lib/new_relic/rack/newrelic.yml +1 -0
  58. data/lib/new_relic/{rack.rb → rack_app.rb} +0 -0
  59. data/lib/new_relic/recipes.rb +1 -1
  60. data/lib/new_relic/stats.rb +40 -26
  61. data/lib/new_relic/transaction_analysis.rb +5 -2
  62. data/lib/new_relic/transaction_sample.rb +134 -55
  63. data/lib/new_relic/version.rb +27 -20
  64. data/lib/new_relic_api.rb +67 -47
  65. data/lib/newrelic_rpm.rb +5 -5
  66. data/lib/tasks/tests.rake +2 -0
  67. data/newrelic.yml +69 -29
  68. data/test/active_record_fixtures.rb +2 -2
  69. data/test/config/newrelic.yml +4 -7
  70. data/test/config/test_control.rb +1 -2
  71. data/test/new_relic/agent/active_record_instrumentation_test.rb +115 -31
  72. data/test/new_relic/agent/agent_controller_test.rb +274 -0
  73. data/test/new_relic/agent/agent_test_controller.rb +42 -6
  74. data/test/new_relic/agent/busy_calculator_test.rb +79 -0
  75. data/test/new_relic/agent/collection_helper_test.rb +10 -3
  76. data/test/new_relic/agent/error_collector_test.rb +35 -17
  77. data/test/new_relic/agent/method_tracer_test.rb +60 -20
  78. data/test/new_relic/agent/metric_data_test.rb +2 -2
  79. data/test/new_relic/agent/metric_frame_test.rb +51 -0
  80. data/test/new_relic/agent/net_instrumentation_test.rb +77 -0
  81. data/test/new_relic/agent/{agent_test.rb → rpm_agent_test.rb} +26 -5
  82. data/test/new_relic/agent/stats_engine/metric_stats_test.rb +79 -0
  83. data/test/new_relic/{samplers_test.rb → agent/stats_engine/samplers_test.rb} +23 -22
  84. data/test/new_relic/agent/{stats_engine_test.rb → stats_engine/stats_engine_test.rb} +19 -101
  85. data/test/new_relic/agent/task_instrumentation_test.rb +176 -0
  86. data/test/new_relic/agent/transaction_sample_builder_test.rb +2 -2
  87. data/test/new_relic/agent/transaction_sample_test.rb +53 -38
  88. data/test/new_relic/agent/transaction_sampler_test.rb +101 -33
  89. data/test/new_relic/agent/worker_loop_test.rb +16 -14
  90. data/test/new_relic/control_test.rb +26 -13
  91. data/test/new_relic/metric_parser_test.rb +31 -1
  92. data/test/new_relic/metric_spec_test.rb +2 -2
  93. data/test/new_relic/stats_test.rb +0 -8
  94. data/test/new_relic/version_number_test.rb +31 -1
  95. data/test/test_helper.rb +37 -1
  96. data/ui/controllers/newrelic_controller.rb +19 -11
  97. data/ui/helpers/google_pie_chart.rb +5 -11
  98. data/ui/helpers/newrelic_helper.rb +40 -35
  99. data/ui/views/layouts/newrelic_default.rhtml +7 -7
  100. data/ui/views/newrelic/_sample.rhtml +5 -1
  101. data/ui/views/newrelic/images/new-relic-rpm-desktop.gif +0 -0
  102. data/ui/views/newrelic/images/textmate.png +0 -0
  103. data/ui/views/newrelic/index.rhtml +13 -1
  104. data/ui/views/newrelic/show_sample.rhtml +5 -2
  105. data/ui/views/newrelic/stylesheets/style.css +54 -3
  106. metadata +65 -145
  107. data/Manifest +0 -143
  108. data/Rakefile +0 -22
  109. data/init.rb +0 -38
  110. data/lib/new_relic/agent/instrumentation/dispatcher_instrumentation.rb +0 -127
  111. data/lib/new_relic/agent/instrumentation/error_instrumentation.rb +0 -14
  112. data/lib/new_relic/agent/instrumentation/merb/dispatcher.rb +0 -13
  113. data/lib/new_relic/agent/instrumentation/rails/dispatcher.rb +0 -38
  114. data/lib/new_relic/agent/patch_const_missing.rb +0 -125
  115. data/lib/new_relic/agent/samplers/mongrel_sampler.rb +0 -22
  116. data/lib/new_relic/metric_parser/database.rb +0 -23
  117. data/newrelic_rpm.gemspec +0 -35
  118. data/test/new_relic/agent/classloader_patch_test.rb +0 -56
  119. data/test/new_relic/agent/controller_test.rb +0 -107
  120. 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::Agent::Samplers
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::Agent::Samplers
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? Java
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.log.warn "Error attempting to use /proc/$$/status file for reading memory. Using ps command instead."
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.info "Using /proc/$$/status for reading process memory."
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
- def platform
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? Java
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::Agent
3
- class StatsEngine
4
- POLL_PERIOD = 10
5
-
6
- ScopeStackElement = Struct.new(:name, :children_time, :deduct_call_time_from_parent)
7
-
8
- def initialize
9
- @stats_hash = {}
10
- @harvest_samplers = []
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
- Thread::current[:newrelic_transaction_name] = nil
253
- end
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
- end
282
- def scope_stack
283
- Thread::current[:newrelic_scope_stack] ||= []
21
+
284
22
  end
285
23
  end
286
24
  end