scout_apm 5.3.3 → 5.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +30 -0
- data/.github/workflows/test.yml +13 -12
- data/CHANGELOG.markdown +24 -0
- data/README.markdown +5 -1
- data/gems/instruments.gemfile +2 -0
- data/gems/sqlite3-1.3.gemfile +3 -0
- data/lib/scout_apm/app_server_load.rb +1 -1
- data/lib/scout_apm/auto_instrument/rails.rb +21 -0
- data/lib/scout_apm/background_job_integrations/good_job.rb +49 -0
- data/lib/scout_apm/background_job_integrations/solid_queue.rb +47 -0
- data/lib/scout_apm/environment.rb +2 -0
- data/lib/scout_apm/instruments/action_view.rb +62 -3
- data/lib/scout_apm/instruments/active_record.rb +1 -1
- data/lib/scout_apm/instruments/net_http.rb +1 -1
- data/lib/scout_apm/layer_converters/external_service_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +7 -0
- data/lib/scout_apm/slow_request_policy.rb +1 -1
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +2 -0
- data/scout_apm.gemspec +2 -2
- data/test/test_helper.rb +32 -0
- data/test/unit/auto_instrument/hash_shorthand_controller-instrumented.rb +41 -0
- data/test/unit/auto_instrument/hash_shorthand_controller.rb +41 -0
- data/test/unit/auto_instrument_test.rb +7 -1
- data/test/unit/environment_test.rb +0 -28
- data/test/unit/instruments/action_view_test.rb +102 -0
- data/test/unit/instruments/active_record_test.rb +30 -0
- data/test/unit/instruments/fixtures/test/_test_partial.html.erb +3 -0
- data/test/unit/instruments/fixtures/test/_test_partial_collection.html.erb +3 -0
- data/test/unit/instruments/fixtures/test_view.html.erb +10 -0
- metadata +17 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e125a347fd3bbfa0b52f6dde597daf1dc95d97ba95cb6c44553bc36fae71b82
|
4
|
+
data.tar.gz: 9f0c708e386be527f255582195a791938c1663b418999816b79f25e4f7caaf6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7e079bde40861cf673834ec606f8a09fc84bd4416eddccd22abd29d5cd64dbf071347d832549f840dd3b07026c8a3d4e6788f458587128482a1b44682479ec4
|
7
|
+
data.tar.gz: e20c7d5fadbfe008f1febb62e3e31e5aa23cc4410f02634329493b10159603fb467e4ae66d504709f247ff457b0bf5d386f62baa0835010e7e5a0954160f7b97
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: Release Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags:
|
6
|
+
- 'v*'
|
7
|
+
|
8
|
+
# Directly from https://github.com/rubygems/release-gem/tree/v1
|
9
|
+
# Predicated on Tag Ruleset preventing arbitrary tag creation and
|
10
|
+
# requiring checks to pass for commit before tag creation
|
11
|
+
jobs:
|
12
|
+
push:
|
13
|
+
name: Push gem to RubyGems.org
|
14
|
+
runs-on: ubuntu-latest
|
15
|
+
|
16
|
+
permissions:
|
17
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
18
|
+
contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
|
19
|
+
|
20
|
+
steps:
|
21
|
+
# Set up
|
22
|
+
- uses: actions/checkout@v4
|
23
|
+
- name: Set up Ruby
|
24
|
+
uses: ruby/setup-ruby@v1
|
25
|
+
with:
|
26
|
+
bundler-cache: true
|
27
|
+
ruby-version: ruby
|
28
|
+
|
29
|
+
# Release
|
30
|
+
- uses: rubygems/release-gem@v1
|
data/.github/workflows/test.yml
CHANGED
@@ -7,7 +7,7 @@ jobs:
|
|
7
7
|
runs-on: ubuntu-latest
|
8
8
|
|
9
9
|
steps:
|
10
|
-
- uses: actions/checkout@
|
10
|
+
- uses: actions/checkout@v4
|
11
11
|
- uses: ruby/setup-ruby@v1
|
12
12
|
with:
|
13
13
|
bundler-cache: true
|
@@ -23,7 +23,9 @@ jobs:
|
|
23
23
|
gemfile: gems/rails3.gemfile
|
24
24
|
- ruby: 2.2
|
25
25
|
- ruby: 2.3
|
26
|
+
gemfile: gems/sqlite3-1.3.gemfile
|
26
27
|
- ruby: 2.4
|
28
|
+
gemfile: gems/sqlite3-1.3.gemfile
|
27
29
|
- ruby: 2.5
|
28
30
|
- ruby: 2.6
|
29
31
|
- ruby: 2.6
|
@@ -31,35 +33,34 @@ jobs:
|
|
31
33
|
test_features: "typhoeus"
|
32
34
|
- ruby: 2.6
|
33
35
|
gemfile: gems/octoshark.gemfile
|
34
|
-
- ruby: 2.6
|
35
|
-
gemfile: gems/rails3.gemfile
|
36
|
-
bundler: 1.17.3
|
37
36
|
- ruby: 2.7
|
38
37
|
- ruby: 2.7
|
39
38
|
prepend: true
|
40
|
-
- ruby: 3.0
|
41
|
-
- ruby: 3.0
|
39
|
+
- ruby: "3.0"
|
40
|
+
- ruby: "3.0"
|
42
41
|
prepend: true
|
43
|
-
- ruby: 3.0
|
42
|
+
- ruby: "3.0"
|
44
43
|
gemfile: gems/instruments.gemfile
|
45
44
|
test_features: "instruments"
|
46
|
-
- ruby: 3.0
|
45
|
+
- ruby: "3.0"
|
47
46
|
gemfile: gems/instruments.gemfile
|
48
47
|
prepend: true
|
49
48
|
test_features: "instruments"
|
50
|
-
- ruby: 3.0
|
49
|
+
- ruby: "3.0"
|
51
50
|
gemfile: gems/sidekiq.gemfile
|
52
51
|
test_features: "sidekiq_install"
|
53
|
-
|
52
|
+
- ruby: 3.1
|
53
|
+
- ruby: 3.2
|
54
54
|
env:
|
55
55
|
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
56
56
|
SCOUT_TEST_FEATURES: ${{ matrix.test_features }}
|
57
57
|
SCOUT_USE_PREPEND: ${{ matrix.prepend }}
|
58
58
|
|
59
|
-
|
59
|
+
# https://github.com/ruby/setup-ruby/issues/496
|
60
|
+
runs-on: ${{ matrix.ruby == '2.2' && 'ubuntu-20.04' || 'ubuntu-latest' }}
|
60
61
|
|
61
62
|
steps:
|
62
|
-
- uses: actions/checkout@
|
63
|
+
- uses: actions/checkout@v4
|
63
64
|
- uses: ruby/setup-ruby@v1
|
64
65
|
with:
|
65
66
|
bundler-cache: true
|
data/CHANGELOG.markdown
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 5.4.0
|
4
|
+
* Add support for GoodJob (#506)
|
5
|
+
* Add support for Solid Queue (#508)
|
6
|
+
|
7
|
+
# 5.3.8
|
8
|
+
* Avoid inaccurate websocket queue time capturing (#494)
|
9
|
+
|
10
|
+
# 5.3.7
|
11
|
+
* Fix parser dependency issue
|
12
|
+
|
13
|
+
# 5.3.6
|
14
|
+
* Fix AutoInstruments when instrumenting hash with shorthand (#486)
|
15
|
+
* Fix Connection Handling deprecation in ActiveRecord for Rails 7.1 (#483)
|
16
|
+
* Update ActionView partial instrumentation for Rails 7 (#487)
|
17
|
+
|
18
|
+
# 5.3.5
|
19
|
+
* Fix adding instrumentation of ActiveRecord after configuration has initialized for Rails versions greater than 3. (#465)
|
20
|
+
* Fix typo with double use of PercentilePolicy, instead of PercentPolicy, for scoring. (#468)
|
21
|
+
* Fix span annotations/desc for external service requests with the use of prepend. (#471)
|
22
|
+
* Fix ActiveSupport methods and replace them with non ActiveSupport methods. (#474)
|
23
|
+
|
24
|
+
# 5.3.4
|
25
|
+
Unused.
|
26
|
+
|
3
27
|
# 5.3.3
|
4
28
|
|
5
29
|
* Fix double firing of Puma `on_worker_boot` when preloading. (#463)
|
data/README.markdown
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# ScoutApm Ruby Agent
|
2
2
|
|
3
|
-
[![Build Status](https://
|
3
|
+
[![Build Status](https://github.com/scoutapp/scout_apm_ruby/actions/workflows/test.yml/badge.svg)](https://github.com/scoutapp/scout_apm_ruby/actions)
|
4
4
|
|
5
5
|
A Ruby gem for detailed Rails application performance monitoring 📈. Metrics and transaction traces are
|
6
6
|
reported to [Scout](https://scoutapp.com), a hosted application monitoring
|
@@ -21,6 +21,10 @@ Add the gem to your Gemfile
|
|
21
21
|
|
22
22
|
gem 'scout_apm'
|
23
23
|
|
24
|
+
Add [a version of the `parser` gem that supports your version of Ruby](https://github.com/whitequark/parser?tab=readme-ov-file#backwards-compatibility). For example, if you're on Ruby 3.3.0:
|
25
|
+
|
26
|
+
gem 'parser', '~> 3.3.0.0'
|
27
|
+
|
24
28
|
Update your Gemfile
|
25
29
|
|
26
30
|
bundle install
|
data/gems/instruments.gemfile
CHANGED
@@ -51,7 +51,7 @@ module ScoutApm
|
|
51
51
|
ensure
|
52
52
|
# Sometimes :database_engine and :database_adapter can cause a reference to an AR connection.
|
53
53
|
# Make sure we release all AR connections held by this thread.
|
54
|
-
ActiveRecord::Base.clear_active_connections! if Utils::KlassHelper.defined?("ActiveRecord::Base")
|
54
|
+
ActiveRecord::Base.connection_handler.clear_active_connections! if Utils::KlassHelper.defined?("ActiveRecord::Base")
|
55
55
|
end
|
56
56
|
|
57
57
|
# Calls `.to_s` on the object passed in.
|
@@ -135,6 +135,27 @@ module ScoutApm
|
|
135
135
|
wrap(node.location.expression, *instrument(node.location.expression.source, file_name, line))
|
136
136
|
end
|
137
137
|
|
138
|
+
def on_hash(node)
|
139
|
+
node.children.each do |pair|
|
140
|
+
# Skip `pair` if we're sure it's not using the hash shorthand syntax
|
141
|
+
next if pair.type != :pair
|
142
|
+
key_node, value_node = pair.children
|
143
|
+
next unless key_node.type == :sym && value_node.type == :send
|
144
|
+
key = key_node.children[0]
|
145
|
+
next unless value_node.children.size == 2 && value_node.children[0].nil? && key == value_node.children[1]
|
146
|
+
|
147
|
+
# Extract useful metadata for instrumentation:
|
148
|
+
line = pair.location.line || 'line?'
|
149
|
+
# column = pair.location.column || 'column?' # not used
|
150
|
+
# method_name = key || '*unknown*' # not used
|
151
|
+
file_name = @source_rewriter.source_buffer.name
|
152
|
+
|
153
|
+
instrument_before, instrument_after = instrument(pair.location.expression.source, file_name, line)
|
154
|
+
replace(pair.loc.expression, "#{key}: #{instrument_before}#{key}#{instrument_after}")
|
155
|
+
end
|
156
|
+
super
|
157
|
+
end
|
158
|
+
|
138
159
|
# def on_class(node)
|
139
160
|
# class_name = node.children[1]
|
140
161
|
#
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module BackgroundJobIntegrations
|
3
|
+
class GoodJob
|
4
|
+
UNKNOWN_QUEUE_PLACEHOLDER = 'default'.freeze
|
5
|
+
attr_reader :logger
|
6
|
+
|
7
|
+
def name
|
8
|
+
:good_job
|
9
|
+
end
|
10
|
+
|
11
|
+
def present?
|
12
|
+
defined?(::GoodJob::VERSION)
|
13
|
+
end
|
14
|
+
|
15
|
+
def forking?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def install
|
20
|
+
ActiveSupport.on_load(:active_job) do
|
21
|
+
include ScoutApm::Tracer
|
22
|
+
|
23
|
+
around_perform do |job, block|
|
24
|
+
# I have a sneaking suspicion there is a better way to handle Agent starting
|
25
|
+
# Maybe hook into GoodJob lifecycle events?
|
26
|
+
req = ScoutApm::RequestManager.lookup
|
27
|
+
latency = Time.now - (job.scheduled_at || job.enqueued_at) rescue 0
|
28
|
+
req.annotate_request(queue_latency: latency)
|
29
|
+
|
30
|
+
begin
|
31
|
+
req.start_layer ScoutApm::Layer.new("Queue", job.queue_name.presence || UNKNOWN_QUEUE_PLACEHOLDER)
|
32
|
+
started_queue = true # Following Convention
|
33
|
+
req.start_layer ScoutApm::Layer.new("Job", job.class.name)
|
34
|
+
started_job = true # Following Convention
|
35
|
+
|
36
|
+
block.call
|
37
|
+
rescue
|
38
|
+
req.error!
|
39
|
+
raise
|
40
|
+
ensure
|
41
|
+
req.stop_layer if started_job
|
42
|
+
req.stop_layer if started_queue
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
module BackgroundJobIntegrations
|
3
|
+
class SolidQueue
|
4
|
+
UNKNOWN_QUEUE_PLACEHOLDER = 'default'.freeze
|
5
|
+
attr_reader :logger
|
6
|
+
|
7
|
+
def name
|
8
|
+
:solid_queue
|
9
|
+
end
|
10
|
+
|
11
|
+
def present?
|
12
|
+
defined?(::SolidQueue::VERSION)
|
13
|
+
end
|
14
|
+
|
15
|
+
def forking?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def install
|
20
|
+
ActiveSupport.on_load(:active_job) do
|
21
|
+
include ScoutApm::Tracer
|
22
|
+
|
23
|
+
around_perform do |job, block|
|
24
|
+
req = ScoutApm::RequestManager.lookup
|
25
|
+
latency = Time.now - (job.scheduled_at || job.enqueued_at) rescue 0
|
26
|
+
req.annotate_request(queue_latency: latency)
|
27
|
+
|
28
|
+
begin
|
29
|
+
req.start_layer ScoutApm::Layer.new("Queue", job.queue_name.presence || UNKNOWN_QUEUE_PLACEHOLDER)
|
30
|
+
started_queue = true # Following Convention
|
31
|
+
req.start_layer ScoutApm::Layer.new("Job", job.class.name)
|
32
|
+
started_job = true # Following Convention
|
33
|
+
|
34
|
+
block.call
|
35
|
+
rescue
|
36
|
+
req.error!
|
37
|
+
raise
|
38
|
+
ensure
|
39
|
+
req.stop_layer if started_job
|
40
|
+
req.stop_layer if started_queue
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -31,6 +31,8 @@ module ScoutApm
|
|
31
31
|
ScoutApm::BackgroundJobIntegrations::DelayedJob.new,
|
32
32
|
ScoutApm::BackgroundJobIntegrations::Que.new,
|
33
33
|
ScoutApm::BackgroundJobIntegrations::Faktory.new,
|
34
|
+
ScoutApm::BackgroundJobIntegrations::GoodJob.new,
|
35
|
+
ScoutApm::BackgroundJobIntegrations::SolidQueue.new,
|
34
36
|
]
|
35
37
|
|
36
38
|
FRAMEWORK_INTEGRATIONS = [
|
@@ -72,9 +72,67 @@ module ScoutApm
|
|
72
72
|
|
73
73
|
logger.info "Instrumenting ActionView::TemplateRenderer"
|
74
74
|
::ActionView::TemplateRenderer.prepend(ActionViewTemplateRendererInstruments)
|
75
|
+
|
76
|
+
if defined?(::ActionView::CollectionRenderer)
|
77
|
+
logger.info "Instrumenting ActionView::CollectionRenderer"
|
78
|
+
::ActionView::CollectionRenderer.prepend(ActionViewCollectionRendererInstruments)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# In Rails 6.1 collection was moved to CollectionRenderer.
|
83
|
+
module ActionViewCollectionRendererInstruments
|
84
|
+
def render_collection(*args, **kwargs)
|
85
|
+
req = ScoutApm::RequestManager.lookup
|
86
|
+
|
87
|
+
maybe_template = args[3]
|
88
|
+
|
89
|
+
template_name ||= maybe_template.virtual_path rescue nil
|
90
|
+
template_name ||= "Unknown Collection"
|
91
|
+
layer_name = template_name + "/Rendering"
|
92
|
+
|
93
|
+
layer = ScoutApm::Layer.new("View", layer_name)
|
94
|
+
layer.subscopable!
|
95
|
+
|
96
|
+
begin
|
97
|
+
req.start_layer(layer)
|
98
|
+
if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
|
99
|
+
super(*args, **kwargs)
|
100
|
+
else
|
101
|
+
super(*args)
|
102
|
+
end
|
103
|
+
ensure
|
104
|
+
req.stop_layer
|
105
|
+
end
|
106
|
+
end
|
75
107
|
end
|
76
108
|
|
77
109
|
module ActionViewPartialRendererInstruments
|
110
|
+
# In Rails 6.1, render_partial was renamed to render_partial_template
|
111
|
+
def render_partial_template(*args, **kwargs)
|
112
|
+
req = ScoutApm::RequestManager.lookup
|
113
|
+
|
114
|
+
# Template was moved to the third argument in Rails 6.1.
|
115
|
+
maybe_template = args[2]
|
116
|
+
|
117
|
+
template_name ||= maybe_template.virtual_path rescue nil
|
118
|
+
template_name ||= "Unknown Partial"
|
119
|
+
|
120
|
+
layer_name = template_name + "/Rendering"
|
121
|
+
layer = ScoutApm::Layer.new("View", layer_name)
|
122
|
+
layer.subscopable!
|
123
|
+
|
124
|
+
begin
|
125
|
+
req.start_layer(layer)
|
126
|
+
if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
|
127
|
+
super(*args, **kwargs)
|
128
|
+
else
|
129
|
+
super(*args)
|
130
|
+
end
|
131
|
+
ensure
|
132
|
+
req.stop_layer
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
78
136
|
# In Rails 6, the signature changed to pass the view & template args directly, as opposed to through the instance var
|
79
137
|
# New signature is: def render_partial(view, template)
|
80
138
|
def render_partial(*args, **kwargs)
|
@@ -83,7 +141,7 @@ module ScoutApm
|
|
83
141
|
maybe_template = args[1]
|
84
142
|
|
85
143
|
template_name = @template.virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
86
|
-
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.
|
144
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.6
|
87
145
|
template_name ||= "Unknown Partial"
|
88
146
|
|
89
147
|
layer_name = template_name + "/Rendering"
|
@@ -102,13 +160,14 @@ module ScoutApm
|
|
102
160
|
end
|
103
161
|
end
|
104
162
|
|
163
|
+
# This method was moved in Rails 6.1 to CollectionRender.
|
105
164
|
def collection_with_template(*args, **kwargs)
|
106
165
|
req = ScoutApm::RequestManager.lookup
|
107
166
|
|
108
167
|
maybe_template = args[1]
|
109
168
|
|
110
169
|
template_name = @template.virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
111
|
-
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.
|
170
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.6
|
112
171
|
template_name ||= "Unknown Collection"
|
113
172
|
layer_name = template_name + "/Rendering"
|
114
173
|
|
@@ -137,7 +196,7 @@ module ScoutApm
|
|
137
196
|
maybe_template = args[1]
|
138
197
|
|
139
198
|
template_name = args[0].virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
140
|
-
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 ->
|
199
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 7.1.3
|
141
200
|
template_name ||= "Unknown"
|
142
201
|
layer_name = template_name + "/Rendering"
|
143
202
|
|
@@ -60,7 +60,7 @@ module ScoutApm
|
|
60
60
|
|
61
61
|
module NetHttpInstrumentationPrepend
|
62
62
|
def request(request, *args, &block)
|
63
|
-
self.class.instrument("HTTP", "request", :ignore_children => true, :desc => request_scout_description(
|
63
|
+
self.class.instrument("HTTP", "request", :ignore_children => true, :desc => request_scout_description(request)) do
|
64
64
|
super(request, *args, &block)
|
65
65
|
end
|
66
66
|
end
|
@@ -15,6 +15,9 @@ module ScoutApm
|
|
15
15
|
|
16
16
|
return unless headers
|
17
17
|
|
18
|
+
# When an application uses Turbo Streams, we capture very innaccurate queue times.
|
19
|
+
return if request_over_websocket?
|
20
|
+
|
18
21
|
raw_start = locate_timestamp
|
19
22
|
return unless raw_start
|
20
23
|
|
@@ -38,6 +41,10 @@ module ScoutApm
|
|
38
41
|
|
39
42
|
private
|
40
43
|
|
44
|
+
def request_over_websocket?
|
45
|
+
headers["Upgrade"] == "websocket"
|
46
|
+
end
|
47
|
+
|
41
48
|
# Looks through the possible headers with this data, and extracts the raw
|
42
49
|
# value of the header
|
43
50
|
# Returns nil if not found
|
@@ -14,7 +14,7 @@ module ScoutApm
|
|
14
14
|
|
15
15
|
def add_default_policies
|
16
16
|
add(SlowPolicy::SpeedPolicy.new(context))
|
17
|
-
add(SlowPolicy::
|
17
|
+
add(SlowPolicy::PercentPolicy.new(context))
|
18
18
|
add(SlowPolicy::AgePolicy.new(context))
|
19
19
|
add(SlowPolicy::PercentilePolicy.new(context))
|
20
20
|
end
|
data/lib/scout_apm/version.rb
CHANGED
data/lib/scout_apm.rb
CHANGED
@@ -67,6 +67,8 @@ require 'scout_apm/background_job_integrations/shoryuken'
|
|
67
67
|
require 'scout_apm/background_job_integrations/sneakers'
|
68
68
|
require 'scout_apm/background_job_integrations/que'
|
69
69
|
require 'scout_apm/background_job_integrations/legacy_sneakers'
|
70
|
+
require 'scout_apm/background_job_integrations/good_job'
|
71
|
+
require 'scout_apm/background_job_integrations/solid_queue'
|
70
72
|
|
71
73
|
require 'scout_apm/framework_integrations/rails_2'
|
72
74
|
require 'scout_apm/framework_integrations/rails_3_or_4'
|
data/scout_apm.gemspec
CHANGED
@@ -31,10 +31,10 @@ Gem::Specification.new do |s|
|
|
31
31
|
s.add_runtime_dependency "parser"
|
32
32
|
|
33
33
|
# These are general development dependencies which are used in instrumentation
|
34
|
-
# tests. Specific versions are pulled in using specific gemfiles, e.g.
|
34
|
+
# tests. Specific versions are pulled in using specific gemfiles, e.g.
|
35
35
|
# `gems/rails3.gemfile`.
|
36
36
|
s.add_development_dependency "activerecord"
|
37
|
-
s.add_development_dependency "sqlite3"
|
37
|
+
s.add_development_dependency "sqlite3", "~> 1.4"
|
38
38
|
|
39
39
|
s.add_development_dependency "rubocop"
|
40
40
|
s.add_development_dependency "guard"
|
data/test/test_helper.rb
CHANGED
@@ -65,6 +65,38 @@ class FakeEnvironment
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
def remove_rails_namespace
|
69
|
+
Object.send(:remove_const, "Rails") if defined?(Rails)
|
70
|
+
end
|
71
|
+
|
72
|
+
def fake_rails(version)
|
73
|
+
remove_rails_namespace if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
|
74
|
+
|
75
|
+
Kernel.const_set("Rails", Module.new)
|
76
|
+
Kernel.const_set("ActionController", Module.new)
|
77
|
+
r = Kernel.const_get("Rails")
|
78
|
+
r.const_set("VERSION", Module.new)
|
79
|
+
v = r.const_get("VERSION")
|
80
|
+
v.const_set("MAJOR", version)
|
81
|
+
|
82
|
+
assert_equal version, Rails::VERSION::MAJOR
|
83
|
+
end
|
84
|
+
|
85
|
+
def clean_fake_rails
|
86
|
+
Kernel.send(:remove_const, "Rails") if defined?(Kernel::Rails)
|
87
|
+
Kernel.send(:remove_const, "ActionController") if defined?(Kernel::ActionController)
|
88
|
+
end
|
89
|
+
|
90
|
+
def fake_sinatra
|
91
|
+
Kernel.const_set("Sinatra", Module.new)
|
92
|
+
s = Kernel.const_get("Sinatra")
|
93
|
+
s.const_set("Base", Module.new)
|
94
|
+
end
|
95
|
+
|
96
|
+
def clean_fake_sinatra
|
97
|
+
Kernel.const_unset("Sinatra") if defined?(Kernel::Sinatra)
|
98
|
+
end
|
99
|
+
|
68
100
|
# Helpers available to all tests
|
69
101
|
class Minitest::Test
|
70
102
|
def setup
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
class HashShorthandController < ApplicationController
|
3
|
+
def hash
|
4
|
+
json = {
|
5
|
+
static: "static",
|
6
|
+
shorthand: ::ScoutApm::AutoInstrument("shorthand:",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:6:in `hash'"]){shorthand},
|
7
|
+
longhand: ::ScoutApm::AutoInstrument("longhand: longhand",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:7:in `hash'"]){longhand},
|
8
|
+
longhand_different_key: ::ScoutApm::AutoInstrument("longhand",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:8:in `hash'"]){longhand},
|
9
|
+
hash_rocket: ::ScoutApm::AutoInstrument(":hash_rocket => hash_rocket",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:9:in `hash'"]){hash_rocket},
|
10
|
+
:hash_rocket_different_key => ::ScoutApm::AutoInstrument("hash_rocket",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:10:in `hash'"]){hash_rocket},
|
11
|
+
non_nil_receiver: ::ScoutApm::AutoInstrument("non_nil_receiver.value",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:11:in `hash'"]){non_nil_receiver.value},
|
12
|
+
nested: {
|
13
|
+
shorthand: ::ScoutApm::AutoInstrument("shorthand:",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:13:in `hash'"]){shorthand},
|
14
|
+
},
|
15
|
+
nested_call: ::ScoutApm::AutoInstrument("nested_call(params[\"timestamp\"])",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:15:in `hash'"]){nested_call(params["timestamp"])}
|
16
|
+
}
|
17
|
+
::ScoutApm::AutoInstrument("render json:",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:17:in `hash'"]){render json:}
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def nested_call(noop)
|
23
|
+
noop
|
24
|
+
end
|
25
|
+
|
26
|
+
def shorthand
|
27
|
+
"shorthand"
|
28
|
+
end
|
29
|
+
|
30
|
+
def longhand
|
31
|
+
"longhand"
|
32
|
+
end
|
33
|
+
|
34
|
+
def hash_rocket
|
35
|
+
"hash_rocket"
|
36
|
+
end
|
37
|
+
|
38
|
+
def non_nil_receiver
|
39
|
+
::ScoutApm::AutoInstrument("OpenStruct.new(value: \"value\")",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:39:in `non_nil_receiver'"]){OpenStruct.new(value: "value")}
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
class HashShorthandController < ApplicationController
|
3
|
+
def hash
|
4
|
+
json = {
|
5
|
+
static: "static",
|
6
|
+
shorthand:,
|
7
|
+
longhand: longhand,
|
8
|
+
longhand_different_key: longhand,
|
9
|
+
:hash_rocket => hash_rocket,
|
10
|
+
:hash_rocket_different_key => hash_rocket,
|
11
|
+
non_nil_receiver: non_nil_receiver.value,
|
12
|
+
nested: {
|
13
|
+
shorthand:,
|
14
|
+
},
|
15
|
+
nested_call: nested_call(params["timestamp"])
|
16
|
+
}
|
17
|
+
render json:
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def nested_call(noop)
|
23
|
+
noop
|
24
|
+
end
|
25
|
+
|
26
|
+
def shorthand
|
27
|
+
"shorthand"
|
28
|
+
end
|
29
|
+
|
30
|
+
def longhand
|
31
|
+
"longhand"
|
32
|
+
end
|
33
|
+
|
34
|
+
def hash_rocket
|
35
|
+
"hash_rocket"
|
36
|
+
end
|
37
|
+
|
38
|
+
def non_nil_receiver
|
39
|
+
OpenStruct.new(value: "value")
|
40
|
+
end
|
41
|
+
end
|
@@ -4,7 +4,7 @@ require 'scout_apm/auto_instrument'
|
|
4
4
|
|
5
5
|
class AutoInstrumentTest < Minitest::Test
|
6
6
|
ROOT = File.expand_path("../../", __dir__)
|
7
|
-
|
7
|
+
|
8
8
|
def source_path(name)
|
9
9
|
File.expand_path("auto_instrument/#{name}.rb", __dir__)
|
10
10
|
end
|
@@ -38,6 +38,12 @@ class AutoInstrumentTest < Minitest::Test
|
|
38
38
|
normalize_backtrace(::ScoutApm::AutoInstrument::Rails.rewrite(source_path("controller")))
|
39
39
|
end
|
40
40
|
|
41
|
+
def test_controller_rewrite_hash_shorthand
|
42
|
+
skip if RUBY_VERSION < "3.1"
|
43
|
+
assert_equal instrumented_source("hash_shorthand_controller"),
|
44
|
+
normalize_backtrace(::ScoutApm::AutoInstrument::Rails.rewrite(source_path("hash_shorthand_controller")))
|
45
|
+
end
|
46
|
+
|
41
47
|
def test_rescue_from_rewrite
|
42
48
|
# update_instrumented_source("rescue_from")
|
43
49
|
|
@@ -29,32 +29,4 @@ class EnvironmentTest < Minitest::Test
|
|
29
29
|
def test_framework_ruby
|
30
30
|
assert_equal :ruby, ScoutApm::Environment.send(:new).framework
|
31
31
|
end
|
32
|
-
|
33
|
-
############################################################
|
34
|
-
|
35
|
-
def fake_rails(version)
|
36
|
-
Kernel.const_set("Rails", Module.new)
|
37
|
-
Kernel.const_set("ActionController", Module.new)
|
38
|
-
r = Kernel.const_get("Rails")
|
39
|
-
r.const_set("VERSION", Module.new)
|
40
|
-
v = r.const_get("VERSION")
|
41
|
-
v.const_set("MAJOR", version)
|
42
|
-
|
43
|
-
assert_equal version, Rails::VERSION::MAJOR
|
44
|
-
end
|
45
|
-
|
46
|
-
def clean_fake_rails
|
47
|
-
Kernel.send(:remove_const, "Rails") if defined?(Kernel::Rails)
|
48
|
-
Kernel.send(:remove_const, "ActionController") if defined?(Kernel::ActionController)
|
49
|
-
end
|
50
|
-
|
51
|
-
def fake_sinatra
|
52
|
-
Kernel.const_set("Sinatra", Module.new)
|
53
|
-
s = Kernel.const_get("Sinatra")
|
54
|
-
s.const_set("Base", Module.new)
|
55
|
-
end
|
56
|
-
|
57
|
-
def clean_fake_sinatra
|
58
|
-
Kernel.const_unset("Sinatra") if defined?(Kernel::Sinatra)
|
59
|
-
end
|
60
32
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Most of this was taken from Rails:
|
2
|
+
# https://github.com/rails/rails/blob/v7.1.3/actionview/test/actionpack/controller/render_test.rb
|
3
|
+
# https://github.com/rails/rails/blob/v7.1.3/actionview/test/abstract_unit.rb
|
4
|
+
|
5
|
+
if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
|
6
|
+
require 'test_helper'
|
7
|
+
require 'action_view'
|
8
|
+
require 'action_pack'
|
9
|
+
require 'action_controller'
|
10
|
+
|
11
|
+
FIXTURE_LOAD_PATH = File.expand_path("fixtures", __dir__)
|
12
|
+
|
13
|
+
include ActionView::Context
|
14
|
+
include ActionView::Helpers::TagHelper
|
15
|
+
include ActionView::Helpers::TextHelper
|
16
|
+
|
17
|
+
module ActionController
|
18
|
+
|
19
|
+
class Base
|
20
|
+
self.view_paths = FIXTURE_LOAD_PATH
|
21
|
+
|
22
|
+
def self.test_routes(&block)
|
23
|
+
routes = ActionDispatch::Routing::RouteSet.new
|
24
|
+
routes.draw(&block)
|
25
|
+
include routes.url_helpers
|
26
|
+
routes
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class TestCase
|
31
|
+
include ActionDispatch::TestProcess
|
32
|
+
|
33
|
+
def self.with_routes(&block)
|
34
|
+
routes = ActionDispatch::Routing::RouteSet.new
|
35
|
+
routes.draw(&block)
|
36
|
+
include Module.new {
|
37
|
+
define_method(:setup) do
|
38
|
+
super()
|
39
|
+
@routes = routes
|
40
|
+
@controller.singleton_class.include @routes.url_helpers if @controller
|
41
|
+
end
|
42
|
+
}
|
43
|
+
routes
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class TestController < ActionController::Base
|
49
|
+
|
50
|
+
def render_test_view
|
51
|
+
render template: "test_view"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class RenderTest < ActionController::TestCase
|
56
|
+
|
57
|
+
tests TestController
|
58
|
+
|
59
|
+
with_routes do
|
60
|
+
get :render_test_view, to: "test#render_test_view"
|
61
|
+
end
|
62
|
+
|
63
|
+
def setup
|
64
|
+
super
|
65
|
+
@controller.logger = ActiveSupport::Logger.new(nil)
|
66
|
+
ActionView::Base.logger = ActiveSupport::Logger.new(nil)
|
67
|
+
|
68
|
+
@request.host = "www.scoutapm.com"
|
69
|
+
|
70
|
+
@old_view_paths = ActionController::Base.view_paths
|
71
|
+
ActionController::Base.view_paths = FIXTURE_LOAD_PATH
|
72
|
+
end
|
73
|
+
|
74
|
+
def teardown
|
75
|
+
ActionView::Base.logger = nil
|
76
|
+
|
77
|
+
ActionController::Base.view_paths = @old_view_paths
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_partial_instrumentation
|
81
|
+
recorder = FakeRecorder.new
|
82
|
+
agent_context.recorder = recorder
|
83
|
+
|
84
|
+
instrument = ScoutApm::Instruments::ActionView.new(agent_context)
|
85
|
+
instrument.install(prepend: true)
|
86
|
+
|
87
|
+
get :render_test_view
|
88
|
+
assert_response :success
|
89
|
+
|
90
|
+
root_layer = recorder.requests.first.root_layer
|
91
|
+
children = root_layer.children.to_a
|
92
|
+
assert_equal 2, children.size
|
93
|
+
|
94
|
+
partial_layer = children[0]
|
95
|
+
collection_layer = children[1]
|
96
|
+
|
97
|
+
assert_equal "test_view/Rendering", root_layer.name
|
98
|
+
assert_equal "test/_test_partial/Rendering", partial_layer.name
|
99
|
+
assert_equal "test/_test_partial_collection/Rendering", collection_layer.name
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -24,6 +24,36 @@ class ActiveRecordTest < Minitest::Test
|
|
24
24
|
class User < ActiveRecord::Base
|
25
25
|
end
|
26
26
|
|
27
|
+
class DumbRailsConfig
|
28
|
+
def self.after_initialize; end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_old_rails_initialization
|
32
|
+
recorder = FakeRecorder.new
|
33
|
+
agent_context.recorder = recorder
|
34
|
+
old_rails_version = (1..2).to_a.sample
|
35
|
+
fake_rails(old_rails_version)
|
36
|
+
|
37
|
+
::Rails.expects(:configuration).never
|
38
|
+
|
39
|
+
instrument = ScoutApm::Instruments::ActiveRecord.new(agent_context)
|
40
|
+
instrument.install(prepend: false)
|
41
|
+
clean_fake_rails
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_modern_rails_initialization
|
45
|
+
recorder = FakeRecorder.new
|
46
|
+
agent_context.recorder = recorder
|
47
|
+
modern_rails_version = (3..7).to_a.sample
|
48
|
+
fake_rails(modern_rails_version)
|
49
|
+
|
50
|
+
::Rails.expects(:configuration).returns(DumbRailsConfig).once
|
51
|
+
|
52
|
+
instrument = ScoutApm::Instruments::ActiveRecord.new(agent_context)
|
53
|
+
instrument.install(prepend: false)
|
54
|
+
clean_fake_rails
|
55
|
+
end
|
56
|
+
|
27
57
|
def test_instrumentation
|
28
58
|
recorder = FakeRecorder.new
|
29
59
|
agent_context.recorder = recorder
|
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: 5.
|
4
|
+
version: 5.4.0
|
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:
|
12
|
+
date: 2024-09-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -141,16 +141,16 @@ dependencies:
|
|
141
141
|
name: sqlite3
|
142
142
|
requirement: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
|
-
- - "
|
144
|
+
- - "~>"
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
version: '
|
146
|
+
version: '1.4'
|
147
147
|
type: :development
|
148
148
|
prerelease: false
|
149
149
|
version_requirements: !ruby/object:Gem::Requirement
|
150
150
|
requirements:
|
151
|
-
- - "
|
151
|
+
- - "~>"
|
152
152
|
- !ruby/object:Gem::Version
|
153
|
-
version: '
|
153
|
+
version: '1.4'
|
154
154
|
- !ruby/object:Gem::Dependency
|
155
155
|
name: rubocop
|
156
156
|
requirement: !ruby/object:Gem::Requirement
|
@@ -216,6 +216,7 @@ extensions:
|
|
216
216
|
- ext/rusage/extconf.rb
|
217
217
|
extra_rdoc_files: []
|
218
218
|
files:
|
219
|
+
- ".github/workflows/release.yml"
|
219
220
|
- ".github/workflows/test.yml"
|
220
221
|
- ".gitignore"
|
221
222
|
- ".rubocop.yml"
|
@@ -239,6 +240,7 @@ files:
|
|
239
240
|
- gems/rails5.gemfile
|
240
241
|
- gems/rails6.gemfile
|
241
242
|
- gems/sidekiq.gemfile
|
243
|
+
- gems/sqlite3-1.3.gemfile
|
242
244
|
- gems/typhoeus.gemfile
|
243
245
|
- lib/scout_apm.rb
|
244
246
|
- lib/scout_apm/agent.rb
|
@@ -254,12 +256,14 @@ files:
|
|
254
256
|
- lib/scout_apm/auto_instrument/rails.rb
|
255
257
|
- lib/scout_apm/background_job_integrations/delayed_job.rb
|
256
258
|
- lib/scout_apm/background_job_integrations/faktory.rb
|
259
|
+
- lib/scout_apm/background_job_integrations/good_job.rb
|
257
260
|
- lib/scout_apm/background_job_integrations/legacy_sneakers.rb
|
258
261
|
- lib/scout_apm/background_job_integrations/que.rb
|
259
262
|
- lib/scout_apm/background_job_integrations/resque.rb
|
260
263
|
- lib/scout_apm/background_job_integrations/shoryuken.rb
|
261
264
|
- lib/scout_apm/background_job_integrations/sidekiq.rb
|
262
265
|
- lib/scout_apm/background_job_integrations/sneakers.rb
|
266
|
+
- lib/scout_apm/background_job_integrations/solid_queue.rb
|
263
267
|
- lib/scout_apm/background_recorder.rb
|
264
268
|
- lib/scout_apm/background_worker.rb
|
265
269
|
- lib/scout_apm/bucket_name_splitter.rb
|
@@ -423,6 +427,8 @@ files:
|
|
423
427
|
- test/unit/auto_instrument/controller-instrumented.rb
|
424
428
|
- test/unit/auto_instrument/controller.rb
|
425
429
|
- test/unit/auto_instrument/hanging_method.rb
|
430
|
+
- test/unit/auto_instrument/hash_shorthand_controller-instrumented.rb
|
431
|
+
- test/unit/auto_instrument/hash_shorthand_controller.rb
|
426
432
|
- test/unit/auto_instrument/rescue_from-instrumented.rb
|
427
433
|
- test/unit/auto_instrument/rescue_from.rb
|
428
434
|
- test/unit/auto_instrument_test.rb
|
@@ -442,7 +448,11 @@ files:
|
|
442
448
|
- test/unit/git_revision_test.rb
|
443
449
|
- test/unit/histogram_test.rb
|
444
450
|
- test/unit/ignored_uris_test.rb
|
451
|
+
- test/unit/instruments/action_view_test.rb
|
445
452
|
- test/unit/instruments/active_record_test.rb
|
453
|
+
- test/unit/instruments/fixtures/test/_test_partial.html.erb
|
454
|
+
- test/unit/instruments/fixtures/test/_test_partial_collection.html.erb
|
455
|
+
- test/unit/instruments/fixtures/test_view.html.erb
|
446
456
|
- test/unit/instruments/http_client_test.rb
|
447
457
|
- test/unit/instruments/http_test.rb
|
448
458
|
- test/unit/instruments/moped_test.rb
|
@@ -495,7 +505,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
495
505
|
- !ruby/object:Gem::Version
|
496
506
|
version: '0'
|
497
507
|
requirements: []
|
498
|
-
rubygems_version: 3.
|
508
|
+
rubygems_version: 3.5.16
|
499
509
|
signing_key:
|
500
510
|
specification_version: 4
|
501
511
|
summary: Ruby application performance monitoring
|