scout_apm 5.2.0 → 5.3.0

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +12 -0
  3. data/CHANGELOG.markdown +8 -0
  4. data/gems/instruments.gemfile +6 -0
  5. data/lib/scout_apm/config.rb +16 -1
  6. data/lib/scout_apm/instrument_manager.rb +18 -1
  7. data/lib/scout_apm/instruments/action_controller_rails_2.rb +1 -1
  8. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +1 -1
  9. data/lib/scout_apm/instruments/action_view.rb +1 -1
  10. data/lib/scout_apm/instruments/active_record.rb +1 -1
  11. data/lib/scout_apm/instruments/elasticsearch.rb +91 -42
  12. data/lib/scout_apm/instruments/grape.rb +1 -1
  13. data/lib/scout_apm/instruments/http.rb +36 -16
  14. data/lib/scout_apm/instruments/http_client.rb +33 -14
  15. data/lib/scout_apm/instruments/influxdb.rb +2 -2
  16. data/lib/scout_apm/instruments/memcached.rb +26 -11
  17. data/lib/scout_apm/instruments/middleware_detailed.rb +1 -1
  18. data/lib/scout_apm/instruments/middleware_summary.rb +1 -1
  19. data/lib/scout_apm/instruments/mongoid.rb +1 -1
  20. data/lib/scout_apm/instruments/moped.rb +44 -19
  21. data/lib/scout_apm/instruments/net_http.rb +49 -21
  22. data/lib/scout_apm/instruments/rails_router.rb +1 -1
  23. data/lib/scout_apm/instruments/redis.rb +26 -11
  24. data/lib/scout_apm/instruments/sinatra.rb +1 -1
  25. data/lib/scout_apm/instruments/typhoeus.rb +1 -1
  26. data/lib/scout_apm/version.rb +1 -1
  27. data/test/unit/instruments/active_record_test.rb +1 -1
  28. data/test/unit/instruments/http_client_test.rb +24 -0
  29. data/test/unit/instruments/http_test.rb +24 -0
  30. data/test/unit/instruments/moped_test.rb +24 -0
  31. data/test/unit/instruments/net_http_test.rb +11 -1
  32. data/test/unit/instruments/redis_test.rb +24 -0
  33. data/test/unit/instruments/typhoeus_test.rb +1 -1
  34. metadata +11 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 81ad3b683899407ed049b7267d6d79a84d5afac011445d1078f4342c345c7ace
4
- data.tar.gz: 29e141854923fa21c57ca3fcc7ca9e4cc96dbd741c36c0586e7fd2cd7a8186f8
3
+ metadata.gz: 4a454dcd926d9c3d9a9432c43ee101bfb9305b2ed181193d55a51fd8d525f3d8
4
+ data.tar.gz: 9bfdbfb6852b7b933f83f947cb225fe1096083c6a4fe44a0620ff888ee01e26d
5
5
  SHA512:
6
- metadata.gz: a857678f6d91c709fbbdfb30409339f37e99cd83260cec9d3808f81638245de03cf4b0721270203f0ee619cc56b06fc3baa795a10e859c3068e0e2c36a2247b2
7
- data.tar.gz: 972da0997d251c5704291e60e390916a417de3036aadcada469540acd20520f4edd95b3b76da9202734755bc37c3a9a22e44916c23e74c9199d3c6a7a2447797
6
+ metadata.gz: 29012317cfdcb2958395ca541ec628d4f8d3d2ee5278428075930be30cb9fa445c23463cc11f32dab39649a5b34fd6caf6f981eee7c5c88ef1ade39c36e5806d
7
+ data.tar.gz: a45e503bcd08fa16cbc885133efcc2bbea64bf76eff6a9de7d5c35a7ddce4ea5a059904c902de6afacba0daba5128b62ce477565aecf2512391962cfea729623
@@ -35,7 +35,18 @@ jobs:
35
35
  gemfile: gems/rails3.gemfile
36
36
  bundler: 1.17.3
37
37
  - ruby: 2.7
38
+ - ruby: 2.7
39
+ prepend: true
40
+ - ruby: 3.0
41
+ - ruby: 3.0
42
+ prepend: true
43
+ - ruby: 3.0
44
+ gemfile: gems/instruments.gemfile
45
+ test_features: "instruments"
38
46
  - ruby: 3.0
47
+ gemfile: gems/instruments.gemfile
48
+ prepend: true
49
+ test_features: "instruments"
39
50
  - ruby: 3.0
40
51
  gemfile: gems/sidekiq.gemfile
41
52
  test_features: "sidekiq_install"
@@ -43,6 +54,7 @@ jobs:
43
54
  env:
44
55
  BUNDLE_GEMFILE: ${{ matrix.gemfile }}
45
56
  SCOUT_TEST_FEATURES: ${{ matrix.test_features }}
57
+ SCOUT_USE_PREPEND: ${{ matrix.prepend }}
46
58
 
47
59
  runs-on: ubuntu-latest
48
60
 
data/CHANGELOG.markdown CHANGED
@@ -1,5 +1,13 @@
1
1
  # Unreleased
2
2
 
