scout_apm 5.2.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
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: []