newrelic_rpm 5.3.0.346 → 5.4.0.347
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/config.dot +2 -2
- data/lib/new_relic/agent.rb +13 -19
- data/lib/new_relic/agent/agent.rb +2 -11
- data/lib/new_relic/agent/configuration/default_source.rb +2 -2
- data/lib/new_relic/agent/configuration/server_source.rb +1 -0
- data/lib/new_relic/agent/database.rb +1 -1
- data/lib/new_relic/agent/javascript_instrumentor.rb +0 -3
- data/lib/new_relic/agent/new_relic_service.rb +6 -5
- data/lib/new_relic/agent/pipe_service.rb +4 -0
- data/lib/new_relic/agent/stats_engine.rb +1 -1
- data/lib/new_relic/agent/transaction.rb +3 -2
- data/lib/new_relic/agent/transaction_state.rb +2 -7
- data/lib/new_relic/agent/transaction_time_aggregator.rb +131 -0
- data/lib/new_relic/version.rb +1 -1
- metadata +3 -3
- data/lib/new_relic/agent/busy_calculator.rb +0 -117
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 979f362569155df00b770dba3907bd0eb894169f70b712085ae19d16852f53a7
|
4
|
+
data.tar.gz: b67e7d1c0cb1b5831f06d29c574f8efea5a95405a809b97c55add2e40d0cb101
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2974f98ab0c47eb9f1c0e2014fb4122138fe4d1bf4d5265f8d09d1583f7ee8fc3f9ab5cb86bf1dc7e31d8b44d5762e56c8e1edd421120151ae85e3e2e4bd854d
|
7
|
+
data.tar.gz: 7d16296c46075e1e84824afaeff67db60ea39e0b5b4032bbae9bbcbde8c6c5ca6dc6d40618c92cbf29391f0ba36876937e8d2444313d3805031827a7e8313607
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# New Relic Ruby Agent Release Notes #
|
2
2
|
|
3
|
+
## v5.4.0
|
4
|
+
|
5
|
+
* Capacity analysis for multi-threaded dispatchers
|
6
|
+
|
7
|
+
Metrics around capacity analysis did not previously account for multi-threaded
|
8
|
+
dispatchers, and consequently could result in capacities of over 100% being
|
9
|
+
recorded. This version now properly accounts for multi-threaded dispatchers.
|
10
|
+
|
11
|
+
* `NewRelic::Agent.disable_transaction_tracing` deprecated
|
12
|
+
|
13
|
+
`NewRelic::Agent.disable_transaction_tracing` has been deprecated. Users
|
14
|
+
are encouraged to use `NewRelic::Agent.disable_all_tracing` or
|
15
|
+
`NewRelic::Agent.ignore_transaction` instead.
|
16
|
+
|
17
|
+
* Bugfix for SQL over-obfuscation
|
18
|
+
|
19
|
+
A bug, introduced in v5.3.0, where SQL could be over-obfuscated for some
|
20
|
+
database adapters has been fixed.
|
21
|
+
|
22
|
+
* Bugfix for span event data in Resque processes
|
23
|
+
|
24
|
+
A bug where span events would not be sent from Resque processes due to a
|
25
|
+
missing endpoint has been fixed.
|
26
|
+
|
3
27
|
## v5.3.0 ##
|
4
28
|
|
5
29
|
* Distributed Tracing
|
data/config.dot
CHANGED
@@ -42,7 +42,7 @@ digraph AgentEnabled {
|
|
42
42
|
"[dispatcher]"
|
43
43
|
"[force_send]"
|
44
44
|
"[textmate]"
|
45
|
-
"[
|
45
|
+
"[max_payload_size_in_bytes]"
|
46
46
|
"[sync_startup]"
|
47
47
|
"[send_data_on_exit]"
|
48
48
|
"[high_security]"
|
@@ -223,7 +223,7 @@ digraph AgentEnabled {
|
|
223
223
|
"NewRelicService#initialize" -> "[timeout]"
|
224
224
|
"NewRelicService#initialize" -> "[license_key]"
|
225
225
|
"NewRelicService#initialize" -> "Control#server"
|
226
|
-
"NewRelicService#check_post_size" -> "[
|
226
|
+
"NewRelicService#check_post_size" -> "[max_payload_size_in_bytes]"
|
227
227
|
"NewRelicService#connect" -> "Control#server_from_host"
|
228
228
|
"NewRelicService#send_request" -> "Control#http_connection"
|
229
229
|
|
data/lib/new_relic/agent.rb
CHANGED
@@ -45,7 +45,6 @@ module NewRelic
|
|
45
45
|
require 'new_relic/agent/sql_sampler'
|
46
46
|
require 'new_relic/agent/commands/thread_profiler_session'
|
47
47
|
require 'new_relic/agent/error_collector'
|
48
|
-
require 'new_relic/agent/busy_calculator'
|
49
48
|
require 'new_relic/agent/sampler'
|
50
49
|
require 'new_relic/agent/database'
|
51
50
|
require 'new_relic/agent/datastores'
|
@@ -55,6 +54,7 @@ module NewRelic
|
|
55
54
|
require 'new_relic/agent/http_clients/uri_util'
|
56
55
|
require 'new_relic/agent/system_info'
|
57
56
|
require 'new_relic/agent/external'
|
57
|
+
require 'new_relic/agent/deprecator'
|
58
58
|
|
59
59
|
require 'new_relic/agent/instrumentation/controller_instrumentation'
|
60
60
|
|
@@ -206,11 +206,17 @@ module NewRelic
|
|
206
206
|
# This method is safe to use from any thread.
|
207
207
|
#
|
208
208
|
# @api public
|
209
|
+
|
210
|
+
SUPPORTABILITY_INCREMENT_METRIC = 'Supportability/API/increment_metric'.freeze
|
211
|
+
|
209
212
|
def increment_metric(metric_name, amount=1) #THREAD_LOCAL_ACCESS
|
210
213
|
return unless agent
|
211
|
-
|
212
|
-
|
213
|
-
agent.stats_engine.tl_record_unscoped_metrics(
|
214
|
+
if amount == 1
|
215
|
+
metrics = [metric_name, SUPPORTABILITY_INCREMENT_METRIC]
|
216
|
+
agent.stats_engine.tl_record_unscoped_metrics(metrics) {|stats| stats.increment_count}
|
217
|
+
else
|
218
|
+
agent.stats_engine.tl_record_unscoped_metrics(metric_name) {|stats| stats.increment_count(amount)}
|
219
|
+
agent.stats_engine.tl_record_unscoped_metrics(SUPPORTABILITY_INCREMENT_METRIC) {|stats| stats.increment_count}
|
214
220
|
end
|
215
221
|
end
|
216
222
|
|
@@ -504,16 +510,10 @@ module NewRelic
|
|
504
510
|
# @api public
|
505
511
|
#
|
506
512
|
def disable_transaction_tracing
|
513
|
+
Deprecator.deprecate :disable_transaction_tracing,
|
514
|
+
'disable_all_tracing or ignore_transaction'
|
507
515
|
record_api_supportability_metric(:disable_transaction_tracing)
|
508
|
-
|
509
|
-
return yield unless agent
|
510
|
-
|
511
|
-
state = agent.set_record_tt(false)
|
512
|
-
begin
|
513
|
-
yield
|
514
|
-
ensure
|
515
|
-
agent.set_record_tt(state)
|
516
|
-
end
|
516
|
+
yield
|
517
517
|
end
|
518
518
|
|
519
519
|
# This method sets the state of sql recording in the transaction
|
@@ -547,12 +547,6 @@ module NewRelic
|
|
547
547
|
NewRelic::Agent::TransactionState.tl_get.is_execution_traced?
|
548
548
|
end
|
549
549
|
|
550
|
-
# helper method to check the thread local to determine whether the
|
551
|
-
# transaction in progress is traced or not
|
552
|
-
def tl_is_transaction_traced?
|
553
|
-
NewRelic::Agent::TransactionState.tl_get.is_transaction_traced?
|
554
|
-
end
|
555
|
-
|
556
550
|
# helper method to check the thread local to determine whether sql
|
557
551
|
# is being recorded or not
|
558
552
|
def tl_is_sql_recorded?
|
@@ -268,16 +268,6 @@ module NewRelic
|
|
268
268
|
prev.nil? || prev
|
269
269
|
end
|
270
270
|
|
271
|
-
# Sets a thread local variable as to whether we should or
|
272
|
-
# should not record transaction traces in the current
|
273
|
-
# thread. Returns the previous value, if there is one
|
274
|
-
def set_record_tt(should_record) #THREAD_LOCAL_ACCESS
|
275
|
-
state = TransactionState.tl_get
|
276
|
-
prev = state.record_tt
|
277
|
-
state.record_tt = should_record
|
278
|
-
prev.nil? || prev
|
279
|
-
end
|
280
|
-
|
281
271
|
# Push flag indicating whether we should be tracing in this
|
282
272
|
# thread. This uses a stack which allows us to disable tracing
|
283
273
|
# children of a transaction without affecting the tracing of
|
@@ -934,6 +924,7 @@ module NewRelic
|
|
934
924
|
when :error_event_data then @error_collector.error_event_aggregator
|
935
925
|
when :analytic_event_data then transaction_event_aggregator
|
936
926
|
when :custom_event_data then @custom_event_aggregator
|
927
|
+
when :span_event_data then span_event_aggregator
|
937
928
|
when :sql_trace_data then @sql_sampler
|
938
929
|
end
|
939
930
|
end
|
@@ -1084,7 +1075,7 @@ module NewRelic
|
|
1084
1075
|
end
|
1085
1076
|
|
1086
1077
|
def harvest_and_send_timeslice_data
|
1087
|
-
NewRelic::Agent::
|
1078
|
+
NewRelic::Agent::TransactionTimeAggregator.harvest!
|
1088
1079
|
harvest_and_send_from_container(@stats_engine, :metric_data)
|
1089
1080
|
end
|
1090
1081
|
|
@@ -504,8 +504,8 @@ module NewRelic
|
|
504
504
|
:allowed_from_server => false,
|
505
505
|
:description => 'If <code>true</code>, enables the exit handler that sends data to the New Relic <a href="https://docs.newrelic.com/docs/apm/new-relic-apm/getting-started/glossary#collector">collector</a> before shutting down.'
|
506
506
|
},
|
507
|
-
:
|
508
|
-
:default =>
|
507
|
+
:max_payload_size_in_bytes => {
|
508
|
+
:default => 1000000,
|
509
509
|
:public => false,
|
510
510
|
:type => Integer,
|
511
511
|
:allowed_from_server => true,
|
@@ -65,9 +65,6 @@ module NewRelic
|
|
65
65
|
if !state.current_transaction
|
66
66
|
::NewRelic::Agent.logger.debug "Not in transaction. Skipping browser instrumentation."
|
67
67
|
false
|
68
|
-
elsif !state.is_transaction_traced?
|
69
|
-
::NewRelic::Agent.logger.debug "Transaction is not traced. Skipping browser instrumentation."
|
70
|
-
false
|
71
68
|
elsif !state.is_execution_traced?
|
72
69
|
::NewRelic::Agent.logger.debug "Execution is not traced. Skipping browser instrumentation."
|
73
70
|
false
|
@@ -193,7 +193,7 @@ module NewRelic
|
|
193
193
|
# We do not compress if content is smaller than 64kb. There are
|
194
194
|
# problems with bugs in Ruby in some versions that expose us
|
195
195
|
# to a risk of segfaults if we compress aggressively.
|
196
|
-
def compress_request_if_needed(data)
|
196
|
+
def compress_request_if_needed(data, endpoint)
|
197
197
|
encoding = 'identity'
|
198
198
|
if data.size > 64 * 1024
|
199
199
|
encoding = Agent.config[:compressed_content_encoding]
|
@@ -203,7 +203,7 @@ module NewRelic
|
|
203
203
|
Encoders::Compressed::Deflate.encode(data)
|
204
204
|
end
|
205
205
|
end
|
206
|
-
check_post_size(data)
|
206
|
+
check_post_size(data, endpoint)
|
207
207
|
[data, encoding]
|
208
208
|
end
|
209
209
|
|
@@ -412,7 +412,7 @@ module NewRelic
|
|
412
412
|
end
|
413
413
|
serialize_finish_ts = Time.now
|
414
414
|
|
415
|
-
data, encoding = compress_request_if_needed(data)
|
415
|
+
data, encoding = compress_request_if_needed(data, method)
|
416
416
|
size = data.size
|
417
417
|
|
418
418
|
uri = remote_method_uri(method)
|
@@ -471,9 +471,10 @@ module NewRelic
|
|
471
471
|
|
472
472
|
# Raises an UnrecoverableServerException if the post_string is longer
|
473
473
|
# than the limit configured in the control object
|
474
|
-
def check_post_size(post_string)
|
475
|
-
return if post_string.size < Agent.config[:
|
474
|
+
def check_post_size(post_string, endpoint)
|
475
|
+
return if post_string.size < Agent.config[:max_payload_size_in_bytes]
|
476
476
|
::NewRelic::Agent.logger.debug "Tried to send too much data: #{post_string.size} bytes"
|
477
|
+
NewRelic::Agent.increment_metric("Supportability/Agent/Collector/#{endpoint}/MaxPayloadSizeLimit")
|
477
478
|
raise UnrecoverableServerException.new('413 Request Entity Too Large')
|
478
479
|
end
|
479
480
|
|
@@ -11,6 +11,7 @@ require 'new_relic/agent/transaction/request_attributes'
|
|
11
11
|
require 'new_relic/agent/transaction/tracing'
|
12
12
|
require 'new_relic/agent/transaction/distributed_tracing'
|
13
13
|
require 'new_relic/agent/cross_app_tracing'
|
14
|
+
require 'new_relic/agent/transaction_time_aggregator'
|
14
15
|
|
15
16
|
module NewRelic
|
16
17
|
module Agent
|
@@ -446,7 +447,7 @@ module NewRelic
|
|
446
447
|
|
447
448
|
sql_sampler.on_start_transaction(state, start_time, request_path)
|
448
449
|
NewRelic::Agent.instance.events.notify(:start_transaction)
|
449
|
-
NewRelic::Agent::
|
450
|
+
NewRelic::Agent::TransactionTimeAggregator.transaction_start(start_time)
|
450
451
|
|
451
452
|
ignore! if user_defined_rules_ignore?
|
452
453
|
|
@@ -542,7 +543,7 @@ module NewRelic
|
|
542
543
|
|
543
544
|
outermost_frame.finish
|
544
545
|
|
545
|
-
NewRelic::Agent::
|
546
|
+
NewRelic::Agent::TransactionTimeAggregator.transaction_stop(end_time)
|
546
547
|
|
547
548
|
commit!(state, end_time, outermost_frame.name) unless @ignore_this_transaction
|
548
549
|
end
|
@@ -34,13 +34,12 @@ module NewRelic
|
|
34
34
|
def initialize
|
35
35
|
@untraced = []
|
36
36
|
@current_transaction = nil
|
37
|
-
@record_tt = nil
|
38
37
|
@record_sql = nil
|
39
38
|
end
|
40
39
|
|
41
40
|
# This starts the timer for the transaction.
|
42
41
|
def reset(transaction=nil)
|
43
|
-
# We purposefully don't reset @untraced
|
42
|
+
# We purposefully don't reset @untraced or @record_sql
|
44
43
|
# since those are managed by NewRelic::Agent.disable_* calls explicitly
|
45
44
|
# and (more importantly) outside the scope of a transaction
|
46
45
|
|
@@ -125,11 +124,7 @@ module NewRelic
|
|
125
124
|
end
|
126
125
|
|
127
126
|
# TT's and SQL
|
128
|
-
attr_accessor :
|
129
|
-
|
130
|
-
def is_transaction_traced?
|
131
|
-
@record_tt != false
|
132
|
-
end
|
127
|
+
attr_accessor :record_sql
|
133
128
|
|
134
129
|
def is_sql_recorded?
|
135
130
|
@record_sql != false
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
require 'objspace'
|
6
|
+
|
7
|
+
# This module powers the Busy calculation for the Capacity report in
|
8
|
+
# APM (https://rpm.newrelic.com/accounts/.../applications/.../optimize/capacity_analysis).
|
9
|
+
#
|
10
|
+
# total time spent in transactions this harvest across all threads
|
11
|
+
# Busy time = ------------------------------------------------------------------------
|
12
|
+
# (elapsed time for this harvest cycle) * (# threads that had transactions)
|
13
|
+
#
|
14
|
+
module NewRelic
|
15
|
+
module Agent
|
16
|
+
module TransactionTimeAggregator
|
17
|
+
TransactionStats = Struct.new :transaction_started_at, :elapsed_transaction_time
|
18
|
+
|
19
|
+
@lock = Mutex.new
|
20
|
+
@harvest_cycle_started_at = Time.now
|
21
|
+
|
22
|
+
@stats = Hash.new do |h, k|
|
23
|
+
h[k] = TransactionStats.new nil, 0.0
|
24
|
+
end
|
25
|
+
|
26
|
+
def reset!(now = Time.now)
|
27
|
+
@harvest_cycle_started_at = now
|
28
|
+
@stats.clear
|
29
|
+
end
|
30
|
+
|
31
|
+
def transaction_start(now = Time.now)
|
32
|
+
@lock.synchronize do
|
33
|
+
set_transaction_start_time now
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def transaction_stop(now = Time.now)
|
38
|
+
@lock.synchronize do
|
39
|
+
record_elapsed_transaction_time_until now
|
40
|
+
set_transaction_start_time nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
INSTANCE_BUSY_METRIC = 'Instance/Busy'.freeze
|
45
|
+
|
46
|
+
def harvest!(now = Time.now)
|
47
|
+
active_threads = 0
|
48
|
+
result = @lock.synchronize do
|
49
|
+
# Sum up the transaction times spent in each thread
|
50
|
+
elapsed_transaction_time = @stats.inject(0.0) do |total, (thread_id, entry)|
|
51
|
+
total + transaction_time_in_thread(thread_id, entry, now)
|
52
|
+
end
|
53
|
+
|
54
|
+
active_threads = @stats.size
|
55
|
+
elapsed_harvest_time = (now - @harvest_cycle_started_at) * active_threads
|
56
|
+
@harvest_cycle_started_at = now
|
57
|
+
|
58
|
+
# Clear out the stats for all threads, _except_ the live ones
|
59
|
+
# that have transactions still open (we'll count the rest of
|
60
|
+
# those in a future harvest)
|
61
|
+
@stats.keep_if do |thread_id, _|
|
62
|
+
in_transaction?(thread_id) && thread_is_alive?(thread_id)
|
63
|
+
end
|
64
|
+
|
65
|
+
if elapsed_harvest_time > 0.0
|
66
|
+
elapsed_transaction_time / elapsed_harvest_time
|
67
|
+
else
|
68
|
+
0.0
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if Agent.config[:report_instance_busy]
|
73
|
+
NewRelic::Agent.record_metric(INSTANCE_BUSY_METRIC, result)
|
74
|
+
end
|
75
|
+
|
76
|
+
result
|
77
|
+
end
|
78
|
+
|
79
|
+
module_function :reset!,
|
80
|
+
:transaction_start,
|
81
|
+
:transaction_stop,
|
82
|
+
:harvest!
|
83
|
+
|
84
|
+
class <<self
|
85
|
+
private
|
86
|
+
|
87
|
+
def record_elapsed_transaction_time_until(timestamp, thread_id: current_thread)
|
88
|
+
@stats[thread_id].elapsed_transaction_time +=
|
89
|
+
(timestamp - (@stats[thread_id].transaction_started_at || 0.0))
|
90
|
+
end
|
91
|
+
|
92
|
+
def in_transaction?(thread_id = current_thread)
|
93
|
+
!!@stats[thread_id].transaction_started_at
|
94
|
+
end
|
95
|
+
|
96
|
+
def current_thread
|
97
|
+
Thread.current.object_id
|
98
|
+
end
|
99
|
+
|
100
|
+
def thread_is_alive?(thread_id)
|
101
|
+
thread = ObjectSpace._id2ref(thread_id)
|
102
|
+
thread && thread.alive?
|
103
|
+
rescue StandardError
|
104
|
+
false
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_transaction_start_time(timestamp)
|
108
|
+
@stats[current_thread].transaction_started_at = timestamp
|
109
|
+
end
|
110
|
+
|
111
|
+
def split_transaction_at_harvest(now, thread_id: nil)
|
112
|
+
raise ArgumentError, 'thread_id required' unless thread_id
|
113
|
+
@stats[thread_id].transaction_started_at = now
|
114
|
+
@stats[thread_id].elapsed_transaction_time = 0.0
|
115
|
+
end
|
116
|
+
|
117
|
+
def transaction_time_in_thread thread_id, entry, now
|
118
|
+
return entry.elapsed_transaction_time unless in_transaction? thread_id
|
119
|
+
|
120
|
+
# Count the portion of the transaction that's elapsed so far,...
|
121
|
+
elapsed = record_elapsed_transaction_time_until now, thread_id: thread_id
|
122
|
+
|
123
|
+
# ...then readjust the transaction start time to the next harvest
|
124
|
+
split_transaction_at_harvest now, thread_id: thread_id
|
125
|
+
|
126
|
+
elapsed
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/new_relic/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: newrelic_rpm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.4.0.347
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Wear
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2018-
|
14
|
+
date: 2018-09-04 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rake
|
@@ -202,7 +202,6 @@ files:
|
|
202
202
|
- lib/new_relic/agent/attribute_processing.rb
|
203
203
|
- lib/new_relic/agent/audit_logger.rb
|
204
204
|
- lib/new_relic/agent/autostart.rb
|
205
|
-
- lib/new_relic/agent/busy_calculator.rb
|
206
205
|
- lib/new_relic/agent/chained_call.rb
|
207
206
|
- lib/new_relic/agent/commands/agent_command.rb
|
208
207
|
- lib/new_relic/agent/commands/agent_command_router.rb
|
@@ -393,6 +392,7 @@ files:
|
|
393
392
|
- lib/new_relic/agent/transaction_metrics.rb
|
394
393
|
- lib/new_relic/agent/transaction_sampler.rb
|
395
394
|
- lib/new_relic/agent/transaction_state.rb
|
395
|
+
- lib/new_relic/agent/transaction_time_aggregator.rb
|
396
396
|
- lib/new_relic/agent/transaction_timings.rb
|
397
397
|
- lib/new_relic/agent/utilization/aws.rb
|
398
398
|
- lib/new_relic/agent/utilization/azure.rb
|
@@ -1,117 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
# This file is distributed under New Relic's license terms.
|
3
|
-
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
-
|
5
|
-
require 'new_relic/agent/transaction_state'
|
6
|
-
|
7
|
-
module NewRelic
|
8
|
-
module Agent
|
9
|
-
# This module supports calculation of actual time spent processing requests over the course of
|
10
|
-
# one harvest period. It's similar to what you would get if you just added up all the
|
11
|
-
# execution times of controller calls, however that will be inaccurate when requests
|
12
|
-
# span the minute boundaries. This module manages accounting of requests not yet
|
13
|
-
# completed.
|
14
|
-
#
|
15
|
-
# Calls are re-entrant. All start calls must be paired with finish
|
16
|
-
# calls, or a reset call.
|
17
|
-
module BusyCalculator
|
18
|
-
|
19
|
-
extend self
|
20
|
-
|
21
|
-
# For testability, add accessors:
|
22
|
-
attr_reader :harvest_start, :accumulator
|
23
|
-
|
24
|
-
# sets up busy calculations based on the start and end of
|
25
|
-
# transactions - used for a rough estimate of what percentage of
|
26
|
-
# wall clock time is spent processing requests
|
27
|
-
def dispatcher_start(time) #THREAD_LOCAL_ACCESS
|
28
|
-
state = TransactionState.tl_get
|
29
|
-
state.busy_entries ||= 0
|
30
|
-
callers = state.busy_entries += 1
|
31
|
-
return if callers > 1
|
32
|
-
@lock.synchronize do
|
33
|
-
@entrypoint_stack.push time
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# called when a transaction finishes, to add time to the
|
38
|
-
# instance variable accumulator. this is harvested when we send
|
39
|
-
# data to the server
|
40
|
-
def dispatcher_finish(end_time = nil) #THREAD_LOCAL_ACCESS
|
41
|
-
state = TransactionState.tl_get
|
42
|
-
# If #dispatcher_start hasn't been called at least once, abort early
|
43
|
-
return unless state.busy_entries
|
44
|
-
|
45
|
-
end_time ||= time_now
|
46
|
-
callers = state.busy_entries -= 1
|
47
|
-
|
48
|
-
# Ignore nested calls
|
49
|
-
return if callers > 0
|
50
|
-
|
51
|
-
@lock.synchronize do
|
52
|
-
if @entrypoint_stack.empty?
|
53
|
-
::NewRelic::Agent.logger.warn("Stack underflow tracking dispatcher entry and exit!\n #{caller.join(" \n")}")
|
54
|
-
else
|
55
|
-
@accumulator += (end_time - @entrypoint_stack.pop).to_f
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# this returns the size of the entry point stack, which
|
61
|
-
# determines how many transactions are running
|
62
|
-
def busy_count
|
63
|
-
@entrypoint_stack.size
|
64
|
-
end
|
65
|
-
|
66
|
-
# Reset the state of the information accumulated by all threads,
|
67
|
-
# but only reset the recursion counter for this thread.
|
68
|
-
def reset #THREAD_LOCAL_ACCESS
|
69
|
-
@entrypoint_stack = []
|
70
|
-
TransactionState.tl_get.busy_entries = 0
|
71
|
-
@lock ||= Mutex.new
|
72
|
-
@accumulator = 0
|
73
|
-
@harvest_start = time_now
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
# Called before uploading to to the server to collect current busy stats.
|
78
|
-
def harvest_busy
|
79
|
-
busy = 0
|
80
|
-
t0 = time_now
|
81
|
-
@lock.synchronize do
|
82
|
-
busy = accumulator
|
83
|
-
@accumulator = 0
|
84
|
-
|
85
|
-
# Walk through the stack and capture all times up to
|
86
|
-
# now for entrypoints
|
87
|
-
@entrypoint_stack.size.times do |frame|
|
88
|
-
busy += (t0 - @entrypoint_stack[frame]).to_f
|
89
|
-
@entrypoint_stack[frame] = t0
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
busy = 0.0 if busy < 0.0 # don't go below 0%
|
95
|
-
|
96
|
-
time_window = (t0 - harvest_start).to_f
|
97
|
-
time_window = 1.0 if time_window == 0.0 # protect against divide by zero
|
98
|
-
|
99
|
-
busy = busy / time_window
|
100
|
-
|
101
|
-
if Agent.config[:report_instance_busy]
|
102
|
-
NewRelic::Agent.record_metric('Instance/Busy', busy)
|
103
|
-
end
|
104
|
-
@harvest_start = t0
|
105
|
-
end
|
106
|
-
|
107
|
-
private
|
108
|
-
|
109
|
-
# so we can stub Time.now only for the BusyCalculator in tests
|
110
|
-
def time_now
|
111
|
-
Time.now
|
112
|
-
end
|
113
|
-
|
114
|
-
self.reset
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|