3
+ # 5.3.0
4
+
5
+ * Add configuraiton option to use `Module#prepend` instead of `Module#alias_method` (default)
6
+ for instrumentation (#448). The default method for instrumentation has not changed, but
7
+ configuration options were added to allow switching to `Module#prepend` for most
8
+ instrumentation. Refer to the documentation for more information:
9
+ [Library Instrumentation Method](https://scoutapm.com/docs/ruby/configuration#library-instrumentation-method)
10
+
3
11
  # 5.2.0
4
12
 
5
13
  * Use Sidekiq lifecycle hooks to start Scout agent on Sidekiq start. (#449)
@@ -0,0 +1,6 @@
1
+ eval_gemfile("../Gemfile")
2
+
3
+ gem 'httpclient'
4
+ gem 'http'
5
+ gem 'redis'
6
+ gem 'moped'
@@ -35,7 +35,13 @@ require 'scout_apm/environment'
35
35
  # start_resque_server_instrument - Used in special situations with certain Resque installs
36
36
  # timeline_traces - true/false to enable sending of of the timeline trace format.
37
37
  # auto_instruments - true/false whether to install autoinstruments. Only installed if on a supported Ruby version.
38
- # auto_instruments_ignore - An array of file names to exclude from autoinstruments (Ex: ['application_controller']).
38
+ # auto_instruments_ignore - An array of file names to exclude from autoinstruments (Ex: ['application_controller']).
39
+ # use_prepend - Whether to apply instrumentation using Module#Prepend instead
40
+ # of Module#alias_method (Default: false)
41
+ # alias_method_instruments - If `use_prepend` is true, continue to use Module#alias_method for
42
+ # any instruments listed in this array. Default: []
43
+ # prepend_instruments - If `use_prepend` is false, force using Module#prepend for any
44
+ # instruments listed in this array. Default: []
39
45
  #
40
46
  # Any of these config settings can be set with an environment variable prefixed
41
47
  # by SCOUT_ and uppercasing the key: SCOUT_LOG_LEVEL for instance.
@@ -85,6 +91,9 @@ module ScoutApm
85
91
  'timeline_traces',
86
92
  'auto_instruments',
87
93
  'auto_instruments_ignore',
94
+ 'use_prepend',
95
+ 'alias_method_instruments',
96
+ 'prepend_instruments',
88
97
 
89
98
  # Error Service Related Configuration
90
99
  'errors_enabled',
@@ -189,6 +198,9 @@ module ScoutApm
189
198
  'timeline_traces' => BooleanCoercion.new,
190
199
  'auto_instruments' => BooleanCoercion.new,
191
200
  'auto_instruments_ignore' => JsonCoercion.new,
201
+ 'use_prepend' => BooleanCoercion.new,
202
+ 'alias_method_instruments' => JsonCoercion.new,
203
+ 'prepend_instruments' => JsonCoercion.new,
192
204
  'errors_enabled' => BooleanCoercion.new,
193
205
  'errors_ignored_exceptions' => JsonCoercion.new,
194
206
  'errors_filtered_params' => JsonCoercion.new,
@@ -305,6 +317,9 @@ module ScoutApm
305
317
  'timeline_traces' => true,
306
318
  'auto_instruments' => false,
307
319
  'auto_instruments_ignore' => [],
320
+ 'use_prepend' => false,
321
+ 'alias_method_instruments' => [],
322
+ 'prepend_instruments' => [],
308
323
  'ssl_cert_file' => File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] ),
309
324
  'errors_enabled' => false,
310
325
  'errors_ignored_exceptions' => %w(ActiveRecord::RecordNotFound ActionController::RoutingError),
@@ -50,6 +50,23 @@ module ScoutApm
50
50
  (config.value("disabled_instruments") || []).include?(instrument_short_name)
51
51
  end
52
52
 
53
+ def prepend_for_instrument?(instrument_klass)
54
+ instrument_short_name = instrument_klass.name.split("::").last
55
+
56
+ # `use_prepend` defaults to false, which means we use `alias_method` by default.
57
+ # If `use_prepend` is `true`, then we should default to using `prepend` unless
58
+ # the instrument is explicitly listed in the `alias_method_instruments` config array.
59
+ if config.value("use_prepend")
60
+ return false if (config.value("alias_method_instruments") || []).include?(instrument_short_name)
61
+ return true
62
+ else
63
+ # `use_prepend` is false, but we should use `prepend` if the instrument is
64
+ # explicitly listed in the `prepend_instruments` array.
65
+ return true if (config.value("prepend_instruments") || []).include?(instrument_short_name)
66
+ return false
67
+ end
68
+ end
69
+
53
70
  private
54
71
 
55
72
  def install_instrument(instrument_klass)
@@ -62,7 +79,7 @@ module ScoutApm
62
79
 
63
80
  instance = instrument_klass.new(context)
64
81
  @installed_instruments << instance
65
- instance.install
82
+ instance.install(prepend: prepend_for_instrument?(instrument_klass))
66
83
  end
67
84
 
68
85
  def already_installed?(instrument_klass)
@@ -16,7 +16,7 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(::ActionController) && defined?(::ActionController::Base)
21
21
  @installed = true
22
22
 
@@ -21,7 +21,7 @@ module ScoutApm
21
21
  @installed = true
22
22
  end
23
23
 
24
- def install
24
+ def install(prepend:)
25
25
  if !defined?(::ActiveSupport)
26
26
  return
27
27
  end
@@ -29,7 +29,7 @@ module ScoutApm
29
29
  context.environment.supports_module_prepend?
30
30
  end
31
31
 
32
- def install
32
+ def install(prepend:)
33
33
  return unless defined?(::ActionView) && defined?(::ActionView::PartialRenderer)
34
34
 
35
35
  if prependable?
@@ -50,7 +50,7 @@ module ScoutApm
50
50
  @installed
51
51
  end
52
52
 
53
- def install
53
+ def install(prepend:)
54
54
  if install_via_after_initialize?
55
55
  Rails.configuration.after_initialize do
56
56
  add_instruments
@@ -18,66 +18,115 @@ module ScoutApm
18
18
  @installed
19
19
  end
20
20
 
21
- def install
21
+ def install(prepend:)
22
22
  if defined?(::Elasticsearch) &&
23
23
  defined?(::Elasticsearch::Transport) &&
24
24
  defined?(::Elasticsearch::Transport::Client)
25
25
 
26
26
  @installed = true
27
27
 
28
- logger.info "Instrumenting Elasticsearch"
28
+ logger.info "Instrumenting Elasticsearch. Prepend: #{prepend}"
29
29
 
30
- ::Elasticsearch::Transport::Client.class_eval do
31
- include ScoutApm::Tracer
30
+ if prepend
31
+ ::Elasticsearch::Transport::Client.send(:include, ScoutApm::Tracer)
32
+ ::Elasticsearch::Transport::Client.send(:prepend, ElasticsearchTransportClientInstrumentationPrepend)
33
+ else
34
+ ::Elasticsearch::Transport::Client.class_eval do
35
+ include ScoutApm::Tracer
32
36
 
33
- def perform_request_with_scout_instruments(*args, &block)
34
- name = _sanitize_name(args[1])
37
+ def perform_request_with_scout_instruments(*args, &block)
38
+ name = _sanitize_name(args[1])
35
39
 
36
- self.class.instrument("Elasticsearch", name, :ignore_children => true) do
37
- perform_request_without_scout_instruments(*args, &block)
40
+ self.class.instrument("Elasticsearch", name, :ignore_children => true) do
41
+ perform_request_without_scout_instruments(*args, &block)
42
+ end
38
43
  end
39
- end
40
44
 
41
- alias_method :perform_request_without_scout_instruments, :perform_request
42
- alias_method :perform_request, :perform_request_with_scout_instruments
43
-
44
- def _sanitize_name(name)
45
- name = name.split("/").last.gsub(/^_/, '')
46
- allowed_names = ["bench",
47
- "bulk",
48
- "count",
49
- "exists",
50
- "explain",
51
- "field_stats",
52
- "health",
53
- "mget",
54
- "mlt",
55
- "mpercolate",
56
- "msearch",
57
- "mtermvectors",
58
- "percolate",
59
- "query",
60
- "scroll",
61
- "search_shards",
62
- "source",
63
- "suggest",
64
- "template",
65
- "termvectors",
66
- "update",
67
- "search", ]
68
-
69
- if allowed_names.include?(name)
70
- name
71
- else
45
+ alias_method :perform_request_without_scout_instruments, :perform_request
46
+ alias_method :perform_request, :perform_request_with_scout_instruments
47
+
48
+ def _sanitize_name(name)
49
+ name = name.split("/").last.gsub(/^_/, '')
50
+ allowed_names = ["bench",
51
+ "bulk",
52
+ "count",
53
+ "exists",
54
+ "explain",
55
+ "field_stats",
56
+ "health",
57
+ "mget",
58
+ "mlt",
59
+ "mpercolate",
60
+ "msearch",
61
+ "mtermvectors",
62
+ "percolate",
63
+ "query",
64
+ "scroll",
65
+ "search_shards",
66
+ "source",
67
+ "suggest",
68
+ "template",
69
+ "termvectors",
70
+ "update",
71
+ "search", ]
72
+
73
+ if allowed_names.include?(name)
74
+ name
75
+ else
76
+ "Unknown"
77
+ end
78
+ rescue
72
79
  "Unknown"
73
80
  end
74
- rescue
75
- "Unknown"
76
81
  end
77
82
  end
78
83
  end
79
84
  end
80
85
  end
86
+
87
+ module ElasticsearchTransportClientInstrumentationPrepend
88
+ def perform_request(*args, &block)
89
+ name = _sanitize_name(args[1])
90
+
91
+ self.class.instrument("Elasticsearch", name, :ignore_children => true) do
92
+ super(*args, &block)
93
+ end
94
+ end
95
+
96
+ def _sanitize_name(name)
97
+ name = name.split("/").last.gsub(/^_/, '')
98
+ allowed_names = ["bench",
99
+ "bulk",
100
+ "count",
101
+ "exists",
102
+ "explain",
103
+ "field_stats",
104
+ "health",
105
+ "mget",
106
+ "mlt",
107
+ "mpercolate",
108
+ "msearch",
109
+ "mtermvectors",
110
+ "percolate",
111
+ "query",
112
+ "scroll",
113
+ "search_shards",
114
+ "source",
115
+ "suggest",
116
+ "template",
117
+ "termvectors",
118
+ "update",
119
+ "search", ]
120
+
121
+ if allowed_names.include?(name)
122
+ name
123
+ else
124
+ "Unknown"
125
+ end
126
+ rescue
127
+ "Unknown"
128
+ end
129
+ end
81
130
  end
82
131
  end
83
132
 
@@ -16,7 +16,7 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(::Grape) && defined?(::Grape::Endpoint)
21
21
  @installed = true
22
22
 
@@ -16,33 +16,53 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(::HTTP) && defined?(::HTTP::Client)
21
21
  @installed = true
22
22
 
23
- logger.info "Instrumenting HTTP::Client"
23
+ logger.info "Instrumenting HTTP::Client. Prepend: #{prepend}"
24
24
 
25
- ::HTTP::Client.class_eval do
26
- include ScoutApm::Tracer
25
+ if prepend
26
+ ::HTTP::Client.send(:include, ScoutApm::Tracer)
27
+ ::HTTP::Client.send(:prepend, HTTPInstrumentationPrepend)
28
+ else
29
+ ::HTTP::Client.class_eval do
30
+ include ScoutApm::Tracer
27
31
 
28
- def request_with_scout_instruments(verb, uri, opts = {})
29
- self.class.instrument("HTTP", verb, :ignore_children => true, :desc => request_scout_description(verb, uri)) do
30
- request_without_scout_instruments(verb, uri, opts)
32
+ def request_with_scout_instruments(verb, uri, opts = {})
33
+ self.class.instrument("HTTP", verb, :ignore_children => true, :desc => request_scout_description(verb, uri)) do
34
+ request_without_scout_instruments(verb, uri, opts)
35
+ end
31
36
  end
32
- end
33
37
 
34
- def request_scout_description(verb, uri)
35
- max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
36
- (String(uri).split('?').first)[0..(max_length - 1)]
37
- rescue
38
- ""
39
- end
38
+ def request_scout_description(verb, uri)
39
+ max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
40
+ (String(uri).split('?').first)[0..(max_length - 1)]
41
+ rescue
42
+ ""
43
+ end
40
44
 
41
- alias request_without_scout_instruments request
42
- alias request request_with_scout_instruments
45
+ alias request_without_scout_instruments request
46
+ alias request request_with_scout_instruments
47
+ end
43
48
  end
44
49
  end
45
50
  end
46
51
  end
52
+
53
+ module HTTPInstrumentationPrepend
54
+ def request(verb, uri, opts = {})
55
+ self.class.instrument("HTTP", verb, :ignore_children => true, :desc => request_scout_description(verb, uri)) do
56
+ super(verb, uri, opts)
57
+ end
58
+ end
59
+
60
+ def request_scout_description(verb, uri)
61
+ max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
62
+ (String(uri).split('?').first)[0..(max_length - 1)]
63
+ rescue
64
+ ""
65
+ end
66
+ end
47
67
  end
48
68
  end
@@ -16,33 +16,52 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(::HTTPClient)
21
21
  @installed = true
22
22
 
23
- logger.info "Instrumenting HTTPClient"
23
+ logger.info "Instrumenting HTTPClient. Prepend: #{prepend}"
24
24
 
25
- ::HTTPClient.class_eval do
26
- include ScoutApm::Tracer
25
+ if prepend
26
+ ::HTTPClient.send(:include, ScoutApm::Tracer)
27
+ ::HTTPClient.send(:prepend, HttpClientInstrumentationPrepend)
28
+ else
29
+ ::HTTPClient.class_eval do
30
+ include ScoutApm::Tracer
27
31
 
28
- def request_with_scout_instruments(*args, &block)
32
+ def request_with_scout_instruments(*args, &block)
29
33
 
30
- method = args[0].to_s
31
- url = args[1]
34
+ method = args[0].to_s
35
+ url = args[1]
32
36
 
33
- max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
34
- url = url && url.to_s[0..(max_length - 1)]
37
+ max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
38
+ url = url && url.to_s[0..(max_length - 1)]
35
39
 
36
- self.class.instrument("HTTP", method, :desc => url) do
37
- request_without_scout_instruments(*args, &block)
40
+ self.class.instrument("HTTP", method, :desc => url) do
41
+ request_without_scout_instruments(*args, &block)
42
+ end
38
43
  end
39
- end
40
44
 
41
- alias request_without_scout_instruments request
42
- alias request request_with_scout_instruments
45
+ alias request_without_scout_instruments request
46
+ alias request request_with_scout_instruments
47
+ end
43
48
  end
44
49
  end
45
50
  end
46
51
  end
52
+
53
+ module HttpClientInstrumentationPrepend
54
+ def request(*args, &block)
55
+ method = args[0].to_s
56
+ url = args[1]
57
+
58
+ max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
59
+ url = url && url.to_s[0..(max_length - 1)]
60
+
61
+ self.class.instrument("HTTP", method, :desc => url) do
62
+ super(request, *args, &block)
63
+ end
64
+ end
65
+ end
47
66
  end
48
67
  end
@@ -16,11 +16,11 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(::InfluxDB)
21
21
  @installed = true
22
22
 
23
- logger.debug "Instrumenting InfluxDB"
23
+ logger.debug "Instrumenting InfluxDB."
24
24
 
25
25
  ::InfluxDB::Client.class_eval do
26
26
  include ScoutApm::Tracer
@@ -16,28 +16,43 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(::Dalli) && defined?(::Dalli::Client)
21
21
  @installed = true
22
22
 
23
- logger.info "Instrumenting Memcached"
23
+ logger.info "Instrumenting Memcached. Prepend: #{prepend}"
24
24
 
25
- ::Dalli::Client.class_eval do
26
- include ScoutApm::Tracer
25
+ if prepend
26
+ ::Dalli::Client.send(:include, ScoutApm::Tracer)
27
+ ::Dalli::Client.send(:prepend, MemcachedInstrumentationPrepend)
28
+ else
29
+ ::Dalli::Client.class_eval do
30
+ include ScoutApm::Tracer
27
31
 
28
- def perform_with_scout_instruments(*args, &block)
29
- command = args.first rescue "Unknown"
32
+ def perform_with_scout_instruments(*args, &block)
33
+ command = args.first rescue "Unknown"
30
34
 
31
- self.class.instrument("Memcached", command) do
32
- perform_without_scout_instruments(*args, &block)
35
+ self.class.instrument("Memcached", command) do
36
+ perform_without_scout_instruments(*args, &block)
37
+ end
33
38
  end
34
- end
35
39
 
36
- alias_method :perform_without_scout_instruments, :perform
37
- alias_method :perform, :perform_with_scout_instruments
40
+ alias_method :perform_without_scout_instruments, :perform
41
+ alias_method :perform, :perform_with_scout_instruments
42
+ end
38
43
  end
39
44
  end
40
45
  end
41
46
  end
47
+
48
+ module MemcachedInstrumentationPrepend
49
+ def perform(*args, &block)
50
+ command = args.first rescue "Unknown"
51
+
52
+ self.class.instrument("Memcached", command) do
53
+ super(*args, &block)
54
+ end
55
+ end
56
+ end
42
57
  end
43
58
  end
@@ -24,7 +24,7 @@ module ScoutApm
24
24
  @installed
25
25
  end
26
26
 
27
- def install
27
+ def install(prepend:)
28
28
  if defined?(ActionDispatch) && defined?(ActionDispatch::MiddlewareStack) && defined?(ActionDispatch::MiddlewareStack::Middleware)
29
29
  @installed = true
30
30
 
@@ -21,7 +21,7 @@ module ScoutApm
21
21
  @installed
22
22
  end
23
23
 
24
- def install
24
+ def install(prepend:)
25
25
  if defined?(ActionDispatch) && defined?(ActionDispatch::MiddlewareStack)
26
26
  @installed = true
27
27
 
@@ -16,7 +16,7 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  @installed = true
21
21
 
22
22
  # Mongoid versions that use Moped should instrument Moped.
@@ -16,32 +16,37 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(::Moped)
21
21
  @installed = true
22
22
 
23
- logger.info "Instrumenting Moped"
23
+ logger.info "Instrumenting Moped. Prepend: #{prepend}"
24
24
 
25
- ::Moped::Node.class_eval do
26
- include ScoutApm::Tracer
25
+ if prepend
26
+ ::Moped::Node.send(:include, ScoutApm::Tracer)
27
+ ::Moped::Node.send(:prepend, MopedInstrumentationPrepend)
28
+ else
29
+ ::Moped::Node.class_eval do
30
+ include ScoutApm::Tracer
27
31
 
28
- def process_with_scout_instruments(operation, &callback)
29
- if operation.respond_to?(:collection)
30
- collection = operation.collection
31
- name = "Process/#{collection}/#{operation.class.to_s.split('::').last}"
32
- self.class.instrument("MongoDB", name, :annotate_layer => { :query => scout_sanitize_log(operation.log_inspect) }) do
33
- process_without_scout_instruments(operation, &callback)
32
+ def process_with_scout_instruments(operation, &callback)
33
+ if operation.respond_to?(:collection)
34
+ collection = operation.collection
35
+ name = "Process/#{collection}/#{operation.class.to_s.split('::').last}"
36
+ self.class.instrument("MongoDB", name, :annotate_layer => { :query => scout_sanitize_log(operation.log_inspect) }) do
37
+ process_without_scout_instruments(operation, &callback)
38
+ end
34
39
  end
35
40
  end
36
- end
37
- alias_method :process_without_scout_instruments, :process
38
- alias_method :process, :process_with_scout_instruments
39
-
40
- # replaces values w/ ?
41
- def scout_sanitize_log(log)
42
- return nil if log.length > 1000 # safeguard - don't sanitize large SQL statements
43
- log.gsub(/(=>")((?:[^"]|"")*)"/) do
44
- $1 + '?' + '"'
41
+ alias_method :process_without_scout_instruments, :process
42
+ alias_method :process, :process_with_scout_instruments
43
+
44
+ # replaces values w/ ?
45
+ def scout_sanitize_log(log)
46
+ return nil if log.length > 1000 # safeguard - don't sanitize large SQL statements
47
+ log.gsub(/(=>")((?:[^"]|"")*)"/) do
48
+ $1 + '?' + '"'
49
+ end
45
50
  end
