scout_apm 3.0.0.pre22 → 3.0.0.pre23
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +5 -0
- data/lib/scout_apm.rb +3 -0
- data/lib/scout_apm/agent_context.rb +4 -0
- data/lib/scout_apm/context.rb +9 -0
- data/lib/scout_apm/environment.rb +11 -3
- data/lib/scout_apm/extensions/config.rb +87 -0
- data/lib/scout_apm/extensions/transaction_callback_payload.rb +74 -0
- data/lib/scout_apm/instruments/active_record.rb +25 -4
- data/lib/scout_apm/layer_converters/allocation_metric_converter.rb +3 -1
- data/lib/scout_apm/layer_converters/database_converter.rb +2 -0
- data/lib/scout_apm/layer_converters/error_converter.rb +4 -2
- data/lib/scout_apm/layer_converters/histograms.rb +1 -0
- data/lib/scout_apm/layer_converters/job_converter.rb +1 -0
- data/lib/scout_apm/layer_converters/metric_converter.rb +1 -0
- data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +4 -2
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +1 -0
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +1 -0
- data/lib/scout_apm/reporting.rb +14 -10
- data/lib/scout_apm/tracked_request.rb +22 -15
- data/lib/scout_apm/version.rb +1 -1
- data/test/unit/extensions/periodic_callbacks_test.rb +58 -0
- data/test/unit/extensions/transaction_callbacks_test.rb +58 -0
- metadata +8 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64d1ac40c6dec93f6e7c59b358f8f696a1f53eee
|
4
|
+
data.tar.gz: 815f98467560d0c0b9c4257eafc1f45a9784246e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 439b25ab44127e59d906006f34e8c9efd94fa3bbafde58fbcfd1af34be1caf94b80d8ae84c6087af8e4de5e6866b5c03e609d971524e0cbc3a3aa0d2922f500b
|
7
|
+
data.tar.gz: b6c954e72a252f9dbc113ed12b659c69b9f3e19241a58395a01ff68725ad4bd0b96b2e0bb6857cd9950063469aaba76d03ff1d3a2d802ca14d218c7996dcc10c
|
data/CHANGELOG.markdown
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
* ScoutProf BETA
|
4
4
|
|
5
|
+
# 2.4.11
|
6
|
+
|
7
|
+
* Adds transaction + periodic reporting callback extension support
|
8
|
+
* Use Module#prepend if available for ActiveRecord `exec_query` instrument
|
9
|
+
|
5
10
|
# 2.4.10
|
6
11
|
|
7
12
|
* Improve ActiveRecord instrumentation across Rails 3.2+, and adding support
|
data/lib/scout_apm.rb
CHANGED
@@ -180,6 +180,9 @@ require 'scout_apm/agent/exit_handler'
|
|
180
180
|
require 'scout_apm/tasks/doctor'
|
181
181
|
require 'scout_apm/tasks/support'
|
182
182
|
|
183
|
+
require 'scout_apm/extensions/config'
|
184
|
+
require 'scout_apm/extensions/transaction_callback_payload'
|
185
|
+
|
183
186
|
if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR) && Rails::VERSION::MAJOR >= 3 && defined?(Rails::Railtie)
|
184
187
|
module ScoutApm
|
185
188
|
class Railtie < Rails::Railtie
|
@@ -1,5 +1,8 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
class AgentContext
|
3
|
+
|
4
|
+
attr_accessor :extensions
|
5
|
+
|
3
6
|
# Initially start up without attempting to load a configuration file. We
|
4
7
|
# need to be able to lookup configuration options like "application_root"
|
5
8
|
# which would then in turn influence where the yaml configuration file is
|
@@ -9,6 +12,7 @@ module ScoutApm
|
|
9
12
|
def initialize()
|
10
13
|
@logger = LoggerFactory.build_minimal_logger
|
11
14
|
@process_start_time = Time.now
|
15
|
+
@extensions = ScoutApm::Extensions::Config.new(self)
|
12
16
|
end
|
13
17
|
|
14
18
|
def marshal_dump
|
data/lib/scout_apm/context.rb
CHANGED
@@ -23,6 +23,15 @@ module ScoutApm
|
|
23
23
|
@extra.merge({:user => @user})
|
24
24
|
end
|
25
25
|
|
26
|
+
def to_flat_hash
|
27
|
+
h = to_hash
|
28
|
+
user = h.delete(:user)
|
29
|
+
if user
|
30
|
+
user.each { |k,v| h["user_#{k}"] = v}
|
31
|
+
end
|
32
|
+
h
|
33
|
+
end
|
34
|
+
|
26
35
|
def self.current
|
27
36
|
RequestManager.lookup.context
|
28
37
|
end
|
@@ -165,15 +165,23 @@ module ScoutApm
|
|
165
165
|
end
|
166
166
|
|
167
167
|
def ruby_19?
|
168
|
-
@ruby_19
|
168
|
+
return @ruby_19 if defined?(@ruby_19)
|
169
|
+
@ruby_19 = defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" && RUBY_VERSION.match(/^1\.9/)
|
169
170
|
end
|
170
171
|
|
171
172
|
def ruby_187?
|
172
|
-
@ruby_187
|
173
|
+
return @ruby_187 if defined?(@ruby_187)
|
174
|
+
@ruby_187 = defined?(RUBY_VERSION) && RUBY_VERSION.match(/^1\.8\.7/)
|
173
175
|
end
|
174
176
|
|
175
177
|
def ruby_2?
|
176
|
-
@ruby_2
|
178
|
+
return @ruby_2 if defined?(@ruby_2)
|
179
|
+
@ruby_2 = defined?(RUBY_VERSION) && RUBY_VERSION.match(/^2/)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns true if this Ruby version supports Module#prepend.
|
183
|
+
def supports_module_prepend?
|
184
|
+
ruby_2?
|
177
185
|
end
|
178
186
|
|
179
187
|
# Returns a string representation of the OS (ex: darwin, linux)
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module Extensions
|
3
|
+
# !!! Extensions are a 0.x level API and breakage is expected as the API is refined.
|
4
|
+
# Extensions fan out data collected by the agent to additional services.
|
5
|
+
class Config
|
6
|
+
attr_reader :agent_context
|
7
|
+
attr_accessor :transaction_callbacks
|
8
|
+
attr_accessor :periodic_callbacks
|
9
|
+
|
10
|
+
# Adds a new callback that runs after a transaction completes.
|
11
|
+
# These run inline during the request and thus should add minimal overhead.
|
12
|
+
# For example, a transaction callback should NOT make inline HTTP calls to outside services.
|
13
|
+
# +callback+ must be an object that respond to a +call(payload)+ method.
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
# ScoutApm::Extensions::Config.add_transaction_callback(Proc.new { |payload| puts "Duration: #{payload.duration_ms}" })
|
17
|
+
#
|
18
|
+
# +payload+ is a +ScoutApm::Extensions::TransactionCallbackPayload+ object.
|
19
|
+
def self.add_transaction_callback(callback)
|
20
|
+
agent_context.extensions.transaction_callbacks << callback
|
21
|
+
end
|
22
|
+
|
23
|
+
# Adds a callback that runs when the per-minute report data is sent to Scout.
|
24
|
+
# These run in a background thread so external HTTP calls are OK.
|
25
|
+
# +callback+ must be an object that responds to a +call(reporting_period, metadata)+ method.
|
26
|
+
#
|
27
|
+
# Example:
|
28
|
+
# ScoutApm::Extensions::Config.add_periodic_callback(Proc.new { |reporting_period, metadata| ... })
|
29
|
+
def self.add_periodic_callback(callback)
|
30
|
+
agent_context.extensions.periodic_callbacks << callback
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(agent_context)
|
34
|
+
@agent_context = agent_context
|
35
|
+
@transaction_callbacks = []
|
36
|
+
@periodic_callbacks = []
|
37
|
+
end
|
38
|
+
|
39
|
+
# Runs each reporting period callback.
|
40
|
+
# Each callback runs inside a begin/rescue block so a broken callback doesn't prevent other
|
41
|
+
# callbacks from executing or reporting data from being sent.
|
42
|
+
def run_periodic_callbacks(reporting_period, metadata)
|
43
|
+
return unless periodic_callbacks.any?
|
44
|
+
|
45
|
+
periodic_callbacks.each do |callback|
|
46
|
+
begin
|
47
|
+
callback.call(reporting_period, metadata)
|
48
|
+
rescue => e
|
49
|
+
logger.warn "Error running reporting callback extension=#{callback}"
|
50
|
+
logger.info e.message
|
51
|
+
logger.debug e.backtrace
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Runs each transaction callback.
|
57
|
+
# Each callback runs inside a begin/rescue block so a broken callback doesn't prevent other
|
58
|
+
# callbacks from executing or the transaction from being recorded.
|
59
|
+
def run_transaction_callbacks(converter_results, context, scope_layer)
|
60
|
+
# It looks like layer_finder.scope = nil when a Sidekiq job is retried
|
61
|
+
return unless scope_layer
|
62
|
+
return unless transaction_callbacks.any?
|
63
|
+
|
64
|
+
payload = ScoutApm::Extensions::TransactionCallbackPayload.new(agent_context,converter_results,context,scope_layer)
|
65
|
+
|
66
|
+
transaction_callbacks.each do |callback|
|
67
|
+
begin
|
68
|
+
callback.call(payload)
|
69
|
+
rescue => e
|
70
|
+
logger.warn "Error running transaction callback extension=#{callback}"
|
71
|
+
logger.info e.message
|
72
|
+
logger.debug e.backtrace
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.agent_context
|
78
|
+
ScoutApm::Agent.instance.context
|
79
|
+
end
|
80
|
+
|
81
|
+
def logger
|
82
|
+
agent_context.logger
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module Extensions
|
3
|
+
# A +TransactionCallbackPayload+ is passed to each Transaction callback's +call+ method.
|
4
|
+
# It encapsulates the data about a specific transaction.
|
5
|
+
class TransactionCallbackPayload
|
6
|
+
# A Hash that stores the output of each layer converter by name. See the naming conventions in +TrackedRequest+.
|
7
|
+
attr_accessor :converter_results
|
8
|
+
|
9
|
+
def initialize(agent_context,converter_results,context,scope_layer)
|
10
|
+
@agent_context = agent_context
|
11
|
+
@converter_results = converter_results
|
12
|
+
@context = context
|
13
|
+
@scope_layer = scope_layer
|
14
|
+
end
|
15
|
+
|
16
|
+
# A flat hash of the context associated w/this transaction (ie user ip and another other data added to context).
|
17
|
+
def context
|
18
|
+
@context.to_flat_hash
|
19
|
+
end
|
20
|
+
|
21
|
+
# The total duration of the transaction
|
22
|
+
def duration_ms
|
23
|
+
@scope_layer.total_call_time*1000 # ms
|
24
|
+
end
|
25
|
+
|
26
|
+
# The time in queue of the transaction in ms. If not present, +nil+ is returned as this is unknown.
|
27
|
+
def queue_time_ms
|
28
|
+
# Controller logic
|
29
|
+
if converter_results[:queue_time] && converter_results[:queue].any?
|
30
|
+
converter_results[:queue_time].values.first.total_call_time*1000 # ms
|
31
|
+
# Job logic
|
32
|
+
elsif converter_results[:job]
|
33
|
+
stat = converter_results[:job].metric_set.metrics[ScoutApm::MetricMeta.new("Latency/all", :scope => transaction_name)]
|
34
|
+
stat ? stat.total_call_time*1000 : nil
|
35
|
+
else
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def hostname
|
41
|
+
@agent_context.environment.hostname
|
42
|
+
end
|
43
|
+
|
44
|
+
def app_name
|
45
|
+
@agent_context.config.value('name')
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns +true+ if the transaction raised an exception.
|
49
|
+
def error?
|
50
|
+
converter_results[:errors] && converter_results[:errors].any?
|
51
|
+
end
|
52
|
+
|
53
|
+
def transation_type
|
54
|
+
@scope_layer.type
|
55
|
+
end
|
56
|
+
|
57
|
+
def transaction_name
|
58
|
+
@scope_layer.legacy_metric_name
|
59
|
+
end
|
60
|
+
|
61
|
+
# Web/Job are more language-agnostic names for controller/job. For example, Python Django does not have controllers.
|
62
|
+
def transaction_type_slug
|
63
|
+
case transation_type
|
64
|
+
when 'Controller'
|
65
|
+
'web'
|
66
|
+
when 'Job'
|
67
|
+
'job'
|
68
|
+
else
|
69
|
+
'transaction'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -118,8 +118,14 @@ module ScoutApm
|
|
118
118
|
(::ActiveRecord::VERSION::MAJOR.to_i == 3 && ::ActiveRecord::VERSION::MINOR.to_i >= 2))
|
119
119
|
if rails_3_2_or_above
|
120
120
|
if Utils::KlassHelper.defined?("ActiveRecord::Relation")
|
121
|
-
|
122
|
-
|
121
|
+
if @context.environment.supports_module_prepend?
|
122
|
+
::ActiveRecord::Relation.module_eval do
|
123
|
+
prepend ::ScoutApm::Instruments::ActiveRecordRelationQueryInstruments
|
124
|
+
end
|
125
|
+
else
|
126
|
+
::ActiveRecord::Relation.module_eval do
|
127
|
+
include ::ScoutApm::Instruments::ActiveRecordRelationQueryInstruments
|
128
|
+
end
|
123
129
|
end
|
124
130
|
end
|
125
131
|
else
|
@@ -298,6 +304,10 @@ module ScoutApm
|
|
298
304
|
end
|
299
305
|
|
300
306
|
module ActiveRecordRelationQueryInstruments
|
307
|
+
def self.prepended(instrumented_class)
|
308
|
+
ScoutApm::Agent.instance.context.logger.info "Instrumenting ActiveRecord::Relation#exec_queries - #{instrumented_class.inspect} (prepending)"
|
309
|
+
end
|
310
|
+
|
301
311
|
def self.included(instrumented_class)
|
302
312
|
ScoutApm::Agent.instance.context.logger.info "Instrumenting ActiveRecord::Relation#exec_queries - #{instrumented_class.inspect}"
|
303
313
|
instrumented_class.class_eval do
|
@@ -308,7 +318,7 @@ module ScoutApm
|
|
308
318
|
end
|
309
319
|
end
|
310
320
|
|
311
|
-
def
|
321
|
+
def exec_queries(*args, &block)
|
312
322
|
req = ScoutApm::RequestManager.lookup
|
313
323
|
layer = ScoutApm::Layer.new("ActiveRecord", Utils::ActiveRecordMetricName::DEFAULT_METRIC)
|
314
324
|
layer.annotate_layer(:ignorable => true)
|
@@ -316,12 +326,23 @@ module ScoutApm
|
|
316
326
|
req.start_layer(layer)
|
317
327
|
req.ignore_children!
|
318
328
|
begin
|
319
|
-
|
329
|
+
if ScoutApm::Environment.instance.supports_module_prepend?
|
330
|
+
super(*args, &block)
|
331
|
+
else
|
332
|
+
exec_queries_without_scout_instruments(*args, &block)
|
333
|
+
end
|
320
334
|
ensure
|
321
335
|
req.acknowledge_children!
|
322
336
|
req.stop_layer
|
323
337
|
end
|
324
338
|
end
|
339
|
+
|
340
|
+
# If prepend is not supported, rename the method and use
|
341
|
+
# alias_method_style chaining instead
|
342
|
+
if !ScoutApm::Environment.instance.supports_module_prepend?
|
343
|
+
alias_method :exec_queries_with_scout_instruments, :exec_queries
|
344
|
+
remove_method :exec_queries
|
345
|
+
end
|
325
346
|
end
|
326
347
|
|
327
348
|
module ActiveRecordUpdateInstruments
|
@@ -8,8 +8,10 @@ module ScoutApm
|
|
8
8
|
meta = MetricMeta.new("ObjectAllocations", {:scope => scope_layer.legacy_metric_name})
|
9
9
|
stat = MetricStats.new
|
10
10
|
stat.update!(root_layer.total_allocations)
|
11
|
+
metrics = { meta => stat }
|
11
12
|
|
12
|
-
@store.track!(
|
13
|
+
@store.track!(metrics)
|
14
|
+
nil # not returning anything in the layer results ... not used
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
@@ -32,6 +32,8 @@ module ScoutApm
|
|
32
32
|
# only due to 1 http request)
|
33
33
|
@db_query_metric_set.increment_transaction_count!
|
34
34
|
@store.track_db_query_metrics!(@db_query_metric_set)
|
35
|
+
|
36
|
+
nil # not returning anything in the layer results ... not used
|
35
37
|
end
|
36
38
|
|
37
39
|
def skip_layer?(layer)
|
@@ -10,8 +10,10 @@ module ScoutApm
|
|
10
10
|
meta = MetricMeta.new("Errors/#{scope_layer.legacy_metric_name}", {})
|
11
11
|
stat = MetricStats.new
|
12
12
|
stat.update!(1)
|
13
|
-
|
14
|
-
|
13
|
+
metrics = { meta => stat }
|
14
|
+
|
15
|
+
@store.track!(metrics)
|
16
|
+
metrics # this result must be returned so it can be accessed by transaction callback extensions
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -28,8 +28,10 @@ module ScoutApm
|
|
28
28
|
meta = MetricMeta.new("QueueTime/Request", {:scope => scope_layer.legacy_metric_name})
|
29
29
|
stat = MetricStats.new(true)
|
30
30
|
stat.update!(queue_time)
|
31
|
-
|
32
|
-
|
31
|
+
metrics = { meta => stat }
|
32
|
+
|
33
|
+
@store.track!(metrics)
|
34
|
+
metrics # this result must be returned so it can be accessed by transaction callback extensions
|
33
35
|
end
|
34
36
|
|
35
37
|
private
|
data/lib/scout_apm/reporting.rb
CHANGED
@@ -48,7 +48,9 @@ module ScoutApm
|
|
48
48
|
begin
|
49
49
|
merged = rps.inject { |memo, rp| memo.merge(rp) }
|
50
50
|
logger.debug("Merged #{rps.length} reporting periods, delivering")
|
51
|
-
|
51
|
+
metadata = metadata(merged)
|
52
|
+
deliver_period(merged,metadata)
|
53
|
+
context.extensions.run_periodic_callbacks(merged, metadata)
|
52
54
|
true
|
53
55
|
rescue => e
|
54
56
|
logger.debug("Error merging reporting periods #{e.message}")
|
@@ -63,15 +65,8 @@ module ScoutApm
|
|
63
65
|
end
|
64
66
|
end
|
65
67
|
|
66
|
-
def
|
67
|
-
|
68
|
-
slow_transactions = reporting_period.slow_transactions_payload
|
69
|
-
jobs = reporting_period.jobs
|
70
|
-
slow_jobs = reporting_period.slow_jobs_payload
|
71
|
-
histograms = reporting_period.histograms
|
72
|
-
db_query_metrics = reporting_period.db_query_metrics_payload
|
73
|
-
|
74
|
-
metadata = {
|
68
|
+
def metadata(reporting_period)
|
69
|
+
{
|
75
70
|
:app_root => context.environment.root.to_s,
|
76
71
|
:unique_id => ScoutApm::Utils::UniqueId.simple,
|
77
72
|
:agent_version => ScoutApm::VERSION,
|
@@ -79,6 +74,15 @@ module ScoutApm
|
|
79
74
|
:agent_pid => Process.pid,
|
80
75
|
:platform => "ruby",
|
81
76
|
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def deliver_period(reporting_period,metadata)
|
80
|
+
metrics = reporting_period.metrics_payload
|
81
|
+
slow_transactions = reporting_period.slow_transactions_payload
|
82
|
+
jobs = reporting_period.jobs
|
83
|
+
slow_jobs = reporting_period.slow_jobs_payload
|
84
|
+
histograms = reporting_period.histograms
|
85
|
+
db_query_metrics = reporting_period.db_query_metrics_payload
|
82
86
|
|
83
87
|
log_deliver(metrics, slow_transactions, metadata, slow_jobs, histograms)
|
84
88
|
|
@@ -64,7 +64,6 @@ module ScoutApm
|
|
64
64
|
@mem_start = mem_usage
|
65
65
|
@recorder = agent_context.recorder
|
66
66
|
@real_request = false
|
67
|
-
|
68
67
|
ignore_request! if @recorder.nil?
|
69
68
|
end
|
70
69
|
|
@@ -119,6 +118,7 @@ module ScoutApm
|
|
119
118
|
layer.capture_backtrace!
|
120
119
|
end
|
121
120
|
|
121
|
+
|
122
122
|
if finalized?
|
123
123
|
stop_request
|
124
124
|
else
|
@@ -300,27 +300,34 @@ module ScoutApm
|
|
300
300
|
|
301
301
|
apply_name_override
|
302
302
|
|
303
|
-
converters
|
304
|
-
|
305
|
-
LayerConverters::
|
306
|
-
LayerConverters::
|
307
|
-
LayerConverters::
|
308
|
-
LayerConverters::
|
309
|
-
LayerConverters::
|
310
|
-
LayerConverters::
|
303
|
+
# Make a constant, then call converters.dup.each so it isn't inline?
|
304
|
+
converters = {
|
305
|
+
:histograms => LayerConverters::Histograms,
|
306
|
+
:metrics => LayerConverters::MetricConverter,
|
307
|
+
:errors => LayerConverters::ErrorConverter,
|
308
|
+
:allocation_metrics => LayerConverters::AllocationMetricConverter,
|
309
|
+
:queue_time => LayerConverters::RequestQueueTimeConverter,
|
310
|
+
:job => LayerConverters::JobConverter,
|
311
|
+
:db => LayerConverters::DatabaseConverter,
|
311
312
|
|
312
|
-
LayerConverters::SlowJobConverter,
|
313
|
-
LayerConverters::SlowRequestConverter,
|
314
|
-
|
313
|
+
:slow_job => LayerConverters::SlowJobConverter,
|
314
|
+
:slow_req => LayerConverters::SlowRequestConverter,
|
315
|
+
}
|
315
316
|
|
316
317
|
walker = LayerConverters::DepthFirstWalker.new(self.root_layer)
|
317
|
-
|
318
|
+
converter_instances = converters.inject({}) do |memo, (slug, klass)|
|
318
319
|
instance = klass.new(@agent_context, self, layer_finder, @store)
|
319
320
|
instance.register_hooks(walker)
|
320
|
-
instance
|
321
|
+
memo[slug] = instance
|
322
|
+
memo
|
321
323
|
end
|
322
324
|
walker.walk
|
323
|
-
|
325
|
+
converter_results = converter_instances.inject({}) do |memo, (slug,i)|
|
326
|
+
memo[slug] = i.record!
|
327
|
+
memo
|
328
|
+
end
|
329
|
+
|
330
|
+
@agent_context.extensions.run_transaction_callbacks(converter_results,context,layer_finder.scope)
|
324
331
|
|
325
332
|
# If there's an instant_key, it means we need to report this right away
|
326
333
|
if web? && instant?
|
data/lib/scout_apm/version.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PeriodicCallbacksTest < Minitest::Test
|
4
|
+
|
5
|
+
# We don't have a test that ensures we actually report data to the server, so we can't be 100% sure this doesn't break
|
6
|
+
# reporting.
|
7
|
+
def test_broken_callback_does_not_throw_exception
|
8
|
+
ScoutApm::Extensions::Config.add_periodic_callback(BrokenCallback.new)
|
9
|
+
# Runs via agent context as calling +add_periodic_callback+ initializing the context + extension config.
|
10
|
+
ScoutApm::Agent.instance.context.extensions.run_periodic_callbacks(reporting_period,metadata)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_callback_runs
|
14
|
+
Thread.current[:periodic_callback_output] = nil
|
15
|
+
ScoutApm::Extensions::Config.add_periodic_callback(PeriodicCallback.new)
|
16
|
+
ScoutApm::Agent.instance.context.extensions.run_periodic_callbacks(reporting_period,metadata)
|
17
|
+
assert Thread.current[:periodic_callback_output]
|
18
|
+
end
|
19
|
+
|
20
|
+
def run_proc_callback
|
21
|
+
Thread.current[:proc_periodic] = nil
|
22
|
+
ScoutApm::Extensions::Config.add_periodic_callback(Proc.new { |reporting_period, metadata| Thread.current[:proc_periodic] = Time.at(metadata[:agent_time].to_i) })
|
23
|
+
ScoutApm::Agent.instance.context.extensions.run_periodic_callbacks(reporting_period,metadata)
|
24
|
+
assert Thread.current[:proc_periodic]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Doesn't respond to +call+.
|
28
|
+
class BrokenCallback
|
29
|
+
end
|
30
|
+
|
31
|
+
# Sets a Thread local so we can verify that the callback ran.
|
32
|
+
class PeriodicCallback
|
33
|
+
def call(reporting_period,metadata)
|
34
|
+
Thread.current[:periodic_callback_output] = true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def reporting_period
|
41
|
+
rp = ScoutApm::StoreReportingPeriod.new(ScoutApm::AgentContext.new, Time.at(metadata[:agent_time].to_i))
|
42
|
+
rp.absorb_metrics!(metrics)
|
43
|
+
end
|
44
|
+
|
45
|
+
def metrics
|
46
|
+
meta = ScoutApm::MetricMeta.new("Controller/users/index")
|
47
|
+
stats = ScoutApm::MetricStats.new
|
48
|
+
stats.update!(0.1)
|
49
|
+
{
|
50
|
+
meta => stats
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def metadata
|
55
|
+
{:app_root=>"/srv/rails_app", :unique_id=>"ID", :agent_version=>"2.4.10", :agent_time=>"1523287920", :agent_pid=>21581, :platform=>"ruby"}
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TransactionCallbacksTest < Minitest::Test
|
4
|
+
|
5
|
+
def setup
|
6
|
+
# Another test could set the recorder to +FakeRecorder+, which does not call +TrackedRequest#record!+.
|
7
|
+
ScoutApm::Agent.instance.context.recorder = ScoutApm::RecorderFactory.build(ScoutApm::Agent.instance.context)
|
8
|
+
end
|
9
|
+
|
10
|
+
# This is more of an integration test to ensure that we don't break TrackedRequest.
|
11
|
+
def test_broken_callback_does_not_break_tracked_request
|
12
|
+
ScoutApm::Extensions::Config.add_transaction_callback(BrokenCallback.new)
|
13
|
+
|
14
|
+
controller_layer = ScoutApm::Layer.new("Controller", "users/index")
|
15
|
+
# why doesn't this run? check if callbacks are configured
|
16
|
+
tr = ScoutApm::TrackedRequest.new(ScoutApm::Agent.instance.context, ScoutApm::FakeStore.new)
|
17
|
+
tr.start_layer(controller_layer)
|
18
|
+
tr.stop_layer
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_callback_runs
|
22
|
+
Thread.current[:transaction_callback_output] = nil
|
23
|
+
ScoutApm::Extensions::Config.add_transaction_callback(TransactionCallback.new)
|
24
|
+
|
25
|
+
controller_layer = ScoutApm::Layer.new("Controller", "users/index")
|
26
|
+
|
27
|
+
tr = ScoutApm::TrackedRequest.new(ScoutApm::Agent.instance.context, ScoutApm::FakeStore.new)
|
28
|
+
tr.start_layer(controller_layer)
|
29
|
+
tr.stop_layer
|
30
|
+
|
31
|
+
assert Thread.current[:transaction_callback_output]
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_run_proc_callback
|
35
|
+
Thread.current[:proc_transaction_duration] = nil
|
36
|
+
ScoutApm::Extensions::Config.add_transaction_callback(Proc.new { |payload| Thread.current[:proc_transaction_duration] = payload.duration_ms })
|
37
|
+
|
38
|
+
controller_layer = ScoutApm::Layer.new("Controller", "users/index")
|
39
|
+
|
40
|
+
tr = ScoutApm::TrackedRequest.new(ScoutApm::Agent.instance.context, ScoutApm::FakeStore.new)
|
41
|
+
tr.start_layer(controller_layer)
|
42
|
+
tr.stop_layer
|
43
|
+
|
44
|
+
assert Thread.current[:proc_transaction_duration]
|
45
|
+
end
|
46
|
+
|
47
|
+
# Doesn't respond to +call+.
|
48
|
+
class BrokenCallback
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sets a Thread local so we can verify that the callback ran.
|
52
|
+
class TransactionCallback
|
53
|
+
def call(payload)
|
54
|
+
Thread.current[:transaction_callback_output] = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.
|
4
|
+
version: 3.0.0.pre23
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derek Haynes
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-05-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -213,6 +213,8 @@ files:
|
|
213
213
|
- lib/scout_apm/db_query_metric_stats.rb
|
214
214
|
- lib/scout_apm/debug.rb
|
215
215
|
- lib/scout_apm/environment.rb
|
216
|
+
- lib/scout_apm/extensions/config.rb
|
217
|
+
- lib/scout_apm/extensions/transaction_callback_payload.rb
|
216
218
|
- lib/scout_apm/fake_store.rb
|
217
219
|
- lib/scout_apm/framework_integrations/rails_2.rb
|
218
220
|
- lib/scout_apm/framework_integrations/rails_3_or_4.rb
|
@@ -338,6 +340,8 @@ files:
|
|
338
340
|
- test/unit/db_query_metric_set_test.rb
|
339
341
|
- test/unit/db_query_metric_stats_test.rb
|
340
342
|
- test/unit/environment_test.rb
|
343
|
+
- test/unit/extensions/periodic_callbacks_test.rb
|
344
|
+
- test/unit/extensions/transaction_callbacks_test.rb
|
341
345
|
- test/unit/fake_store_test.rb
|
342
346
|
- test/unit/git_revision_test.rb
|
343
347
|
- test/unit/histogram_test.rb
|
@@ -391,48 +395,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
391
395
|
version: 1.3.1
|
392
396
|
requirements: []
|
393
397
|
rubyforge_project: scout_apm
|
394
|
-
rubygems_version: 2.
|
398
|
+
rubygems_version: 2.6.12
|
395
399
|
signing_key:
|
396
400
|
specification_version: 4
|
397
401
|
summary: Ruby application performance monitoring
|
398
|
-
test_files:
|
399
|
-
- test/data/config_test_1.yml
|
400
|
-
- test/test_helper.rb
|
401
|
-
- test/unit/agent_test.rb
|
402
|
-
- test/unit/background_job_integrations/sidekiq_test.rb
|
403
|
-
- test/unit/config_test.rb
|
404
|
-
- test/unit/context_test.rb
|
405
|
-
- test/unit/db_query_metric_set_test.rb
|
406
|
-
- test/unit/db_query_metric_stats_test.rb
|
407
|
-
- test/unit/environment_test.rb
|
408
|
-
- test/unit/fake_store_test.rb
|
409
|
-
- test/unit/git_revision_test.rb
|
410
|
-
- test/unit/histogram_test.rb
|
411
|
-
- test/unit/ignored_uris_test.rb
|
412
|
-
- test/unit/instruments/active_record_instruments_test.rb
|
413
|
-
- test/unit/instruments/net_http_test.rb
|
414
|
-
- test/unit/instruments/percentile_sampler_test.rb
|
415
|
-
- test/unit/layaway_test.rb
|
416
|
-
- test/unit/layer_children_set_test.rb
|
417
|
-
- test/unit/layer_converters/depth_first_walker_test.rb
|
418
|
-
- test/unit/layer_converters/metric_converter_test.rb
|
419
|
-
- test/unit/layer_converters/stubs.rb
|
420
|
-
- test/unit/limited_layer_test.rb
|
421
|
-
- test/unit/logger_test.rb
|
422
|
-
- test/unit/metric_set_test.rb
|
423
|
-
- test/unit/remote/test_message.rb
|
424
|
-
- test/unit/remote/test_router.rb
|
425
|
-
- test/unit/remote/test_server.rb
|
426
|
-
- test/unit/scored_item_set_test.rb
|
427
|
-
- test/unit/serializers/payload_serializer_test.rb
|
428
|
-
- test/unit/slow_job_policy_test.rb
|
429
|
-
- test/unit/slow_request_policy_test.rb
|
430
|
-
- test/unit/sql_sanitizer_test.rb
|
431
|
-
- test/unit/store_test.rb
|
432
|
-
- test/unit/tracer_test.rb
|
433
|
-
- test/unit/tracked_request_test.rb
|
434
|
-
- test/unit/transaction_test.rb
|
435
|
-
- test/unit/utils/active_record_metric_name_test.rb
|
436
|
-
- test/unit/utils/backtrace_parser_test.rb
|
437
|
-
- test/unit/utils/numbers_test.rb
|
438
|
-
- test/unit/utils/scm.rb
|
402
|
+
test_files: []
|