scout_apm 5.1.1 → 5.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +15 -0
- data/CHANGELOG.markdown +15 -0
- data/gems/instruments.gemfile +6 -0
- data/gems/sidekiq.gemfile +4 -0
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +2 -8
- 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/store.rb +6 -0
- data/lib/scout_apm/version.rb +1 -1
- data/test/unit/background_job_integrations/sidekiq_test.rb +17 -0
- 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 +12 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3d15e4a9b9e98af3e71ba6dde0b666b8807b742f967e35fe36ebd747ae83483
|
4
|
+
data.tar.gz: f8e88492777b44cdc9362500b7ccdcb68daaa756d6c98ec2e114e231e240071d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb7224bc3294417f72279a97713b9be39bd9657efa1b9c39fb969164f353cdd04c8ced4114e6b717d7ed315c86b5412d8591132f3adde543fb09f8ea908e0308
|
7
|
+
data.tar.gz: 354cd7af52c07bb78380614d7aafdde9942a1b3ea973ad0616df187a2f2048b3a04fee9cff737d836c14caef142fec31aafef22e5ee4dfeb2125d31c6ea39f70
|
data/.github/workflows/test.yml
CHANGED
@@ -35,11 +35,26 @@ 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"
|
46
|
+
- ruby: 3.0
|
47
|
+
gemfile: gems/instruments.gemfile
|
48
|
+
prepend: true
|
49
|
+
test_features: "instruments"
|
38
50
|
- ruby: 3.0
|
51
|
+
gemfile: gems/sidekiq.gemfile
|
52
|
+
test_features: "sidekiq_install"
|
39
53
|
|
40
54
|
env:
|
41
55
|
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
42
56
|
SCOUT_TEST_FEATURES: ${{ matrix.test_features }}
|
57
|
+
SCOUT_USE_PREPEND: ${{ matrix.prepend }}
|
43
58
|
|
44
59
|
runs-on: ubuntu-latest
|
45
60
|
|
data/CHANGELOG.markdown
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 5.3.1
|
4
|
+
|
5
|
+
* Fix typo in HTTPClient prepend instrumentation (#457)
|
6
|
+
# 5.3.0
|
7
|
+
|
8
|
+
* Add configuraiton option to use `Module#prepend` instead of `Module#alias_method` (default)
|
9
|
+
for instrumentation (#448). The default method for instrumentation has not changed, but
|
10
|
+
configuration options were added to allow switching to `Module#prepend` for most
|
11
|
+
instrumentation. Refer to the documentation for more information:
|
12
|
+
[Library Instrumentation Method](https://scoutapm.com/docs/ruby/configuration#library-instrumentation-method)
|
13
|
+
|
14
|
+
# 5.2.0
|
15
|
+
|
16
|
+
* Use Sidekiq lifecycle hooks to start Scout agent on Sidekiq start. (#449)
|
17
|
+
|
3
18
|
# 5.1.1
|
4
19
|
|
5
20
|
* Improvements to SqlServer scrubbing in SqlSanitizer (#422)
|
@@ -37,17 +37,11 @@ module ScoutApm
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def install_processor
|
40
|
-
|
41
|
-
|
42
|
-
::Sidekiq::Processor.class_eval do
|
43
|
-
def initialize_with_scout(*args)
|
40
|
+
::Sidekiq.configure_server do |config|
|
41
|
+
config.on(:startup) do
|
44
42
|
agent = ::ScoutApm::Agent.instance
|
45
43
|
agent.start
|
46
|
-
initialize_without_scout(*args)
|
47
44
|
end
|
48
|
-
|
49
|
-
alias_method :initialize_without_scout, :initialize
|
50
|
-
alias_method :initialize, :initialize_with_scout
|
51
45
|
end
|
52
46
|
end
|
53
47
|
end
|
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(*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/store.rb
CHANGED
@@ -217,6 +217,7 @@ module ScoutApm
|
|
217
217
|
|
218
218
|
def initialize(timestamp, context)
|
219
219
|
@timestamp = timestamp
|
220
|
+
@context = context
|
220
221
|
|
221
222
|
@request_traces = ScoredItemSet.new(context.config.value('max_traces'))
|
222
223
|
@job_traces = ScoredItemSet.new(context.config.value('max_traces'))
|
@@ -230,6 +231,11 @@ module ScoutApm
|
|
230
231
|
@jobs = Hash.new
|
231
232
|
end
|
232
233
|
|
234
|
+
def logger
|
235
|
+
@context.logger
|
236
|
+
end
|
237
|
+
private :logger
|
238
|
+
|
233
239
|
# Merges another StoreReportingPeriod into this one
|
234
240
|
def merge(other)
|
235
241
|
self.
|
data/lib/scout_apm/version.rb
CHANGED
@@ -3,8 +3,25 @@ require 'scout_apm/request_manager'
|
|
3
3
|
require 'scout_apm/background_job_integrations/sidekiq'
|
4
4
|
|
5
5
|
class SidekiqTest < Minitest::Test
|
6
|
+
SidekiqIntegration = ScoutApm::BackgroundJobIntegrations::Sidekiq
|
6
7
|
SidekiqMiddleware = ScoutApm::BackgroundJobIntegrations::SidekiqMiddleware
|
7
8
|
|
9
|
+
########################################
|
10
|
+
# Install
|
11
|
+
########################################
|
12
|
+
if (ENV["SCOUT_TEST_FEATURES"] || "").include?("sidekiq_install")
|
13
|
+
require 'sidekiq'
|
14
|
+
|
15
|
+
# Sidekiq::CLI needs to be defined in order for `Sidekiq.configure_server` to work
|
16
|
+
Sidekiq::CLI = nil
|
17
|
+
|
18
|
+
def test_starts_on_startup
|
19
|
+
::ScoutApm::Agent.any_instance.expects(:start)
|
20
|
+
SidekiqIntegration.new.install
|
21
|
+
Sidekiq.options[:lifecycle_events][:startup].map(&:call)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
8
25
|
########################################
|
9
26
|
# Middleware
|
10
27
|
########################################
|
@@ -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.1
|
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:
|
12
|
+
date: 2022-08-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -232,11 +232,13 @@ 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
|
238
239
|
- gems/rails5.gemfile
|
239
240
|
- gems/rails6.gemfile
|
241
|
+
- gems/sidekiq.gemfile
|
240
242
|
- gems/typhoeus.gemfile
|
241
243
|
- lib/scout_apm.rb
|
242
244
|
- lib/scout_apm/agent.rb
|
@@ -440,8 +442,12 @@ files:
|
|
440
442
|
- test/unit/histogram_test.rb
|
441
443
|
- test/unit/ignored_uris_test.rb
|
442
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
|
443
448
|
- test/unit/instruments/net_http_test.rb
|
444
449
|
- test/unit/instruments/percentile_sampler_test.rb
|
450
|
+
- test/unit/instruments/redis_test.rb
|
445
451
|
- test/unit/instruments/typhoeus_test.rb
|
446
452
|
- test/unit/layaway_test.rb
|
447
453
|
- test/unit/layer_children_set_test.rb
|
@@ -472,7 +478,7 @@ homepage: https://github.com/scoutapp/scout_apm_ruby
|
|
472
478
|
licenses:
|
473
479
|
- MIT
|
474
480
|
metadata: {}
|
475
|
-
post_install_message:
|
481
|
+
post_install_message:
|
476
482
|
rdoc_options: []
|
477
483
|
require_paths:
|
478
484
|
- lib
|
@@ -488,8 +494,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
488
494
|
- !ruby/object:Gem::Version
|
489
495
|
version: '0'
|
490
496
|
requirements: []
|
491
|
-
rubygems_version: 3.
|
492
|
-
signing_key:
|
497
|
+
rubygems_version: 3.3.7
|
498
|
+
signing_key:
|
493
499
|
specification_version: 4
|
494
500
|
summary: Ruby application performance monitoring
|
495
501
|
test_files: []
|