46
51
  end
47
52
  end
@@ -49,6 +54,26 @@ module ScoutApm
49
54
  end
50
55
 
51
56
  end
57
+
58
+ module MopedInstrumentationPrepend
59
+ def process(operation, &callback)
60
+ if operation.respond_to?(:collection)
61
+ collection = operation.collection
62
+ name = "Process/#{collection}/#{operation.class.to_s.split('::').last}"
63
+ self.class.instrument("MongoDB", name, :annotate_layer => { :query => scout_sanitize_log(operation.log_inspect) }) do
64
+ super(operation, &callback)
65
+ end
66
+ end
67
+ end
68
+
69
+ # replaces values w/ ?
70
+ def scout_sanitize_log(log)
71
+ return nil if log.length > 1000 # safeguard - don't sanitize large SQL statements
72
+ log.gsub(/(=>")((?:[^"]|"")*)"/) do
73
+ $1 + '?' + '"'
74
+ end
75
+ end
76
+ end
52
77
  end
53
78
  end
54
79
 
@@ -16,41 +16,69 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(::Net) && defined?(::Net::HTTP)
21
21
  @installed = true
22
22
 
23
- logger.info "Instrumenting Net::HTTP"
23
+ logger.info "Instrumenting Net::HTTP. Prepend: #{prepend}"
24
24
 
