scout_apm 5.3.5 → 5.3.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 656e36a7b9a11b5870058c46b31e8227c628691f8ab7f7d4de991c3e89b5f13d
4
- data.tar.gz: 5f27c0af5b3f864a96ea65944c6655106a86aa96dbf489083b1c3a178322d827
3
+ metadata.gz: 8eb7dc8091eba4caa3dfb078b44636de67c94abc26646c05c14339e77ba0b58a
4
+ data.tar.gz: 7e7a596d825cf2c648470f6e867ea2c5cc3eb9112b813b4b4ca4dcf873a2b98c
5
5
  SHA512:
6
- metadata.gz: b805ac798aa0f430182e31e9d4b3d924d20624a59406d333f8d8d02ead0d30bece36bf0b127b4b9e04a5cf149cc9e67621ab72f2143f7fcd5c03f70790fedf09
7
- data.tar.gz: 6883c955de7003baeb2685c369f63532b45e1c25b5859067fdfb2112e31abae6f57e2dc44dd5b06f55d66f3a6b9b778904de864c45905a63cdd9516719d59367
6
+ metadata.gz: 9964722b976e23fc9a629cdb010079f4781e10764226bb8eeb9d6da4439612d20fe21c9fa13f8e1a7fbe8db268ff15005599d102893ee5ed44388ef686874b44
7
+ data.tar.gz: 1b9a9a0847c9f688fe34068cd68e0cd9d4bcac0e70eb3a59c507a2671d03aa2ac30bb7a8a1eea9f1df6d9ec5e4a4a713046707fe60702ff06b353df634e5cce2
data/CHANGELOG.markdown CHANGED
@@ -1,5 +1,16 @@
1
1
  # Unreleased
2
2
 
3
+ # 5.3.8
4
+ * Avoid inaccurate websocket queue time capturing (#494)
5
+
6
+ # 5.3.7
7
+ * Fix parser dependency issue
8
+
9
+ # 5.3.6
10
+ * Fix AutoInstruments when instrumenting hash with shorthand (#486)
11
+ * Fix Connection Handling deprecation in ActiveRecord for Rails 7.1 (#483)
12
+ * Update ActionView partial instrumentation for Rails 7 (#487)
13
+
3
14
  # 5.3.5
4
15
  * Fix adding instrumentation of ActiveRecord after configuration has initialized for Rails versions greater than 3. (#465)
5
16
  * Fix typo with double use of PercentilePolicy, instead of PercentPolicy, for scoring. (#468)
data/README.markdown CHANGED
@@ -1,6 +1,6 @@
1
1
  # ScoutApm Ruby Agent
2
2
 
3
- [![Build Status](https://travis-ci.org/scoutapp/scout_apm_ruby.svg?branch=master)](https://travis-ci.org/scoutapp/scout_apm_ruby)
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
@@ -4,3 +4,5 @@ gem 'httpclient'
4
4
  gem 'http'
5
5
  gem 'redis'
6
6
  gem 'moped'
7
+ gem 'actionpack'
8
+ gem 'actionview'
@@ -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
  #
@@ -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.3.5
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.3.5
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 -> 6.1.3
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
 
@@ -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
@@ -1,3 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "5.3.5"
2
+ VERSION = "5.3.8"
3
3
  end
data/scout_apm.gemspec CHANGED
@@ -31,7 +31,7 @@ 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
37
  s.add_development_dependency "sqlite3"
data/test/test_helper.rb CHANGED
@@ -65,7 +65,13 @@ 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
+
68
72
  def fake_rails(version)
73
+ remove_rails_namespace if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
74
+
69
75
  Kernel.const_set("Rails", Module.new)
70
76
  Kernel.const_set("ActionController", Module.new)
71
77
  r = Kernel.const_get("Rails")
@@ -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
 
@@ -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
@@ -0,0 +1,3 @@
1
+ <div>
2
+ <p><%= message %></p>
3
+ </div>
@@ -0,0 +1,3 @@
1
+ <div>
2
+ <p><%= index %></p>
3
+ </div>
@@ -0,0 +1,10 @@
1
+ <html>
2
+ <head>
3
+ <title>View</title>
4
+ </head>
5
+ <body>
6
+ <h1>View</h1>
7
+ <%= render partial: "test_partial", locals: { message: 'Partial' } %>
8
+ <%= render partial: "test_partial_collection", collection: [1, 2, 3], as: :index %>
9
+ </body>
10
+ </html>
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.3.5
4
+ version: 5.3.8
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: 2023-05-17 00:00:00.000000000 Z
12
+ date: 2024-04-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -423,6 +423,8 @@ files:
423
423
  - test/unit/auto_instrument/controller-instrumented.rb
424
424
  - test/unit/auto_instrument/controller.rb
425
425
  - test/unit/auto_instrument/hanging_method.rb
426
+ - test/unit/auto_instrument/hash_shorthand_controller-instrumented.rb
427
+ - test/unit/auto_instrument/hash_shorthand_controller.rb
426
428
  - test/unit/auto_instrument/rescue_from-instrumented.rb
427
429
  - test/unit/auto_instrument/rescue_from.rb
428
430
  - test/unit/auto_instrument_test.rb
@@ -442,7 +444,11 @@ files:
442
444
  - test/unit/git_revision_test.rb
443
445
  - test/unit/histogram_test.rb
444
446
  - test/unit/ignored_uris_test.rb
447
+ - test/unit/instruments/action_view_test.rb
445
448
  - test/unit/instruments/active_record_test.rb
449
+ - test/unit/instruments/fixtures/test/_test_partial.html.erb
450
+ - test/unit/instruments/fixtures/test/_test_partial_collection.html.erb
451
+ - test/unit/instruments/fixtures/test_view.html.erb
446
452
  - test/unit/instruments/http_client_test.rb
447
453
  - test/unit/instruments/http_test.rb
448
454
  - test/unit/instruments/moped_test.rb
@@ -512,6 +518,8 @@ test_files:
512
518
  - test/unit/auto_instrument/controller-instrumented.rb
513
519
  - test/unit/auto_instrument/controller.rb
514
520
  - test/unit/auto_instrument/hanging_method.rb
521
+ - test/unit/auto_instrument/hash_shorthand_controller-instrumented.rb
522
+ - test/unit/auto_instrument/hash_shorthand_controller.rb
515
523
  - test/unit/auto_instrument/rescue_from-instrumented.rb
516
524
  - test/unit/auto_instrument/rescue_from.rb
517
525
  - test/unit/auto_instrument_test.rb
@@ -531,7 +539,11 @@ test_files:
531
539
  - test/unit/git_revision_test.rb
532
540
  - test/unit/histogram_test.rb
533
541
  - test/unit/ignored_uris_test.rb
542
+ - test/unit/instruments/action_view_test.rb
534
543
  - test/unit/instruments/active_record_test.rb
544
+ - test/unit/instruments/fixtures/test/_test_partial.html.erb
545
+ - test/unit/instruments/fixtures/test/_test_partial_collection.html.erb
546
+ - test/unit/instruments/fixtures/test_view.html.erb
535
547
  - test/unit/instruments/http_client_test.rb
536
548
  - test/unit/instruments/http_test.rb
537
549
  - test/unit/instruments/moped_test.rb