scout_apm 3.0.0.pre22 → 3.0.0.pre23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|