25
- ::Net::HTTP.class_eval do
26
- include ScoutApm::Tracer
25
+ if prepend
26
+ ::Net::HTTP.send(:include, ScoutApm::Tracer)
27
+ ::Net::HTTP.send(:prepend, NetHttpInstrumentationPrepend)
28
+ else
29
+ ::Net::HTTP.class_eval do
30
+ include ScoutApm::Tracer
27
31
 
28
- def request_with_scout_instruments(*args, &block)
29
- self.class.instrument("HTTP", "request", :ignore_children => true, :desc => request_scout_description(args.first)) do
30
- request_without_scout_instruments(*args, &block)
32
+ def request_with_scout_instruments(*args, &block)
33
+ self.class.instrument("HTTP", "request", :ignore_children => true, :desc => request_scout_description(args.first)) do
34
+ request_without_scout_instruments(*args, &block)
35
+ end
31
36
  end
32
- end
33
37
 
34
- def request_scout_description(req)
35
- path = req.path
36
- path = path.path if path.respond_to?(:path)
38
+ def request_scout_description(req)
39
+ path = req.path
40
+ path = path.path if path.respond_to?(:path)
41
+
42
+ # Protect against a nil address value
43
+ if @address.nil?
44
+ return "No Address Found"
45
+ end
37
46
 
