newrelic_rpm 3.6.0.83 → 3.6.1.85.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +27 -0
- data/Gemfile +2 -7
- data/README.md +1 -1
- data/lib/new_relic/agent/agent.rb +3 -2
- data/lib/new_relic/agent/autostart.rb +56 -0
- data/lib/new_relic/agent/browser_monitoring.rb +19 -14
- data/lib/new_relic/agent/configuration/defaults.rb +12 -2
- data/lib/new_relic/agent/configuration/environment_source.rb +4 -1
- data/lib/new_relic/agent/cross_app_monitor.rb +2 -1
- data/lib/new_relic/agent/cross_app_tracing.rb +19 -10
- data/lib/new_relic/agent/error_collector.rb +5 -4
- data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +204 -0
- data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +88 -0
- data/lib/new_relic/agent/instrumentation/active_record.rb +1 -1
- data/lib/new_relic/agent/instrumentation/active_record_helper.rb +1 -1
- data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +84 -0
- data/lib/new_relic/agent/instrumentation/browser_monitoring_timings.rb +3 -2
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +104 -87
- data/lib/new_relic/agent/instrumentation/evented_subscriber.rb +91 -0
- data/lib/new_relic/agent/instrumentation/memcache.rb +4 -4
- data/lib/new_relic/agent/instrumentation/merb/errors.rb +4 -4
- data/lib/new_relic/agent/instrumentation/rack.rb +1 -1
- data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +20 -20
- data/lib/new_relic/agent/instrumentation/rails/errors.rb +5 -5
- data/lib/new_relic/agent/instrumentation/rails3/errors.rb +3 -3
- data/lib/new_relic/agent/instrumentation/rails4/action_controller.rb +3 -25
- data/lib/new_relic/agent/instrumentation/rails4/action_view.rb +2 -115
- data/lib/new_relic/agent/instrumentation/rails4/active_record.rb +2 -82
- data/lib/new_relic/agent/instrumentation/rails4/errors.rb +3 -4
- data/lib/new_relic/agent/method_tracer.rb +93 -56
- data/lib/new_relic/agent/null_logger.rb +6 -0
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +9 -4
- data/lib/new_relic/agent/sql_sampler.rb +10 -6
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +19 -3
- data/lib/new_relic/agent/stats_engine/transactions.rb +53 -34
- data/lib/new_relic/agent/system_info.rb +54 -0
- data/lib/new_relic/agent/thread.rb +2 -2
- data/lib/new_relic/agent/transaction/pop.rb +52 -0
- data/lib/new_relic/agent/transaction.rb +388 -0
- data/lib/new_relic/agent/transaction_info.rb +5 -13
- data/lib/new_relic/agent/transaction_sample_builder.rb +13 -20
- data/lib/new_relic/agent/transaction_sampler.rb +13 -15
- data/lib/new_relic/agent/uri_util.rb +35 -0
- data/lib/new_relic/agent.rb +54 -11
- data/lib/new_relic/build.rb +2 -2
- data/lib/new_relic/control/frameworks/rails.rb +0 -1
- data/lib/new_relic/control/frameworks/rails3.rb +2 -0
- data/lib/new_relic/control/frameworks/rails4.rb +0 -4
- data/lib/new_relic/control/instance_methods.rb +5 -19
- data/lib/new_relic/control/server_methods.rb +2 -0
- data/lib/new_relic/environment_report.rb +4 -34
- data/lib/new_relic/latest_changes.rb +1 -1
- data/lib/new_relic/local_environment.rb +0 -6
- data/lib/new_relic/metric_spec.rb +2 -2
- data/lib/new_relic/rack/error_collector.rb +6 -4
- data/lib/new_relic/transaction_sample.rb +7 -1
- data/lib/new_relic/version.rb +1 -1
- data/lib/newrelic_rpm.rb +2 -2
- data/newrelic.yml +20 -20
- data/test/config/test_control.rb +2 -2
- data/test/multiverse/suites/agent_only/audit_log_test.rb +1 -1
- data/test/multiverse/suites/agent_only/cross_application_tracing_test.rb +0 -2
- data/test/multiverse/suites/agent_only/logging_test.rb +1 -1
- data/test/multiverse/suites/agent_only/marshaling_test.rb +5 -3
- data/test/multiverse/suites/agent_only/rename_rule_test.rb +2 -0
- data/test/multiverse/suites/agent_only/set_transaction_name_test.rb +96 -0
- data/test/multiverse/suites/agent_only/testing_app.rb +1 -0
- data/test/multiverse/suites/rails/error_tracing_test.rb +17 -29
- data/test/multiverse/suites/rails/queue_time_test.rb +8 -2
- data/test/multiverse/suites/rails/view_instrumentation_test.rb +6 -3
- data/test/multiverse/suites/resque/instrumentation_test.rb +1 -1
- data/test/multiverse/suites/sinatra/sinatra_error_tracing_test.rb +8 -0
- data/test/new_relic/agent/agent/connect_test.rb +2 -1
- data/test/new_relic/agent/agent/start_test.rb +0 -10
- data/test/new_relic/agent/agent_logger_test.rb +15 -0
- data/test/new_relic/agent/agent_test_controller.rb +6 -2
- data/test/new_relic/agent/agent_test_controller_test.rb +20 -69
- data/test/new_relic/agent/autostart_test.rb +67 -0
- data/test/new_relic/agent/browser_monitoring_test.rb +60 -38
- data/test/new_relic/agent/configuration/environment_source_test.rb +19 -17
- data/test/new_relic/agent/cross_app_monitor_test.rb +8 -0
- data/test/new_relic/agent/error_collector/notice_error_test.rb +0 -5
- data/test/new_relic/agent/error_collector_test.rb +8 -9
- data/test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb +228 -0
- data/test/new_relic/agent/instrumentation/action_view_subscriber_test.rb +18 -34
- data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +5 -5
- data/test/new_relic/agent/instrumentation/active_record_subscriber_test.rb +8 -9
- data/test/new_relic/agent/instrumentation/browser_monitoring_timings_test.rb +1 -1
- data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +24 -38
- data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +126 -178
- data/test/new_relic/agent/instrumentation/rack_test.rb +1 -1
- data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +135 -151
- data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +153 -81
- data/test/new_relic/agent/method_tracer_test.rb +42 -33
- data/test/new_relic/agent/mock_scope_listener.rb +4 -4
- data/test/new_relic/agent/pipe_channel_manager_test.rb +4 -2
- data/test/new_relic/agent/rpm_agent_test.rb +86 -89
- data/test/new_relic/agent/sql_sampler_test.rb +18 -19
- data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +5 -8
- data/test/new_relic/agent/stats_engine/metric_stats_test.rb +20 -8
- data/test/new_relic/agent/stats_engine/samplers_test.rb +31 -14
- data/test/new_relic/agent/stats_engine_test.rb +53 -60
- data/test/new_relic/agent/thread_test.rb +7 -7
- data/test/new_relic/agent/transaction/pop_test.rb +96 -0
- data/test/new_relic/agent/transaction_info_test.rb +6 -17
- data/test/new_relic/agent/transaction_sample_builder_test.rb +10 -18
- data/test/new_relic/agent/transaction_sampler_test.rb +80 -75
- data/test/new_relic/agent/{instrumentation/metric_frame_test.rb → transaction_test.rb} +76 -42
- data/test/new_relic/agent/uri_util_test.rb +75 -0
- data/test/new_relic/agent_test.rb +115 -9
- data/test/test_helper.rb +138 -9
- data.tar.gz.sig +0 -0
- metadata +37 -74
- metadata.gz.sig +0 -0
- data/lib/new_relic/agent/instrumentation/metric_frame/pop.rb +0 -84
- data/lib/new_relic/agent/instrumentation/metric_frame.rb +0 -353
- data/test/new_relic/agent/instrumentation/metric_frame/pop_test.rb +0 -175
- data/test/test_contexts.rb +0 -34
@@ -48,7 +48,7 @@ module NewRelic
|
|
48
48
|
clazz.extend ClassMethods
|
49
49
|
clazz.extend InstanceMethods
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
# Defines modules used at instrumentation runtime, to do the
|
53
53
|
# actual tracing of time spent
|
54
54
|
module InstanceMethods
|
@@ -93,27 +93,29 @@ module NewRelic
|
|
93
93
|
end
|
94
94
|
|
95
95
|
alias trace_method_execution_no_scope trace_execution_unscoped #:nodoc:
|
96
|
-
|
96
|
+
|
97
97
|
# Refactored out of the previous trace_execution_scoped
|
98
98
|
# method, most methods in this module relate to code used in
|
99
99
|
# the #trace_execution_scoped method in this module
|
100
100
|
module TraceExecutionScoped
|
101
|
+
extend self
|
102
|
+
|
101
103
|
# Shorthand to return the NewRelic::Agent.instance
|
102
104
|
def agent_instance
|
103
105
|
NewRelic::Agent.instance
|
104
106
|
end
|
105
|
-
|
107
|
+
|
106
108
|
# Shorthand to return the status of tracing
|
107
109
|
def traced?
|
108
110
|
NewRelic::Agent.is_execution_traced?
|
109
111
|
end
|
110
|
-
|
112
|
+
|
111
113
|
# Tracing is disabled if we are not in a traced context and
|
112
114
|
# no force option is supplied
|
113
115
|
def trace_disabled?(options)
|
114
116
|
!(traced? || options[:force])
|
115
117
|
end
|
116
|
-
|
118
|
+
|
117
119
|
# Shorthand to return the current statistics engine
|
118
120
|
def stat_engine
|
119
121
|
agent_instance.stats_engine
|
@@ -128,24 +130,13 @@ module NewRelic
|
|
128
130
|
stat_engine.get_stats_no_scope(name)
|
129
131
|
end
|
130
132
|
|
131
|
-
def get_metric_specs(first_name, other_names, scope, options)
|
132
|
-
specs = other_names.map { |name| NewRelic::MetricSpec.new(name) }
|
133
|
-
if options[:metric]
|
134
|
-
if scope && scope != first_name
|
135
|
-
specs << NewRelic::MetricSpec.new(first_name, scope)
|
136
|
-
end
|
137
|
-
specs << NewRelic::MetricSpec.new(first_name) unless options[:scoped_metric_only]
|
138
|
-
end
|
139
|
-
specs
|
140
|
-
end
|
141
|
-
|
142
133
|
# Helper for setting a hash key if the hash key is nil,
|
143
134
|
# instead of the default ||= behavior which sets if it is
|
144
135
|
# false as well
|
145
136
|
def set_if_nil(hash, key)
|
146
137
|
hash[key] = true if hash[key].nil?
|
147
138
|
end
|
148
|
-
|
139
|
+
|
149
140
|
# delegates to #agent_instance to push a trace execution
|
150
141
|
# flag, only if execution of this metric is forced.
|
151
142
|
#
|
@@ -154,7 +145,7 @@ module NewRelic
|
|
154
145
|
def push_flag!(forced)
|
155
146
|
agent_instance.push_trace_execution_flag(true) if forced
|
156
147
|
end
|
157
|
-
|
148
|
+
|
158
149
|
# delegates to #agent_instance to pop the trace execution
|
159
150
|
# flag, only if execution of this metric is
|
160
151
|
# forced. otherwise this is taken care of for us
|
@@ -165,15 +156,15 @@ module NewRelic
|
|
165
156
|
def pop_flag!(forced)
|
166
157
|
agent_instance.pop_trace_execution_flag if forced
|
167
158
|
end
|
168
|
-
|
159
|
+
|
169
160
|
# helper for logging errors to the newrelic_agent.log
|
170
161
|
# properly. Logs the error at error level
|
171
|
-
def log_errors(code_area
|
162
|
+
def log_errors(code_area)
|
172
163
|
yield
|
173
164
|
rescue => e
|
174
|
-
::NewRelic::Agent.logger.error("Caught exception in #{code_area}.
|
165
|
+
::NewRelic::Agent.logger.error("Caught exception in #{code_area}.", e)
|
175
166
|
end
|
176
|
-
|
167
|
+
|
177
168
|
# provides the header for our traced execution scoped
|
178
169
|
# method - gets the initial time, sets the tracing flag if
|
179
170
|
# needed, and pushes the scope onto the metric stack
|
@@ -181,16 +172,66 @@ module NewRelic
|
|
181
172
|
# the scope so that we can check for it later, to maintain
|
182
173
|
# sanity. If the scope stack becomes unbalanced, this
|
183
174
|
# transaction loses meaning.
|
184
|
-
def trace_execution_scoped_header(
|
185
|
-
scope = log_errors("trace_execution_scoped header"
|
175
|
+
def trace_execution_scoped_header(options, t0=Time.now.to_f)
|
176
|
+
scope = log_errors("trace_execution_scoped header") do
|
186
177
|
push_flag!(options[:force])
|
187
|
-
scope = stat_engine.push_scope(
|
178
|
+
scope = stat_engine.push_scope(:method_tracer, t0, options[:deduct_call_time_from_parent])
|
188
179
|
end
|
189
180
|
# needed in case we have an error, above, to always return
|
190
181
|
# the start time.
|
191
182
|
[t0, scope]
|
192
183
|
end
|
193
|
-
|
184
|
+
|
185
|
+
def metrics_for_current_transaction(first_name, other_names, options)
|
186
|
+
metrics = []
|
187
|
+
|
188
|
+
if !options[:scoped_metric_only]
|
189
|
+
metrics += other_names.map { |n| NewRelic::MetricSpec.new(n) }
|
190
|
+
end
|
191
|
+
|
192
|
+
if options[:metric]
|
193
|
+
if !options[:scoped_metric_only]
|
194
|
+
metrics << NewRelic::MetricSpec.new(first_name)
|
195
|
+
end
|
196
|
+
if in_transaction? && !options[:transaction]
|
197
|
+
metrics << NewRelic::MetricSpec.new(first_name, StatsEngine::MetricStats::SCOPE_PLACEHOLDER)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
metrics
|
202
|
+
end
|
203
|
+
|
204
|
+
def in_transaction?
|
205
|
+
NewRelic::Agent::Transaction.in_transaction?
|
206
|
+
end
|
207
|
+
|
208
|
+
def has_parent?
|
209
|
+
NewRelic::Agent::Transaction.current &&
|
210
|
+
NewRelic::Agent::Transaction.current.has_parent?
|
211
|
+
end
|
212
|
+
|
213
|
+
def metrics_for_parent_transaction(first_name, options)
|
214
|
+
if has_parent? && options[:metric] && options[:transaction]
|
215
|
+
[NewRelic::MetricSpec.new(first_name, StatsEngine::MetricStats::SCOPE_PLACEHOLDER)]
|
216
|
+
else
|
217
|
+
[]
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def record_metrics(first_name, other_names, duration, exclusive, options)
|
222
|
+
metrics = metrics_for_current_transaction(first_name, other_names, options)
|
223
|
+
stat_engine.record_metrics(metrics) do |stat|
|
224
|
+
stat.record_data_point(duration, exclusive)
|
225
|
+
end
|
226
|
+
|
227
|
+
parent_metrics = metrics_for_parent_transaction(first_name, options)
|
228
|
+
parent_metrics.each do |metric|
|
229
|
+
stat_engine.transaction_stats_stack[-2].record(metric) do |stats|
|
230
|
+
stats.record_data_point(duration, exclusive)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
194
235
|
# Handles the end of the #trace_execution_scoped method -
|
195
236
|
# calculating the time taken, popping the tracing flag if
|
196
237
|
# needed, deducting time taken by children, and tracing the
|
@@ -199,17 +240,14 @@ module NewRelic
|
|
199
240
|
# this method fails safely if the header does not manage to
|
200
241
|
# push the scope onto the stack - it simply does not trace
|
201
242
|
# any metrics.
|
202
|
-
def trace_execution_scoped_footer(t0, first_name,
|
203
|
-
log_errors("trace_method_execution footer"
|
204
|
-
|
205
|
-
|
206
|
-
pop_flag!(forced)
|
243
|
+
def trace_execution_scoped_footer(t0, first_name, metric_names, expected_scope, scope, options, t1=Time.now.to_f)
|
244
|
+
log_errors("trace_method_execution footer") do
|
245
|
+
pop_flag!(options[:force])
|
207
246
|
if expected_scope
|
208
|
-
scope = stat_engine.pop_scope(expected_scope,
|
247
|
+
scope = stat_engine.pop_scope(expected_scope, first_name, t1)
|
248
|
+
duration = t1 - t0
|
209
249
|
exclusive = duration - scope.children_time
|
210
|
-
|
211
|
-
stat.record_data_point(duration, exclusive)
|
212
|
-
end
|
250
|
+
record_metrics(first_name, metric_names, duration, exclusive, options)
|
213
251
|
end
|
214
252
|
end
|
215
253
|
end
|
@@ -230,12 +268,11 @@ module NewRelic
|
|
230
268
|
metric_names = Array(metric_names)
|
231
269
|
first_name = metric_names.shift
|
232
270
|
scope = stat_engine.scope_name
|
233
|
-
start_time, expected_scope = trace_execution_scoped_header(
|
234
|
-
begin
|
271
|
+
start_time, expected_scope = trace_execution_scoped_header(options)
|
272
|
+
begin
|
235
273
|
yield
|
236
274
|
ensure
|
237
|
-
|
238
|
-
trace_execution_scoped_footer(start_time, first_name, metric_specs, expected_scope, options[:force])
|
275
|
+
trace_execution_scoped_footer(start_time, first_name, metric_names, expected_scope, scope, options)
|
239
276
|
end
|
240
277
|
end
|
241
278
|
|
@@ -243,20 +280,20 @@ module NewRelic
|
|
243
280
|
include TraceExecutionScoped
|
244
281
|
|
245
282
|
end
|
246
|
-
|
283
|
+
|
247
284
|
# Defines methods used at the class level, for adding instrumentation
|
248
285
|
module ClassMethods
|
249
286
|
# contains methods refactored out of the #add_method_tracer method
|
250
287
|
module AddMethodTracer
|
251
288
|
ALLOWED_KEYS = [:force, :metric, :push_scope, :deduct_call_time_from_parent, :code_header, :code_footer, :scoped_metric_only].freeze
|
252
|
-
|
289
|
+
|
253
290
|
# used to verify that the keys passed to
|
254
291
|
# NewRelic::Agent::MethodTracer::ClassMethods#add_method_tracer
|
255
292
|
# are valid. Returns a list of keys that were unexpected
|
256
293
|
def unrecognized_keys(expected, given)
|
257
294
|
given.keys - expected
|
258
295
|
end
|
259
|
-
|
296
|
+
|
260
297
|
# used to verify that the keys passed to
|
261
298
|
# NewRelic::Agent::MethodTracer::ClassMethods#add_method_tracer
|
262
299
|
# are valid. checks the expected list against the list
|
@@ -264,7 +301,7 @@ module NewRelic
|
|
264
301
|
def any_unrecognized_keys?(expected, given)
|
265
302
|
unrecognized_keys(expected, given).any?
|
266
303
|
end
|
267
|
-
|
304
|
+
|
268
305
|
# raises an error when the
|
269
306
|
# NewRelic::Agent::MethodTracer::ClassMethods#add_method_tracer
|
270
307
|
# method is called with improper keys. This aids in
|
@@ -274,7 +311,7 @@ module NewRelic
|
|
274
311
|
raise "Unrecognized options in add_method_tracer_call: #{unrecognized_keys(ALLOWED_KEYS, options).join(', ')}"
|
275
312
|
end
|
276
313
|
end
|
277
|
-
|
314
|
+
|
278
315
|
# Sets the options for deducting call time from
|
279
316
|
# parents. This defaults to true if we are recording a metric, but
|
280
317
|
# can be overridden by the user if desired.
|
@@ -284,7 +321,7 @@ module NewRelic
|
|
284
321
|
def set_deduct_call_time_based_on_metric(options)
|
285
322
|
{:deduct_call_time_from_parent => !!options[:metric]}.merge(options)
|
286
323
|
end
|
287
|
-
|
324
|
+
|
288
325
|
# validity checking - add_method_tracer must receive either
|
289
326
|
# push scope or metric, or else it would record no
|
290
327
|
# data. Raises an error if this is the case
|
@@ -295,7 +332,7 @@ module NewRelic
|
|
295
332
|
end
|
296
333
|
|
297
334
|
DEFAULT_SETTINGS = {:push_scope => true, :metric => true, :force => false, :code_header => "", :code_footer => "", :scoped_metric_only => false}.freeze
|
298
|
-
|
335
|
+
|
299
336
|
# Checks the provided options to make sure that they make
|
300
337
|
# sense. Raises an error if the options are incorrect to
|
301
338
|
# assist with debugging, so that errors occur at class
|
@@ -315,7 +352,7 @@ module NewRelic
|
|
315
352
|
def default_metric_name_code(method_name)
|
316
353
|
"Custom/#{self.name}/#{method_name.to_s}"
|
317
354
|
end
|
318
|
-
|
355
|
+
|
319
356
|
# Checks to see if the method we are attempting to trace
|
320
357
|
# actually exists or not. #add_method_tracer can't do
|
321
358
|
# anything if the method doesn't exist.
|
@@ -324,7 +361,7 @@ module NewRelic
|
|
324
361
|
::NewRelic::Agent.logger.error("Did not trace #{self.name}##{method_name} because that method does not exist") unless exists
|
325
362
|
exists
|
326
363
|
end
|
327
|
-
|
364
|
+
|
328
365
|
# Checks to see if we have already traced a method with a
|
329
366
|
# given metric by checking to see if the traced method
|
330
367
|
# exists. Warns the user if methods are being double-traced
|
@@ -334,7 +371,7 @@ module NewRelic
|
|
334
371
|
::NewRelic::Agent.logger.error("Attempt to trace a method twice with the same metric: Method = #{method_name}, Metric Name = #{metric_name_code}") if exists
|
335
372
|
exists
|
336
373
|
end
|
337
|
-
|
374
|
+
|
338
375
|
# Returns a code snippet to be eval'd that skips tracing
|
339
376
|
# when the agent is not tracing execution. turns
|
340
377
|
# instrumentation into effectively one method call overhead
|
@@ -344,7 +381,7 @@ module NewRelic
|
|
344
381
|
"return #{_untraced_method_name(method_name, metric_name_code)}(*args, &block) unless NewRelic::Agent.is_execution_traced?\n"
|
345
382
|
end.to_s + options[:code_header].to_s
|
346
383
|
end
|
347
|
-
|
384
|
+
|
348
385
|
# returns an eval-able string that contains the traced
|
349
386
|
# method code used if the agent is not creating a scope for
|
350
387
|
# use in scoped metrics.
|
@@ -382,7 +419,7 @@ module NewRelic
|
|
382
419
|
result
|
383
420
|
end"
|
384
421
|
end
|
385
|
-
|
422
|
+
|
386
423
|
# Decides which code snippet we should be eval'ing in this
|
387
424
|
# context, based on the options.
|
388
425
|
def code_to_eval(method_name, metric_name_code, options)
|
@@ -397,7 +434,7 @@ module NewRelic
|
|
397
434
|
include AddMethodTracer
|
398
435
|
|
399
436
|
|
400
|
-
|
437
|
+
|
401
438
|
# Add a method tracer to the specified method.
|
402
439
|
#
|
403
440
|
# === Common Options
|
@@ -500,22 +537,22 @@ module NewRelic
|
|
500
537
|
end
|
501
538
|
end
|
502
539
|
private
|
503
|
-
|
540
|
+
|
504
541
|
# given a method and a metric, this method returns the
|
505
542
|
# untraced alias of the method name
|
506
543
|
def _untraced_method_name(method_name, metric_name)
|
507
544
|
"#{_sanitize_name(method_name)}_without_trace_#{_sanitize_name(metric_name)}"
|
508
545
|
end
|
509
|
-
|
546
|
+
|
510
547
|
# given a method and a metric, this method returns the traced
|
511
548
|
# alias of the method name
|
512
549
|
def _traced_method_name(method_name, metric_name)
|
513
550
|
"#{_sanitize_name(method_name)}_with_trace_#{_sanitize_name(metric_name)}"
|
514
551
|
end
|
515
|
-
|
552
|
+
|
516
553
|
# makes sure that method names do not contain characters that
|
517
554
|
# might break the interpreter, for example ! or ? characters
|
518
|
-
# that are not allowed in the middle of method names
|
555
|
+
# that are not allowed in the middle of method names
|
519
556
|
def _sanitize_name(name)
|
520
557
|
name.to_s.tr_s('^a-zA-Z0-9', '_')
|
521
558
|
end
|
@@ -11,6 +11,11 @@ module NewRelic
|
|
11
11
|
attr_reader :last_time
|
12
12
|
def initialize
|
13
13
|
super :cpu
|
14
|
+
@processor_count = NewRelic::Agent::SystemInfo.processor_count
|
15
|
+
if @processor_count.nil?
|
16
|
+
NewRelic::Agent.logger.warn("Failed to determine processor count, assuming 1")
|
17
|
+
@processor_count = 1
|
18
|
+
end
|
14
19
|
poll
|
15
20
|
end
|
16
21
|
|
@@ -42,7 +47,7 @@ module NewRelic
|
|
42
47
|
if @last_time
|
43
48
|
elapsed = now - @last_time
|
44
49
|
return if elapsed < 1 # Causing some kind of math underflow
|
45
|
-
|
50
|
+
|
46
51
|
usertime = t.utime - @last_utime
|
47
52
|
systemtime = t.stime - @last_stime
|
48
53
|
|
@@ -50,10 +55,10 @@ module NewRelic
|
|
50
55
|
record_usertime(usertime) if usertime >= 0
|
51
56
|
|
52
57
|
# Calculate the true utilization by taking cpu times and dividing by
|
53
|
-
# elapsed time X
|
58
|
+
# elapsed time X processor_count.
|
54
59
|
|
55
|
-
record_user_util(usertime / (elapsed *
|
56
|
-
record_system_util(systemtime / (elapsed *
|
60
|
+
record_user_util(usertime / (elapsed * @processor_count))
|
61
|
+
record_system_util(systemtime / (elapsed * @processor_count))
|
57
62
|
end
|
58
63
|
@last_utime = t.utime
|
59
64
|
@last_stime = t.stime
|
@@ -30,7 +30,7 @@ module NewRelic
|
|
30
30
|
@sql_traces = {}
|
31
31
|
clear_transaction_data
|
32
32
|
|
33
|
-
# This lock is used to synchronize access to
|
33
|
+
# This lock is used to synchronize access to @sql_traces
|
34
34
|
# and related variables. It can become necessary on JRuby or
|
35
35
|
# any 'honest-to-god'-multithreaded system
|
36
36
|
@samples_lock = Mutex.new
|
@@ -43,12 +43,12 @@ module NewRelic
|
|
43
43
|
Agent.config[:'transaction_tracer.enabled']
|
44
44
|
end
|
45
45
|
|
46
|
-
def notice_transaction(
|
46
|
+
def notice_transaction(uri=nil, params={})
|
47
47
|
if NewRelic::Agent.instance.transaction_sampler.builder
|
48
48
|
guid = NewRelic::Agent.instance.transaction_sampler.builder.sample.guid
|
49
49
|
end
|
50
50
|
if Agent.config[:'slow_sql.enabled'] && transaction_data
|
51
|
-
transaction_data.set_transaction_info(
|
51
|
+
transaction_data.set_transaction_info(uri, params, guid)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -69,8 +69,9 @@ module NewRelic
|
|
69
69
|
end
|
70
70
|
|
71
71
|
# This is called when we are done with the transaction.
|
72
|
-
def notice_scope_empty(time=Time.now)
|
72
|
+
def notice_scope_empty(name, time=Time.now)
|
73
73
|
data = transaction_data
|
74
|
+
data.set_transaction_name(name)
|
74
75
|
clear_transaction_data
|
75
76
|
|
76
77
|
if data.sql_data.size > 0
|
@@ -145,12 +146,15 @@ module NewRelic
|
|
145
146
|
@sql_data = []
|
146
147
|
end
|
147
148
|
|
148
|
-
def set_transaction_info(
|
149
|
-
@path = path
|
149
|
+
def set_transaction_info(uri, params, guid)
|
150
150
|
@uri = uri
|
151
151
|
@params = params
|
152
152
|
@guid = guid
|
153
153
|
end
|
154
|
+
|
155
|
+
def set_transaction_name(name)
|
156
|
+
@path = name
|
157
|
+
end
|
154
158
|
end
|
155
159
|
|
156
160
|
class SlowSql
|
@@ -9,6 +9,8 @@ module NewRelic
|
|
9
9
|
class StatsEngine
|
10
10
|
# Handles methods related to actual Metric collection
|
11
11
|
module MetricStats
|
12
|
+
SCOPE_PLACEHOLDER = :__SCOPE__
|
13
|
+
|
12
14
|
# Lookup and write to the named metric in a single call.
|
13
15
|
#
|
14
16
|
# This method is thead-safe, and is preferred to the lookup / modify
|
@@ -16,13 +18,19 @@ module NewRelic
|
|
16
18
|
def record_metrics(metric_names_or_specs, value=nil, options={}, &blk)
|
17
19
|
defaults = {
|
18
20
|
:scoped => false,
|
19
|
-
:scope =>
|
21
|
+
:scope => in_transaction? ? SCOPE_PLACEHOLDER : nil
|
20
22
|
}
|
21
23
|
options = defaults.merge(options)
|
24
|
+
|
22
25
|
effective_scope = options[:scoped] && options[:scope]
|
23
26
|
specs = coerce_to_metric_spec_array(metric_names_or_specs, effective_scope)
|
24
|
-
|
25
|
-
|
27
|
+
|
28
|
+
if in_transaction?
|
29
|
+
transaction_stats_hash.record(specs, value, &blk)
|
30
|
+
else
|
31
|
+
with_stats_lock do
|
32
|
+
@stats_hash.record(specs, value, &blk)
|
33
|
+
end
|
26
34
|
end
|
27
35
|
end
|
28
36
|
|
@@ -155,6 +163,14 @@ module NewRelic
|
|
155
163
|
@stats_hash.keys.map { |spec| spec.to_s }
|
156
164
|
end
|
157
165
|
end
|
166
|
+
|
167
|
+
def metric_specs
|
168
|
+
with_stats_lock { @stats_hash.keys }
|
169
|
+
end
|
170
|
+
|
171
|
+
def in_transaction?
|
172
|
+
!!transaction_stats_hash
|
173
|
+
end
|
158
174
|
end
|
159
175
|
end
|
160
176
|
end
|
@@ -2,17 +2,17 @@
|
|
2
2
|
# This file is distributed under New Relic's license terms.
|
3
3
|
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
4
|
|
5
|
-
# -*- coding: utf-8 -*-
|
6
5
|
module NewRelic
|
7
6
|
module Agent
|
8
7
|
class StatsEngine
|
9
8
|
# A simple stack element that tracks the current name and length
|
10
9
|
# of the executing stack
|
11
10
|
class ScopeStackElement
|
12
|
-
attr_reader :deduct_call_time_from_parent
|
13
|
-
attr_accessor :name, :children_time
|
14
|
-
def initialize(
|
15
|
-
@
|
11
|
+
attr_reader :deduct_call_time_from_parent, :tag
|
12
|
+
attr_accessor :name, :start_time, :children_time
|
13
|
+
def initialize(tag, start_time, deduct_call_time)
|
14
|
+
@tag = tag
|
15
|
+
@start_time = start_time
|
16
16
|
@deduct_call_time_from_parent = deduct_call_time
|
17
17
|
@children_time = 0
|
18
18
|
end
|
@@ -50,29 +50,38 @@ module Agent
|
|
50
50
|
|
51
51
|
# Pushes a scope onto the transaction stack - this generates a
|
52
52
|
# TransactionSample::Segment at the end of transaction execution
|
53
|
-
|
53
|
+
# The generated segment will not be named until the corresponding
|
54
|
+
# pop_scope call is made.
|
55
|
+
# +tag+ should be a Symbol, and is only used for debugging purposes to
|
56
|
+
# identify this scope if the stack gets corrupted.
|
57
|
+
def push_scope(tag, time = Time.now.to_f, deduct_call_time_from_parent = true)
|
54
58
|
stack = scope_stack
|
55
|
-
@transaction_sampler.notice_push_scope
|
56
|
-
scope = ScopeStackElement.new(
|
59
|
+
@transaction_sampler.notice_push_scope(time) if sampler_enabled?
|
60
|
+
scope = ScopeStackElement.new(tag, time, deduct_call_time_from_parent)
|
57
61
|
stack.push scope
|
58
62
|
scope
|
59
63
|
end
|
60
64
|
|
61
65
|
# Pops a scope off the transaction stack - this updates the
|
62
66
|
# transaction sampler that we've finished execution of a traced method
|
63
|
-
|
67
|
+
# +expected_scope+ should be the ScopeStackElement that was returned by
|
68
|
+
# the corresponding push_scope call.
|
69
|
+
# +name+ is the name that will be applied to the generated transaction
|
70
|
+
# trace segment.
|
71
|
+
def pop_scope(expected_scope, name, time=Time.now.to_f)
|
64
72
|
stack = scope_stack
|
65
73
|
scope = stack.pop
|
66
|
-
fail "unbalanced pop from blame stack, got #{scope ? scope.
|
74
|
+
fail "unbalanced pop from blame stack, got #{scope ? scope.tag : 'nil'}, expected #{expected_scope ? expected_scope.tag : 'nil'}" if scope != expected_scope
|
67
75
|
|
68
76
|
if !stack.empty?
|
69
77
|
if scope.deduct_call_time_from_parent
|
70
|
-
stack.last.children_time +=
|
78
|
+
stack.last.children_time += (time - scope.start_time)
|
71
79
|
else
|
72
80
|
stack.last.children_time += scope.children_time
|
73
81
|
end
|
74
82
|
end
|
75
|
-
@transaction_sampler.notice_pop_scope(
|
83
|
+
@transaction_sampler.notice_pop_scope(name, time) if sampler_enabled?
|
84
|
+
scope.name = name
|
76
85
|
scope
|
77
86
|
end
|
78
87
|
|
@@ -80,17 +89,6 @@ module Agent
|
|
80
89
|
@transaction_sampler && Agent.config[:'transaction_tracer.enabled']
|
81
90
|
end
|
82
91
|
|
83
|
-
# Rename the segment associated with the last pushed scope to +new_name+.
|
84
|
-
def rename_scope_segment( new_name )
|
85
|
-
self.peek_scope.name = new_name
|
86
|
-
@transaction_sampler.rename_scope_segment( new_name ) if sampler_enabled?
|
87
|
-
end
|
88
|
-
|
89
|
-
# Returns the latest ScopeStackElement
|
90
|
-
def peek_scope
|
91
|
-
scope_stack.last
|
92
|
-
end
|
93
|
-
|
94
92
|
# set the name of the transaction for the current thread, which will be used
|
95
93
|
# to define the scope of all traced methods called on this thread until the
|
96
94
|
# scope stack is empty.
|
@@ -109,11 +107,8 @@ module Agent
|
|
109
107
|
end
|
110
108
|
|
111
109
|
# Start a new transaction, unless one is already in progress
|
112
|
-
def start_transaction
|
113
|
-
|
114
|
-
self.scope_name = name if name
|
115
|
-
NewRelic::Agent.instance.events.notify(:start_transaction, name)
|
116
|
-
GCProfiler.init
|
110
|
+
def start_transaction
|
111
|
+
NewRelic::Agent.instance.events.notify(:start_transaction)
|
117
112
|
end
|
118
113
|
|
119
114
|
# Try to clean up gracefully, otherwise we leave things hanging around on thread locals.
|
@@ -121,11 +116,6 @@ module Agent
|
|
121
116
|
# and is ignored.
|
122
117
|
#
|
123
118
|
def end_transaction
|
124
|
-
elapsed = GCProfiler.capture
|
125
|
-
if @transaction_sampler && @transaction_sampler.last_sample
|
126
|
-
@transaction_sampler.last_sample.params[:custom_params] ||= {}
|
127
|
-
@transaction_sampler.last_sample.params[:custom_params][:gc_time] = elapsed
|
128
|
-
end
|
129
119
|
stack = scope_stack
|
130
120
|
|
131
121
|
if stack && stack.empty?
|
@@ -134,12 +124,41 @@ module Agent
|
|
134
124
|
end
|
135
125
|
end
|
136
126
|
|
137
|
-
|
127
|
+
def transaction_stats_hash
|
128
|
+
transaction_stats_stack.last
|
129
|
+
end
|
130
|
+
|
131
|
+
def push_transaction_stats
|
132
|
+
transaction_stats_stack << StatsHash.new
|
133
|
+
end
|
134
|
+
|
135
|
+
def pop_transaction_stats(transaction_name)
|
136
|
+
Thread::current[:newrelic_scope_stack] ||= []
|
137
|
+
stats = transaction_stats_stack.pop
|
138
|
+
merge!(apply_scopes(stats, transaction_name)) if stats
|
139
|
+
stats
|
140
|
+
end
|
141
|
+
|
142
|
+
def apply_scopes(stats_hash, resolved_name)
|
143
|
+
new_stats = StatsHash.new
|
144
|
+
stats_hash.each do |spec, stats|
|
145
|
+
if spec.scope != '' &&
|
146
|
+
spec.scope.to_sym == StatsEngine::SCOPE_PLACEHOLDER
|
147
|
+
spec.scope = resolved_name
|
148
|
+
end
|
149
|
+
new_stats[spec] = stats
|
150
|
+
end
|
151
|
+
return new_stats
|
152
|
+
end
|
138
153
|
|
139
154
|
# Returns the current scope stack, memoized to a thread local variable
|
140
155
|
def scope_stack
|
141
156
|
Thread::current[:newrelic_scope_stack] ||= []
|
142
157
|
end
|
158
|
+
|
159
|
+
def transaction_stats_stack
|
160
|
+
Thread.current[:newrelic_transaction_stack] ||= []
|
161
|
+
end
|
143
162
|
end
|
144
163
|
end
|
145
164
|
end
|