newrelic_rpm 3.6.3.111 → 3.6.4.113.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +14 -1
- data/lib/new_relic/agent/agent.rb +14 -6
- data/lib/new_relic/agent/configuration/defaults.rb +8 -3
- data/lib/new_relic/agent/configuration/manager.rb +32 -1
- data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +2 -2
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +7 -11
- data/lib/new_relic/agent/instrumentation/resque.rb +2 -1
- data/lib/new_relic/agent/method_tracer.rb +4 -6
- data/lib/new_relic/agent/pipe_service.rb +4 -0
- data/lib/new_relic/agent/request_sampler.rb +46 -1
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +11 -10
- data/lib/new_relic/agent/stats_engine/stats_hash.rb +12 -0
- data/lib/new_relic/agent/stats_engine/transactions.rb +1 -28
- data/lib/new_relic/agent/thread_profiler.rb +3 -3
- data/lib/new_relic/agent/transaction.rb +29 -15
- data/lib/new_relic/build.rb +2 -2
- data/lib/new_relic/noticed_error.rb +28 -12
- data/lib/new_relic/version.rb +1 -1
- data/test/agent_helper.rb +25 -0
- data/test/multiverse/suites/agent_only/key_transactions_test.rb +11 -11
- data/test/multiverse/suites/rails/request_statistics_test.rb +14 -20
- data/test/multiverse/suites/resque/Envfile +0 -1
- data/test/multiverse/suites/resque/config/newrelic.yml +1 -1
- data/test/multiverse/suites/resque/instrumentation_test.rb +16 -107
- data/test/new_relic/agent/agent/connect_test.rb +3 -3
- data/test/new_relic/agent/agent/start_test.rb +2 -0
- data/test/new_relic/agent/configuration/manager_test.rb +10 -0
- data/test/new_relic/agent/error_collector_test.rb +9 -9
- data/test/new_relic/agent/method_tracer_test.rb +8 -1
- data/test/new_relic/agent/request_sampler_test.rb +10 -0
- data/test/new_relic/agent/stats_engine/metric_stats_test.rb +40 -4
- data/test/new_relic/agent/transaction_test.rb +23 -0
- data/test/new_relic/noticed_error_test.rb +84 -2
- data/test/test_helper.rb +2 -16
- data.tar.gz.sig +0 -0
- metadata +11 -6
- metadata.gz.sig +0 -0
data/CHANGELOG
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
# New Relic Ruby Agent Release Notes #
|
2
2
|
|
3
|
-
## v3.6.
|
3
|
+
## v3.6.4 ##
|
4
|
+
|
5
|
+
* Exception Whitelist
|
6
|
+
|
7
|
+
We've improved exception message handling for applications running in
|
8
|
+
high security mode. Enabling 'high_security' now removes exception messages
|
9
|
+
entirely rather than simply obfuscating any SQL.
|
10
|
+
|
11
|
+
By default this feature affects all exceptions, though you can configure a
|
12
|
+
whitelist of exceptions whose messages should be left intact.
|
13
|
+
|
14
|
+
More details: https://newrelic.com/docs/ruby/ruby-agent-configuration
|
15
|
+
|
16
|
+
## 3.6.3 ##
|
4
17
|
|
5
18
|
* Better Sinatra Support
|
6
19
|
|
@@ -45,8 +45,9 @@ module NewRelic
|
|
45
45
|
@request_sampler = NewRelic::Agent::RequestSampler.new(@events)
|
46
46
|
@harvest_samplers = NewRelic::Agent::SamplerCollection.new(@events)
|
47
47
|
|
48
|
-
@connect_state
|
49
|
-
@connect_attempts
|
48
|
+
@connect_state = :pending
|
49
|
+
@connect_attempts = 0
|
50
|
+
@environment_report = nil
|
50
51
|
|
51
52
|
@last_harvest_time = Time.now
|
52
53
|
@obfuscator = lambda {|sql| NewRelic::Agent::Database.default_sql_obfuscator(sql) }
|
@@ -203,8 +204,7 @@ module NewRelic
|
|
203
204
|
# Clear out stats that are left over from parent process
|
204
205
|
reset_stats
|
205
206
|
|
206
|
-
|
207
|
-
# I'm pretty sure we're not also forking new instances.
|
207
|
+
generate_environment_report
|
208
208
|
start_worker_thread(options)
|
209
209
|
end
|
210
210
|
|
@@ -463,6 +463,7 @@ module NewRelic
|
|
463
463
|
def check_config_and_start_agent
|
464
464
|
return unless monitoring? && has_correct_license_key?
|
465
465
|
return if using_forking_dispatcher?
|
466
|
+
generate_environment_report
|
466
467
|
connect_in_foreground if Agent.config[:sync_startup]
|
467
468
|
start_worker_thread
|
468
469
|
install_exit_handler
|
@@ -711,8 +712,15 @@ module NewRelic
|
|
711
712
|
shutdown
|
712
713
|
end
|
713
714
|
|
715
|
+
def generate_environment_report
|
716
|
+
@environment_report = environment_for_connect
|
717
|
+
end
|
718
|
+
|
714
719
|
# Checks whether we should send environment info, and if so,
|
715
|
-
# returns the snapshot from the local environment
|
720
|
+
# returns the snapshot from the local environment.
|
721
|
+
# Generating the EnvironmentReport has the potential to trigger
|
722
|
+
# require calls in Rails environments, so this method should only
|
723
|
+
# be called synchronously from on the main thread.
|
716
724
|
def environment_for_connect
|
717
725
|
Agent.config[:send_environment_info] ? Array(EnvironmentReport.new) : []
|
718
726
|
end
|
@@ -726,7 +734,7 @@ module NewRelic
|
|
726
734
|
:app_name => Agent.config.app_names,
|
727
735
|
:language => 'ruby',
|
728
736
|
:agent_version => NewRelic::VERSION::STRING,
|
729
|
-
:environment =>
|
737
|
+
:environment => @environment_report,
|
730
738
|
:settings => Agent.config.to_collector_hash,
|
731
739
|
}
|
732
740
|
end
|
@@ -57,9 +57,9 @@ module NewRelic
|
|
57
57
|
::NewRelic::Agent::Autostart.agent_should_start?
|
58
58
|
end,
|
59
59
|
# Don't autostart the agent if we're in IRB or Rails console.
|
60
|
-
# This config option accepts a comma
|
60
|
+
# This config option accepts a comma separated list of constants.
|
61
61
|
:'autostart.blacklisted_constants' => 'Rails::Console',
|
62
|
-
# Comma
|
62
|
+
# Comma separated list of executables that you don't want to trigger
|
63
63
|
# agents start. e.g. 'rake,my_ruby_script.rb'
|
64
64
|
:'autostart.blacklisted_executables' => 'irb',
|
65
65
|
:'autostart.blacklisted_rake_tasks' => 'about,assets:clean,assets:clobber,assets:environment,assets:precompile,db:create,db:drop,db:fixtures:load,db:migrate,db:migrate:status,db:rollback,db:schema:cache:clear,db:schema:cache:dump,db:schema:dump,db:schema:load,db:seed,db:setup,db:structure:dump,db:version,doc:app,log:clear,middleware,notes,notes:custom,rails:template,rails:update,routes,secret,spec,spec:controllers,spec:helpers,spec:models,spec:rcov,stats,test,test:all,test:all:db,test:recent,test:single,test:uncommitted,time:zones:all,tmp:clear,tmp:create',
|
@@ -69,6 +69,11 @@ module NewRelic
|
|
69
69
|
:monitor_daemons => false,
|
70
70
|
:multi_homed => false,
|
71
71
|
:high_security => false,
|
72
|
+
# Strip messages from all exceptions that are not specified in the whitelist.
|
73
|
+
:'strip_exception_messages.enabled' => Proc.new { self[:high_security] },
|
74
|
+
# Comma separated list of exceptions that should show messages when
|
75
|
+
# strip_exception_messages is enabled (e.g. 'NewException, RelicException').
|
76
|
+
:'strip_exception_messages.whitelist' => '',
|
72
77
|
|
73
78
|
:host => 'collector.newrelic.com',
|
74
79
|
:api_host => 'rpm.newrelic.com',
|
@@ -143,7 +148,7 @@ module NewRelic
|
|
143
148
|
|
144
149
|
:marshaller => Proc.new { NewRelic::Agent::NewRelicService::JsonMarshaller.is_supported? ? 'json' : 'pruby' },
|
145
150
|
|
146
|
-
:'request_sampler.enabled' =>
|
151
|
+
:'request_sampler.enabled' => true,
|
147
152
|
:'request_sampler.sample_rate_ms' => 50
|
148
153
|
].freeze
|
149
154
|
end
|
@@ -15,12 +15,20 @@ module NewRelic
|
|
15
15
|
class Manager
|
16
16
|
extend Forwardable
|
17
17
|
def_delegators :@cache, :[], :has_key?
|
18
|
-
attr_reader :config_stack
|
18
|
+
attr_reader :config_stack, :stripped_exceptions_whitelist
|
19
19
|
|
20
20
|
def initialize
|
21
21
|
@config_stack = [ EnvironmentSource.new, DEFAULTS ]
|
22
22
|
@cache = Hash.new {|hash,key| hash[key] = self.fetch(key) }
|
23
23
|
@callbacks = Hash.new {|hash,key| hash[key] = [] }
|
24
|
+
|
25
|
+
register_callback(:'strip_exception_messages.whitelist') do |whitelist|
|
26
|
+
if whitelist
|
27
|
+
@stripped_exceptions_whitelist = parse_constant_list(whitelist).compact
|
28
|
+
else
|
29
|
+
@stripped_exceptions_whitelist = []
|
30
|
+
end
|
31
|
+
end
|
24
32
|
end
|
25
33
|
|
26
34
|
def apply_config(source, level=0)
|
@@ -146,6 +154,29 @@ module NewRelic
|
|
146
154
|
"Updating config (#{direction}) from #{source.class}. Results:",
|
147
155
|
flattened.inspect)
|
148
156
|
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def parse_constant_list(list)
|
161
|
+
list.split(/\s*,\s*/).map do |class_name|
|
162
|
+
const = constantize(class_name)
|
163
|
+
|
164
|
+
unless const
|
165
|
+
NewRelic::Agent.logger.warn "Configuration referenced undefined constant: #{class_name}"
|
166
|
+
end
|
167
|
+
|
168
|
+
const
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def constantize(class_name)
|
173
|
+
namespaces = class_name.split('::')
|
174
|
+
|
175
|
+
namespaces.inject(Object) do |namespace, name|
|
176
|
+
return unless namespace
|
177
|
+
namespace.const_get(name) if namespace.const_defined?(name)
|
178
|
+
end
|
179
|
+
end
|
149
180
|
end
|
150
181
|
end
|
151
182
|
end
|
@@ -72,8 +72,8 @@ module NewRelic
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def record_metric_on_parent_transaction(metric, time)
|
75
|
-
Agent.
|
76
|
-
|
75
|
+
txn = NewRelic::Agent::Transaction.current
|
76
|
+
txn.parent.stats_hash.record(metric, time)
|
77
77
|
end
|
78
78
|
|
79
79
|
def record_apdex(event)
|
@@ -331,20 +331,16 @@ module NewRelic
|
|
331
331
|
end
|
332
332
|
|
333
333
|
ensure
|
334
|
+
end_time = Time.now
|
335
|
+
|
334
336
|
txn.freeze_name
|
335
|
-
|
336
|
-
txn_name =
|
337
|
+
metric_names = Array(recorded_metrics(txn))
|
338
|
+
txn_name = metric_names.shift
|
337
339
|
|
338
|
-
metric_names
|
339
|
-
|
340
|
-
|
341
|
-
end_time = Time.now
|
342
|
-
NewRelic::Agent::MethodTracer::InstanceMethods::TraceExecutionScoped.trace_execution_scoped_footer(txn.start_time.to_f, first_name, metric_names, expected_scope, scope, options, end_time.to_f)
|
343
|
-
NewRelic::Agent::BusyCalculator.dispatcher_finish
|
344
|
-
# Look for a transaction in the thread local and process it.
|
345
|
-
# Clear the thread local when finished to ensure it only gets called once.
|
340
|
+
NewRelic::Agent::MethodTracer::InstanceMethods::TraceExecutionScoped.trace_execution_scoped_footer(txn.start_time.to_f, txn_name, metric_names, expected_scope, options, end_time.to_f)
|
341
|
+
NewRelic::Agent::BusyCalculator.dispatcher_finish(end_time)
|
342
|
+
txn.record_apdex(txn_name, end_time) unless ignore_apdex?
|
346
343
|
txn = Transaction.stop(txn_name, end_time)
|
347
|
-
txn.record_apdex(txn_name) unless ignore_apdex?
|
348
344
|
|
349
345
|
NewRelic::Agent::TransactionInfo.get.ignore_end_user = true if ignore_enduser?
|
350
346
|
end
|
@@ -34,7 +34,8 @@ DependencyDetection.defer do
|
|
34
34
|
yield(*args)
|
35
35
|
end
|
36
36
|
ensure
|
37
|
-
NewRelic::Agent.shutdown if NewRelic::LanguageSupport.can_fork?
|
37
|
+
NewRelic::Agent.shutdown if NewRelic::LanguageSupport.can_fork? &&
|
38
|
+
(!Resque.respond_to?(:inline) || !Resque.inline)
|
38
39
|
end
|
39
40
|
end
|
40
41
|
end
|
@@ -206,8 +206,7 @@ module NewRelic
|
|
206
206
|
end
|
207
207
|
|
208
208
|
def has_parent?
|
209
|
-
NewRelic::Agent::Transaction.
|
210
|
-
NewRelic::Agent::Transaction.current.has_parent?
|
209
|
+
!NewRelic::Agent::Transaction.parent.nil?
|
211
210
|
end
|
212
211
|
|
213
212
|
def metrics_for_parent_transaction(first_name, options)
|
@@ -226,7 +225,7 @@ module NewRelic
|
|
226
225
|
|
227
226
|
parent_metrics = metrics_for_parent_transaction(first_name, options)
|
228
227
|
parent_metrics.each do |metric|
|
229
|
-
|
228
|
+
NewRelic::Agent::Transaction.parent.stats_hash.record(metric) do |stats|
|
230
229
|
stats.record_data_point(duration, exclusive)
|
231
230
|
end
|
232
231
|
end
|
@@ -240,7 +239,7 @@ module NewRelic
|
|
240
239
|
# this method fails safely if the header does not manage to
|
241
240
|
# push the scope onto the stack - it simply does not trace
|
242
241
|
# any metrics.
|
243
|
-
def trace_execution_scoped_footer(t0, first_name, metric_names, expected_scope,
|
242
|
+
def trace_execution_scoped_footer(t0, first_name, metric_names, expected_scope, options, t1=Time.now.to_f)
|
244
243
|
log_errors("trace_method_execution footer") do
|
245
244
|
pop_flag!(options[:force])
|
246
245
|
if expected_scope
|
@@ -267,12 +266,11 @@ module NewRelic
|
|
267
266
|
set_if_nil(options, :deduct_call_time_from_parent)
|
268
267
|
metric_names = Array(metric_names)
|
269
268
|
first_name = metric_names.shift
|
270
|
-
scope = stat_engine.scope_name
|
271
269
|
start_time, expected_scope = trace_execution_scoped_header(options)
|
272
270
|
begin
|
273
271
|
yield
|
274
272
|
ensure
|
275
|
-
trace_execution_scoped_footer(start_time, first_name, metric_names, expected_scope,
|
273
|
+
trace_execution_scoped_footer(start_time, first_name, metric_names, expected_scope, options)
|
276
274
|
end
|
277
275
|
end
|
278
276
|
|
@@ -22,6 +22,11 @@ class NewRelic::Agent::RequestSampler
|
|
22
22
|
# :TODO: Get this from the agent instead?
|
23
23
|
DEFAULT_REPORT_FREQUENCY = 60
|
24
24
|
|
25
|
+
# Regardless of whether #throttle is successfully called, we will store
|
26
|
+
# at most this many harvest-cycles worth of samples total, to avoid unbounded
|
27
|
+
# memory growth when there's a low-level failure talking to the collector.
|
28
|
+
MAX_FAILED_REPORT_RETENTION = 10
|
29
|
+
|
25
30
|
# The namespace and keys of config values
|
26
31
|
CONFIG_NAMESPACE = 'request_sampler'
|
27
32
|
SAMPLE_RATE_KEY = "#{CONFIG_NAMESPACE}.sample_rate_ms".to_sym
|
@@ -46,8 +51,13 @@ class NewRelic::Agent::RequestSampler
|
|
46
51
|
@sample_rate_ms = DEFAULT_SAMPLE_RATE_MS
|
47
52
|
@normal_sample_rate_ms = @sample_rate_ms
|
48
53
|
@last_sample_taken = nil
|
49
|
-
@last_harvest = nil
|
50
54
|
@samples = []
|
55
|
+
@notified_max_samples = false
|
56
|
+
|
57
|
+
@sample_count = 0
|
58
|
+
@request_count = 0
|
59
|
+
@sample_count_total = 0
|
60
|
+
@request_count_total = 0
|
51
61
|
|
52
62
|
event_listener.subscribe( :transaction_finished, &method(:on_transaction_finished) )
|
53
63
|
self.register_config_callbacks
|
@@ -80,13 +90,33 @@ class NewRelic::Agent::RequestSampler
|
|
80
90
|
def reset
|
81
91
|
NewRelic::Agent.logger.debug "Resetting RequestSampler"
|
82
92
|
|
93
|
+
request_count = nil
|
94
|
+
sample_count = nil
|
95
|
+
|
83
96
|
self.synchronize do
|
97
|
+
sample_count = @samples.size
|
98
|
+
request_count = @request_count
|
99
|
+
@request_count = 0
|
84
100
|
@samples.clear
|
85
101
|
@sample_rate_ms = @normal_sample_rate_ms
|
86
102
|
@last_sample_taken = Time.now
|
103
|
+
@notified_max_samples = false
|
87
104
|
end
|
105
|
+
|
106
|
+
record_sampling_rate(request_count, sample_count) if @enabled
|
88
107
|
end
|
89
108
|
|
109
|
+
def record_sampling_rate(request_count, sample_count)
|
110
|
+
@request_count_total += request_count
|
111
|
+
@sample_count_total += sample_count
|
112
|
+
|
113
|
+
NewRelic::Agent.logger.debug("Sampled #{sample_count} / #{request_count} (%.1f %%) requests this cycle" % (sample_count.to_f / request_count * 100.0))
|
114
|
+
NewRelic::Agent.logger.debug("Sampled #{@sample_count_total} / #{@request_count_total} (%.1f %%) requests since startup" % (@sample_count_total.to_f / @request_count_total * 100.0))
|
115
|
+
|
116
|
+
engine = NewRelic::Agent.instance.stats_engine
|
117
|
+
engine.record_supportability_metric_count("RequestSampler/requests", request_count)
|
118
|
+
engine.record_supportability_metric_count("RequestSampler/samples", sample_count)
|
119
|
+
end
|
90
120
|
|
91
121
|
#
|
92
122
|
# :group: Event handlers
|
@@ -103,6 +133,8 @@ class NewRelic::Agent::RequestSampler
|
|
103
133
|
end
|
104
134
|
|
105
135
|
@normal_sample_rate_ms = rate_ms
|
136
|
+
@max_samples = calculate_max_samples
|
137
|
+
NewRelic::Agent.logger.debug "RequestSampler max_samples set to #{@max_samples}"
|
106
138
|
self.reset
|
107
139
|
end
|
108
140
|
|
@@ -140,6 +172,7 @@ class NewRelic::Agent::RequestSampler
|
|
140
172
|
# This method is synchronized.
|
141
173
|
def <<( sample )
|
142
174
|
self.synchronize do
|
175
|
+
@request_count += 1
|
143
176
|
self.add_sample( sample ) if should_sample?
|
144
177
|
end
|
145
178
|
|
@@ -176,10 +209,22 @@ class NewRelic::Agent::RequestSampler
|
|
176
209
|
protected
|
177
210
|
#########
|
178
211
|
|
212
|
+
def calculate_max_samples
|
213
|
+
max_samples_per_harvest = (DEFAULT_REPORT_FREQUENCY * 1000.0) / @normal_sample_rate_ms
|
214
|
+
max_samples_per_harvest * MAX_FAILED_REPORT_RETENTION
|
215
|
+
end
|
216
|
+
|
179
217
|
# Returns +true+ if a sample added now should be kept based on the sample
|
180
218
|
# frequency.
|
181
219
|
def should_sample?
|
182
220
|
return false unless @last_sample_taken
|
221
|
+
if @samples.size >= @max_samples
|
222
|
+
unless @notified_max_samples
|
223
|
+
NewRelic::Agent.logger.warn("Reached maximum of #{@max_samples} samples, ceasing collection")
|
224
|
+
@notified_max_samples = true
|
225
|
+
end
|
226
|
+
return false
|
227
|
+
end
|
183
228
|
return ((Time.now - @last_sample_taken) * 1000).ceil >= @sample_rate_ms
|
184
229
|
end
|
185
230
|
|
@@ -74,27 +74,28 @@ module NewRelic
|
|
74
74
|
end
|
75
75
|
|
76
76
|
# Helper method for timing supportability metrics
|
77
|
-
def
|
77
|
+
def record_supportability_metric_timed(metric)
|
78
78
|
start_time = Time.now
|
79
79
|
yield
|
80
|
-
end_time = Time.now
|
81
|
-
duration = (end_time - start_time).to_f
|
82
80
|
ensure
|
83
|
-
|
81
|
+
duration = (Time.now - start_time).to_f
|
82
|
+
record_supportability_metric(metric, duration)
|
84
83
|
end
|
85
84
|
|
86
85
|
# Helper for recording a straight value into the count
|
87
|
-
def
|
88
|
-
|
86
|
+
def record_supportability_metric_count(metric, value)
|
87
|
+
record_supportability_metric(metric) do |stat|
|
89
88
|
stat.call_count = value
|
90
89
|
end
|
91
90
|
end
|
92
91
|
|
93
92
|
# Helper method for recording supportability metrics consistently
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
yield stat
|
93
|
+
def record_supportability_metric(metric, value=nil)
|
94
|
+
real_name = "Supportability/#{metric}"
|
95
|
+
if block_given?
|
96
|
+
record_metrics(real_name) { |stat| yield stat }
|
97
|
+
else
|
98
|
+
record_metrics(real_name, value)
|
98
99
|
end
|
99
100
|
end
|
100
101
|
|
@@ -57,6 +57,18 @@ module NewRelic
|
|
57
57
|
end
|
58
58
|
self
|
59
59
|
end
|
60
|
+
|
61
|
+
def resolve_scopes(resolved_scope)
|
62
|
+
new_stats = self.class.new
|
63
|
+
self.each do |spec, stats|
|
64
|
+
if spec.scope != '' &&
|
65
|
+
spec.scope.to_sym == StatsEngine::SCOPE_PLACEHOLDER
|
66
|
+
spec.scope = resolved_scope
|
67
|
+
end
|
68
|
+
new_stats[spec] = stats
|
69
|
+
end
|
70
|
+
return new_stats
|
71
|
+
end
|
60
72
|
end
|
61
73
|
end
|
62
74
|
end
|
@@ -121,40 +121,13 @@ module Agent
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def transaction_stats_hash
|
124
|
-
|
125
|
-
end
|
126
|
-
|
127
|
-
def push_transaction_stats
|
128
|
-
transaction_stats_stack << StatsHash.new
|
129
|
-
end
|
130
|
-
|
131
|
-
def pop_transaction_stats(transaction_name)
|
132
|
-
Thread::current[:newrelic_scope_stack] ||= []
|
133
|
-
stats = transaction_stats_stack.pop
|
134
|
-
merge!(apply_scopes(stats, transaction_name)) if stats
|
135
|
-
stats
|
136
|
-
end
|
137
|
-
|
138
|
-
def apply_scopes(stats_hash, resolved_name)
|
139
|
-
new_stats = StatsHash.new
|
140
|
-
stats_hash.each do |spec, stats|
|
141
|
-
if spec.scope != '' &&
|
142
|
-
spec.scope.to_sym == StatsEngine::SCOPE_PLACEHOLDER
|
143
|
-
spec.scope = resolved_name
|
144
|
-
end
|
145
|
-
new_stats[spec] = stats
|
146
|
-
end
|
147
|
-
return new_stats
|
124
|
+
Transaction.current && Transaction.current.stats_hash
|
148
125
|
end
|
149
126
|
|
150
127
|
# Returns the current scope stack, memoized to a thread local variable
|
151
128
|
def scope_stack
|
152
129
|
Thread::current[:newrelic_scope_stack] ||= []
|
153
130
|
end
|
154
|
-
|
155
|
-
def transaction_stats_stack
|
156
|
-
Thread.current[:newrelic_transaction_stack] ||= []
|
157
|
-
end
|
158
131
|
end
|
159
132
|
end
|
160
133
|
end
|