38
- # Protect against a nil address value
39
- if @address.nil?
40
- return "No Address Found"
47
+ max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
48
+ (@address + path.split('?').first)[0..(max_length - 1)]
49
+ rescue
50
+ ""
41
51
  end
42
52
 
43
- max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
44
- (@address + path.split('?').first)[0..(max_length - 1)]
45
- rescue
46
- ""
53
+ alias request_without_scout_instruments request
54
+ alias request request_with_scout_instruments
47
55
  end
48
-
49
- alias request_without_scout_instruments request
50
- alias request request_with_scout_instruments
51
56
  end
52
57
  end
53
58
  end
54
59
  end
60
+
61
+ module NetHttpInstrumentationPrepend
62
+ def request(request, *args, &block)
63
+ self.class.instrument("HTTP", "request", :ignore_children => true, :desc => request_scout_description(args.first)) do
64
+ super(request, *args, &block)
65
+ end
66
+ end
67
+
68
+ def request_scout_description(req)
69
+ path = req.path
70
+ path = path.path if path.respond_to?(:path)
71
+
72
+ # Protect against a nil address value
73
+ if @address.nil?
74
+ return "No Address Found"
75
+ end
76
+
77
+ max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
78
+ (@address + path.split('?').first)[0..(max_length - 1)]
79
+ rescue
80
+ ""
81
+ end
82
+ end
55
83
  end
