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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +12 -0
- data/CHANGELOG.markdown +8 -0
- data/gems/instruments.gemfile +6 -0
- data/lib/scout_apm/config.rb +16 -1
- data/lib/scout_apm/instrument_manager.rb +18 -1
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +1 -1
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +1 -1
- data/lib/scout_apm/instruments/action_view.rb +1 -1
- data/lib/scout_apm/instruments/active_record.rb +1 -1
- data/lib/scout_apm/instruments/elasticsearch.rb +91 -42
- data/lib/scout_apm/instruments/grape.rb +1 -1
- data/lib/scout_apm/instruments/http.rb +36 -16
- data/lib/scout_apm/instruments/http_client.rb +33 -14
- data/lib/scout_apm/instruments/influxdb.rb +2 -2
- data/lib/scout_apm/instruments/memcached.rb +26 -11
- data/lib/scout_apm/instruments/middleware_detailed.rb +1 -1
- data/lib/scout_apm/instruments/middleware_summary.rb +1 -1
- data/lib/scout_apm/instruments/mongoid.rb +1 -1
- data/lib/scout_apm/instruments/moped.rb +44 -19
- data/lib/scout_apm/instruments/net_http.rb +49 -21
- data/lib/scout_apm/instruments/rails_router.rb +1 -1
- data/lib/scout_apm/instruments/redis.rb +26 -11
- data/lib/scout_apm/instruments/sinatra.rb +1 -1
- data/lib/scout_apm/instruments/typhoeus.rb +1 -1
- data/lib/scout_apm/version.rb +1 -1
- data/test/unit/instruments/active_record_test.rb +1 -1
- data/test/unit/instruments/http_client_test.rb +24 -0
- data/test/unit/instruments/http_test.rb +24 -0
- data/test/unit/instruments/moped_test.rb +24 -0
- data/test/unit/instruments/net_http_test.rb +11 -1
- data/test/unit/instruments/redis_test.rb +24 -0
- data/test/unit/instruments/typhoeus_test.rb +1 -1
- metadata +11 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a454dcd926d9c3d9a9432c43ee101bfb9305b2ed181193d55a51fd8d525f3d8
|
4
|
+
data.tar.gz: 9bfdbfb6852b7b933f83f947cb225fe1096083c6a4fe44a0620ff888ee01e26d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29012317cfdcb2958395ca541ec628d4f8d3d2ee5278428075930be30cb9fa445c23463cc11f32dab39649a5b34fd6caf6f981eee7c5c88ef1ade39c36e5806d
|
7
|
+
data.tar.gz: a45e503bcd08fa16cbc885133efcc2bbea64bf76eff6a9de7d5c35a7ddce4ea5a059904c902de6afacba0daba5128b62ce477565aecf2512391962cfea729623
|
data/.github/workflows/test.yml
CHANGED
@@ -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)
|
data/lib/scout_apm/config.rb
CHANGED
@@ -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
|
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)
|
@@ -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
|
-
|
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
|
-
|
34
|
-
|
37
|
+
def perform_request_with_scout_instruments(*args, &block)
|
38
|
+
name = _sanitize_name(args[1])
|
35
39
|
|
36
|
-
|
37
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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,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
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
42
|
-
|
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
|
-
|
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
|
-
|
32
|
+
def request_with_scout_instruments(*args, &block)
|
29
33
|
|
30
|
-
|
31
|
-
|
34
|
+
method = args[0].to_s
|
35
|
+
url = args[1]
|
32
36
|
|
33
|
-
|
34
|
-
|
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
|
-
|
37
|
-
|
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
|
-
|
42
|
-
|
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
|
-
|
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
|
-
|
29
|
-
|
32
|
+
def perform_with_scout_instruments(*args, &block)
|
33
|
+
command = args.first rescue "Unknown"
|
30
34
|
|
31
|
-
|
32
|
-
|
35
|
+
self.class.instrument("Memcached", command) do
|
36
|
+
perform_without_scout_instruments(*args, &block)
|
37
|
+
end
|
33
38
|
end
|
34
|
-
end
|
35
39
|
|
36
|
-
|
37
|
-
|
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
|
@@ -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
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
44
|
-
|
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,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
|
-
|
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
|
-
|
29
|
-
|
32
|
+
def call_with_scout_instruments(*args, &block)
|
33
|
+
command = args.first.first rescue "Unknown"
|
30
34
|
|
31
|
-
|
32
|
-
|
35
|
+
self.class.instrument("Redis", command) do
|
36
|
+
call_without_scout_instruments(*args, &block)
|
37
|
+
end
|
33
38
|
end
|
34
|
-
end
|
35
39
|
|
36
|
-
|
37
|
-
|
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
|
data/lib/scout_apm/version.rb
CHANGED
@@ -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)
|
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.
|
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-
|
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
|
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: []
|