56
84
  end
@@ -16,7 +16,7 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(ActionDispatch) && defined?(ActionDispatch::Routing) && defined?(ActionDispatch::Routing::RouteSet)
21
21
  @installed = true
22
22
 
@@ -16,28 +16,43 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(::Redis) && defined?(::Redis::Client)
21
21
  @installed = true
22
22
 
23
- logger.info "Instrumenting Redis"
23
+ logger.info "Instrumenting Redis. Prepend: #{prepend}"
24
24
 
25
- ::Redis::Client.class_eval do
26
- include ScoutApm::Tracer
25
+ if prepend
26
+ ::Redis::Client.send(:include, ScoutApm::Tracer)
27
+ ::Redis::Client.send(:prepend, RedisClientInstrumentationPrepend)
28
+ else
29
+ ::Redis::Client.class_eval do
30
+ include ScoutApm::Tracer
27
31
 
28
- def call_with_scout_instruments(*args, &block)
29
- command = args.first.first rescue "Unknown"
32
+ def call_with_scout_instruments(*args, &block)
33
+ command = args.first.first rescue "Unknown"
30
34
 
31
- self.class.instrument("Redis", command) do
32
- call_without_scout_instruments(*args, &block)
35
+ self.class.instrument("Redis", command) do
36
+ call_without_scout_instruments(*args, &block)
37
+ end
33
38
  end
34
- end
35
39
 
36
- alias_method :call_without_scout_instruments, :call
37
- alias_method :call, :call_with_scout_instruments
40
+ alias_method :call_without_scout_instruments, :call
41
+ alias_method :call, :call_with_scout_instruments
42
+ end
38
43
  end
39
44
  end
40
45
  end
41
46
  end
47
+
48
+ module RedisClientInstrumentationPrepend
49
+ def call(*args, &block)
50
+ command = args.first.first rescue "Unknown"
51
+
52
+ self.class.instrument("Redis", command) do
53
+ super(*args, &block)
54
+ end
55
+ end
56
+ end
42
57
  end
43
58
  end
@@ -15,7 +15,7 @@ module ScoutApm
15
15
  @installed
16
16
  end
17
17
 
18
- def install
18
+ def install(prepend:)
19
19
  if defined?(::Sinatra) && defined?(::Sinatra::Base) && ::Sinatra::Base.private_method_defined?(:dispatch!)
20
20
  @installed = true
21
21
 
@@ -16,7 +16,7 @@ module ScoutApm
16
16
  @installed
17
17
  end
18
18
 
19
- def install
19
+ def install(prepend:)
20
20
  if defined?(::Typhoeus)
21
21
  @installed = true
22
22
 
@@ -1,3 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "5.2.0"
2
+ VERSION = "5.3.0"
3
3
  end
@@ -29,7 +29,7 @@ class ActiveRecordTest < Minitest::Test
29
29
  agent_context.recorder = recorder
30
30
 
31
31
  instrument = ScoutApm::Instruments::ActiveRecord.new(agent_context)
32
- instrument.install
32
+ instrument.install(prepend: false)
33
33
 
34
34
  ScoutApm::Tracer.instrument("Controller", "foo/bar") do
35
35
  user = User.create
@@ -0,0 +1,24 @@
1
+ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
2
+ require 'test_helper'
3
+
4
+ require 'scout_apm/instruments/http_client'
5
+
6
+ require 'httpclient'
7
+
8
+ class HttpClientTest < Minitest::Test
9
+ def setup
10
+ @context = ScoutApm::AgentContext.new
11
+ @instance = ScoutApm::Instruments::HttpClient.new(@context)
12
+ @instrument_manager = ScoutApm::InstrumentManager.new(@context)
13
+ @instance.install(prepend: @instrument_manager.prepend_for_instrument?(@instance.class))
14
+ end
15
+
16
+ def test_installs_using_proper_method
17
+ if @instrument_manager.prepend_for_instrument?(@instance.class) == true
18
+ assert ::HTTPClient.ancestors.include?(ScoutApm::Instruments::HttpClientInstrumentationPrepend)
19
+ else
20
+ assert_equal false, ::HTTPClient.ancestors.include?(ScoutApm::Instruments::HttpClientInstrumentationPrepend)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
2
+ require 'test_helper'
3
+
4
+ require 'scout_apm/instruments/http'
5
+
6
+ require 'http'
7
+
8
+ class HttpTest < Minitest::Test
9
+ def setup
10
+ @context = ScoutApm::AgentContext.new
11
+ @instance = ScoutApm::Instruments::HTTP.new(@context)
12
+ @instrument_manager = ScoutApm::InstrumentManager.new(@context)
13
+ @instance.install(prepend: @instrument_manager.prepend_for_instrument?(@instance.class))
14
+ end
15
+
16
+ def test_installs_using_proper_method
17
+ if @instrument_manager.prepend_for_instrument?(@instance.class) == true
18
+ assert ::HTTP::Client.ancestors.include?(ScoutApm::Instruments::HTTPInstrumentationPrepend)
19
+ else
20
+ assert_equal false, ::HTTP::Client.ancestors.include?(ScoutApm::Instruments::HTTPInstrumentationPrepend)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
2
+ require 'test_helper'
3
+
4
+ require 'scout_apm/instruments/moped'
5
+
6
+ require 'moped'
7
+
8
+ class MopedTest < Minitest::Test
9
+ def setup
10
+ @context = ScoutApm::AgentContext.new
11
+ @instance = ScoutApm::Instruments::Moped.new(@context)
12
+ @instrument_manager = ScoutApm::InstrumentManager.new(@context)
13
+ @instance.install(prepend: @instrument_manager.prepend_for_instrument?(@instance.class))
14
+ end
15
+
16
+ def test_installs_using_proper_method
17
+ if @instrument_manager.prepend_for_instrument?(@instance.class) == true
18
+ assert ::Moped::Node.ancestors.include?(ScoutApm::Instruments::MopedInstrumentationPrepend)
19
+ else
20
+ assert_equal false, ::Moped::Node.ancestors.include?(ScoutApm::Instruments::MopedInstrumentationPrepend)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -7,7 +7,9 @@ require 'addressable/uri'
7
7
  class NetHttpTest < Minitest::Test
8
8
  def setup
9
9
  @context = ScoutApm::AgentContext.new
10
- ScoutApm::Instruments::NetHttp.new(@context).install
10
+ @instance = ScoutApm::Instruments::NetHttp.new(@context)
11
+ @instrument_manager = ScoutApm::InstrumentManager.new(@context)
12
+ @instance.install(prepend: @instrument_manager.prepend_for_instrument?(@instance.class))
11
13
  end
12
14
 
13
15
  def test_request_scout_description_for_uri
@@ -24,4 +26,12 @@ class NetHttpTest < Minitest::Test
24
26
  req = Net::HTTP::Get.new(Addressable::URI.parse('http://example.org/here'))
25
27
  assert_equal '/here', Net::HTTP.new('').request_scout_description(req)
26
28
  end
29
+
30
+ def test_installs_using_proper_method
31
+ if @instrument_manager.prepend_for_instrument?(@instance.class) == true
32
+ assert Net::HTTP.ancestors.include?(ScoutApm::Instruments::NetHttpInstrumentationPrepend)
33
+ else
34
+ assert_equal false, Net::HTTP.ancestors.include?(ScoutApm::Instruments::NetHttpInstrumentationPrepend)
35
+ end
36
+ end
27
37
  end
@@ -0,0 +1,24 @@
1
+ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
2
+ require 'test_helper'
3
+
4
+ require 'scout_apm/instruments/redis'
5
+
6
+ require 'redis'
7
+
8
+ class RedisTest < Minitest::Test
9
+ def setup
10
+ @context = ScoutApm::AgentContext.new
11
+ @instance = ScoutApm::Instruments::Redis.new(@context)
12
+ @instrument_manager = ScoutApm::InstrumentManager.new(@context)
13
+ @instance.install(prepend: @instrument_manager.prepend_for_instrument?(@instance.class))
14
+ end
15
+
16
+ def test_installs_using_proper_method
17
+ if @instrument_manager.prepend_for_instrument?(@instance.class) == true
18
+ assert ::Redis::Client.ancestors.include?(ScoutApm::Instruments::RedisClientInstrumentationPrepend)
19
+ else
20
+ assert_equal false, ::Redis::Client.ancestors.include?(ScoutApm::Instruments::RedisClientInstrumentationPrepend)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -10,7 +10,7 @@ if (ENV["SCOUT_TEST_FEATURES"] || "").include?("typhoeus")
10
10
  @context = ScoutApm::AgentContext.new
11
11
  @recorder = FakeRecorder.new
12
12
  ScoutApm::Agent.instance.context.recorder = @recorder
13
- ScoutApm::Instruments::Typhoeus.new(@context).install
13
+ ScoutApm::Instruments::Typhoeus.new(@context).install(prepend: false)
14
14
  end
15
15
 
16
16
  def test_instruments_typhoeus_hydra
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout_apm
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Haynes
8
8
  - Andre Lewis
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-06-21 00:00:00.000000000 Z
12
+ date: 2022-08-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -232,6 +232,7 @@ files:
232
232
  - ext/rusage/extconf.rb
233
233
  - ext/rusage/rusage.c
234
234
  - gems/README.md
235
+ - gems/instruments.gemfile
235
236
  - gems/octoshark.gemfile
236
237
  - gems/rails3.gemfile
237
238
  - gems/rails4.gemfile
@@ -441,8 +442,12 @@ files:
441
442
  - test/unit/histogram_test.rb
442
443
  - test/unit/ignored_uris_test.rb
443
444
  - test/unit/instruments/active_record_test.rb
445
+ - test/unit/instruments/http_client_test.rb
446
+ - test/unit/instruments/http_test.rb
447
+ - test/unit/instruments/moped_test.rb
444
448
  - test/unit/instruments/net_http_test.rb
445
449
  - test/unit/instruments/percentile_sampler_test.rb
450
+ - test/unit/instruments/redis_test.rb
446
451
  - test/unit/instruments/typhoeus_test.rb
447
452
  - test/unit/layaway_test.rb
448
453
  - test/unit/layer_children_set_test.rb
@@ -473,7 +478,7 @@ homepage: https://github.com/scoutapp/scout_apm_ruby
473
478
  licenses:
474
479
  - MIT
475
480
  metadata: {}
476
- post_install_message:
481
+ post_install_message:
477
482
  rdoc_options: []
478
483
  require_paths:
479
484
  - lib
@@ -489,8 +494,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
489
494
  - !ruby/object:Gem::Version
490
495
  version: '0'
491
496
  requirements: []
492
- rubygems_version: 3.3.7
493
- signing_key:
497
+ rubygems_version: 3.0.3
498
+ signing_key:
494
499
  specification_version: 4
495
500
  summary: Ruby application performance monitoring
496
501
  test_files: []