scout_apm 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +6 -0
- data/lib/scout_apm/agent.rb +3 -0
- data/lib/scout_apm/config.rb +20 -1
- data/lib/scout_apm/ignored_uris.rb +15 -0
- data/lib/scout_apm/instant/middleware.rb +10 -5
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +0 -7
- data/lib/scout_apm/tracked_request.rb +3 -0
- data/lib/scout_apm/utils/backtrace_parser.rb +10 -8
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +4 -5
- data/test/unit/ignored_uris_test.rb +16 -0
- data/test/unit/utils/backtrace_parser_test.rb +52 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf1eaf130d67f52fcfcf414c6db4347fa4b94521
|
4
|
+
data.tar.gz: 0386d415a40a707645a65662237f0d44b9bbbdd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71d5d59042427c3bf1afbd753fb8646e012c6187c940a0f6a2854a6123cbc5259b8297ca168a5d91793d6722b6082cd0a7c5da520399a13085b4ca7fc274cf3a
|
7
|
+
data.tar.gz: a2ca63e84f57dd17828072076d0c1bd1b757caf1ef3f82a7954ca928b4d09c973ebbda094cb6a5d1ecbbf1c28c3813838239ca55be7c2b1c24c50f430fe49559
|
data/CHANGELOG.markdown
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# 2.1.0
|
2
|
+
|
3
|
+
* Added ignore key to configuration to entirely ignore an endpoint. No traces
|
4
|
+
or metrics will be collected. Useful for health-check endpoints.
|
5
|
+
* Better logging for DevTrace
|
6
|
+
|
1
7
|
# 2.0.0
|
2
8
|
|
3
9
|
* Reporting object allocation & mem delta metrics and mem delta for requests and jobs.
|
data/lib/scout_apm/agent.rb
CHANGED
@@ -20,6 +20,7 @@ module ScoutApm
|
|
20
20
|
attr_reader :slow_request_policy
|
21
21
|
attr_reader :slow_job_policy
|
22
22
|
attr_reader :process_start_time # used when creating slow transactions to report how far from startup the transaction was recorded.
|
23
|
+
attr_reader :ignored_uris
|
23
24
|
|
24
25
|
# Histogram of the cumulative requests since the start of the process
|
25
26
|
attr_reader :request_histograms
|
@@ -115,6 +116,8 @@ module ScoutApm
|
|
115
116
|
init_logger
|
116
117
|
logger.info "Attempting to start Scout Agent [#{ScoutApm::VERSION}] on [#{environment.hostname}]"
|
117
118
|
|
119
|
+
@ignored_uris = ScoutApm::IgnoredUris.new(config.value('ignore'))
|
120
|
+
|
118
121
|
if environment.deploy_integration
|
119
122
|
logger.info "Starting monitoring for [#{environment.deploy_integration.name}]]."
|
120
123
|
return environment.deploy_integration.install
|
data/lib/scout_apm/config.rb
CHANGED
@@ -75,6 +75,23 @@ module ScoutApm
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
# If the passed value is a string, attempt to decode as json
|
79
|
+
# This is a no-op unless the `JSON` constant is defined
|
80
|
+
class JsonCoercion
|
81
|
+
def coerce(val)
|
82
|
+
case val
|
83
|
+
when String
|
84
|
+
if defined?(JSON) && JSON.respond_to?(:parse)
|
85
|
+
JSON.parse(val)
|
86
|
+
else
|
87
|
+
val
|
88
|
+
end
|
89
|
+
else
|
90
|
+
val
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
78
95
|
# Simply returns the passed in value, without change
|
79
96
|
class NullCoercion
|
80
97
|
def coerce(val)
|
@@ -82,10 +99,12 @@ module ScoutApm
|
|
82
99
|
end
|
83
100
|
end
|
84
101
|
|
102
|
+
|
85
103
|
SETTING_COERCIONS = {
|
86
104
|
"monitor" => BooleanCoercion.new,
|
87
105
|
"enable_background_jobs" => BooleanCoercion.new,
|
88
106
|
"dev_trace" => BooleanCoercion.new,
|
107
|
+
"ignore" => JsonCoercion.new,
|
89
108
|
}
|
90
109
|
|
91
110
|
|
@@ -137,7 +156,7 @@ module ScoutApm
|
|
137
156
|
'report_format' => 'json',
|
138
157
|
'disabled_instruments' => [],
|
139
158
|
'enable_background_jobs' => true,
|
140
|
-
'
|
159
|
+
'ignore' => [],
|
141
160
|
'dev_trace' => false, # false for now so code can live in main branch
|
142
161
|
}.freeze
|
143
162
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# On first run, caches the regexes made by the `ignored` configuration setting
|
2
|
+
module ScoutApm
|
3
|
+
class IgnoredUris
|
4
|
+
attr_reader :regex
|
5
|
+
|
6
|
+
def initialize(prefixes)
|
7
|
+
regexes = Array(prefixes).map {|prefix| %r{\A#{prefix}} }
|
8
|
+
@regex = Regexp.union(*regexes)
|
9
|
+
end
|
10
|
+
|
11
|
+
def ignore?(uri)
|
12
|
+
!! regex.match(uri)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -41,12 +41,13 @@ module ScoutApm
|
|
41
41
|
# Note that this middleware never even gets inserted unless Rails environment is development (See Railtie)
|
42
42
|
class Middleware
|
43
43
|
def initialize(app)
|
44
|
+
ScoutApm::Agent.instance.logger.info("Activating Scout DevTrace because environment=development and dev_trace=true in scout_apm config")
|
44
45
|
@app = app
|
45
46
|
end
|
46
47
|
|
47
48
|
def call(env)
|
48
49
|
status, headers, response = @app.call(env)
|
49
|
-
|
50
|
+
path, content_type = env['PATH_INFO'], headers['Content-Type']
|
50
51
|
if ScoutApm::Agent.instance.config.value('dev_trace')
|
51
52
|
if response.respond_to?(:body)
|
52
53
|
req = ScoutApm::RequestManager.lookup
|
@@ -63,8 +64,9 @@ module ScoutApm
|
|
63
64
|
hash.merge!(metadata:metadata)
|
64
65
|
payload = ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(hash)
|
65
66
|
|
66
|
-
if env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'
|
67
|
-
|
67
|
+
if env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' || content_type.include?("application/json")
|
68
|
+
ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This is either AJAX or JSON. Path=#{path}; ContentType=#{content_type}")
|
69
|
+
# Add the payload as a header if it's an AJAX call or JSON
|
68
70
|
headers['X-scoutapminstant'] = payload
|
69
71
|
[status, headers, response]
|
70
72
|
else
|
@@ -77,24 +79,27 @@ module ScoutApm
|
|
77
79
|
page.add_to_body("<script src='#{apm_host}/instant/scout_instant.js?cachebust=#{Time.now.to_i}'></script>")
|
78
80
|
page.add_to_body("<script>var scoutInstantPageTrace=#{payload};window.scoutInstant=window.scoutInstant('#{apm_host}', scoutInstantPageTrace)</script>")
|
79
81
|
|
80
|
-
|
81
82
|
if response.is_a?(ActionDispatch::Response)
|
83
|
+
ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This appears to be an HTML page and an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
|
82
84
|
# preserve the ActionDispatch::Response when applicable
|
83
85
|
response.body=[page.res]
|
84
86
|
[status, headers, response]
|
85
87
|
else
|
88
|
+
ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This appears to be an HTML page but not an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
|
86
89
|
# otherwise, just return an array
|
87
|
-
# TODO: this will break ActionCable repsponse
|
88
90
|
[status, headers, [page.res]]
|
89
91
|
end
|
90
92
|
end
|
91
93
|
else
|
94
|
+
ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body, but no trace was found. Path=#{path}; ContentType=#{content_type}")
|
92
95
|
[status, headers, response]
|
93
96
|
end
|
94
97
|
else
|
98
|
+
# don't log anything here - this is the path for all assets served in development, and the log would get noisy
|
95
99
|
[status, headers, response]
|
96
100
|
end
|
97
101
|
else
|
102
|
+
ScoutApm::Agent.instance.logger.debug("DevTrace: isn't activated via config. Try: SCOUT_DEV_TRACE=true rails server")
|
98
103
|
[status, headers, response]
|
99
104
|
end
|
100
105
|
end
|
@@ -39,13 +39,6 @@ module ScoutApm
|
|
39
39
|
allocation_metrics = {}
|
40
40
|
end
|
41
41
|
|
42
|
-
(ScoutApm::Agent.instance.config.value("ignore_traces") || []).each do |pattern|
|
43
|
-
if /#{pattern}/ =~ uri
|
44
|
-
ScoutApm::Agent.instance.logger.debug("Skipped recording a trace for #{uri} due to `ignore_traces` pattern: #{pattern}")
|
45
|
-
return nil
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
42
|
SlowTransaction.new(uri,
|
50
43
|
scope.legacy_metric_name,
|
51
44
|
root_layer.total_call_time,
|
@@ -222,6 +222,9 @@ module ScoutApm
|
|
222
222
|
def record!
|
223
223
|
@recorded = true
|
224
224
|
|
225
|
+
# Bail out early if the user asked us to ignore this uri
|
226
|
+
return if ScoutApm::Agent.instance.ignored_uris.ignore?(annotations[:uri])
|
227
|
+
|
225
228
|
# Update immediate and long-term histograms for both job and web requests
|
226
229
|
if unique_name != :unknown
|
227
230
|
ScoutApm::Agent.instance.request_histograms.add(unique_name, root_layer.total_call_time)
|
@@ -1,25 +1,27 @@
|
|
1
1
|
require 'scout_apm/environment'
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
3
|
+
# Given a call stack Array, grabs the first +APP_FRAMES+ callers within the
|
4
|
+
# application root directory.
|
5
|
+
#
|
5
6
|
module ScoutApm
|
6
7
|
module Utils
|
7
8
|
class BacktraceParser
|
8
9
|
|
9
10
|
APP_FRAMES = 3 # will return up to 3 frames from the app stack.
|
10
11
|
|
11
|
-
|
12
|
+
attr_reader :call_stack
|
13
|
+
|
14
|
+
def initialize(call_stack, root=ScoutApm::Environment.instance.root)
|
12
15
|
@call_stack = call_stack
|
13
16
|
# We can't use a constant as it'd be too early to fetch environment info
|
14
|
-
@@app_dir_regex
|
17
|
+
@@app_dir_regex = %r|#{root}/(.*)|
|
15
18
|
end
|
16
19
|
|
17
|
-
# Given a call stack Array, grabs the first +APP_FRAMES+ callers within the application root directory.
|
18
20
|
def call
|
19
21
|
stack = []
|
20
|
-
|
22
|
+
call_stack.each do |c|
|
21
23
|
if m = c.match(@@app_dir_regex)
|
22
|
-
stack << m[
|
24
|
+
stack << m[1]
|
23
25
|
break if stack.size == APP_FRAMES
|
24
26
|
end
|
25
27
|
end
|
@@ -28,4 +30,4 @@ module ScoutApm
|
|
28
30
|
|
29
31
|
end
|
30
32
|
end
|
31
|
-
end
|
33
|
+
end
|
data/lib/scout_apm/version.rb
CHANGED
data/lib/scout_apm.rb
CHANGED
@@ -63,7 +63,7 @@ require 'scout_apm/platform_integrations/server'
|
|
63
63
|
require 'scout_apm/histogram'
|
64
64
|
|
65
65
|
require 'scout_apm/deploy_integrations/capistrano_3'
|
66
|
-
#require 'scout_apm/deploy_integrations/capistrano_2'
|
66
|
+
# require 'scout_apm/deploy_integrations/capistrano_2'
|
67
67
|
|
68
68
|
require 'scout_apm/instruments/net_http'
|
69
69
|
require 'scout_apm/instruments/http_client'
|
@@ -88,6 +88,7 @@ require 'allocations'
|
|
88
88
|
|
89
89
|
require 'scout_apm/app_server_load'
|
90
90
|
|
91
|
+
require 'scout_apm/ignored_uris.rb'
|
91
92
|
require 'scout_apm/utils/active_record_metric_name'
|
92
93
|
require 'scout_apm/utils/backtrace_parser'
|
93
94
|
require 'scout_apm/utils/installed_gems'
|
@@ -145,7 +146,7 @@ require 'scout_apm/instant/middleware'
|
|
145
146
|
if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR) && Rails::VERSION::MAJOR >= 3 && defined?(Rails::Railtie)
|
146
147
|
module ScoutApm
|
147
148
|
class Railtie < Rails::Railtie
|
148
|
-
initializer
|
149
|
+
initializer 'scout_apm.start' do |app|
|
149
150
|
# attempt to start on first-request if not otherwise started, which is
|
150
151
|
# a good catch-all for Webrick, and Passenger and similar, where we
|
151
152
|
# can't detect the running app server until actual requests come in.
|
@@ -153,11 +154,10 @@ if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR
|
|
153
154
|
|
154
155
|
# Attempt to start right away, this will work best for preloading apps, Unicorn & Puma & similar
|
155
156
|
ScoutApm::Agent.instance.start
|
156
|
-
|
157
157
|
end
|
158
158
|
end
|
159
159
|
class Railtie < Rails::Railtie
|
160
|
-
initializer
|
160
|
+
initializer 'scout_apm.start' do |app|
|
161
161
|
if Rails.env.development?
|
162
162
|
app.middleware.use ScoutApm::Instant::Middleware
|
163
163
|
end
|
@@ -167,4 +167,3 @@ if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR
|
|
167
167
|
else
|
168
168
|
ScoutApm::Agent.instance.start
|
169
169
|
end
|
170
|
-
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
require 'scout_apm/ignored_uris'
|
4
|
+
|
5
|
+
class IgnoredUrlsTest < Minitest::Test
|
6
|
+
def test_ignores_prefix
|
7
|
+
i = ScoutApm::IgnoredUris.new(["/slow", "/health"])
|
8
|
+
assert_equal true, i.ignore?("/slow/foo/bar")
|
9
|
+
assert_equal true, i.ignore?("/health?leeches=true")
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_does_not_ignore_inner
|
13
|
+
i = ScoutApm::IgnoredUris.new(["/slow", "/health"])
|
14
|
+
assert_equal false, i.ignore?("/users/2/health")
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative '../../test_helper'
|
2
|
+
require 'scout_apm/utils/backtrace_parser'
|
3
|
+
|
4
|
+
class BacktraceParserTest < Minitest::Test
|
5
|
+
|
6
|
+
################################################################################
|
7
|
+
# Helpers
|
8
|
+
|
9
|
+
def root
|
10
|
+
"/Users/scout/secret-next-big-thing/current"
|
11
|
+
end
|
12
|
+
|
13
|
+
def raw_backtrace(count=10)
|
14
|
+
count.times.map {|i| "#{root}/app/controllers/best_#{i}_controller.rb"}
|
15
|
+
end
|
16
|
+
|
17
|
+
################################################################################
|
18
|
+
# Tests
|
19
|
+
|
20
|
+
def test_maxes_at_APP_FRAMES
|
21
|
+
result = ScoutApm::Utils::BacktraceParser.new(raw_backtrace, root).call
|
22
|
+
|
23
|
+
assert_equal ScoutApm::Utils::BacktraceParser::APP_FRAMES, result.length
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_picks_off_top_of_trace
|
27
|
+
result = ScoutApm::Utils::BacktraceParser.new(raw_backtrace, root).call
|
28
|
+
|
29
|
+
assert_equal false, (result[0] =~ %r|app/controllers/best_0_controller.rb|).nil?
|
30
|
+
assert_equal false, (result[1] =~ %r|app/controllers/best_1_controller.rb|).nil?
|
31
|
+
assert_equal false, (result[2] =~ %r|app/controllers/best_2_controller.rb|).nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_trims_off_root_dir
|
35
|
+
result = ScoutApm::Utils::BacktraceParser.new(raw_backtrace, root).call
|
36
|
+
|
37
|
+
result.each do |r|
|
38
|
+
assert_equal true, (r =~ %r|#{root}|).nil?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_works_on_shorter_backtraces
|
43
|
+
result = ScoutApm::Utils::BacktraceParser.new(raw_backtrace(1), root).call
|
44
|
+
|
45
|
+
assert_equal 1, result.length
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_works_with_no_in_app_frames
|
49
|
+
result = ScoutApm::Utils::BacktraceParser.new(raw_backtrace, "/Users/scout/different-secrets").call
|
50
|
+
assert_equal 0, result.length
|
51
|
+
end
|
52
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scout_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derek Haynes
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-08-
|
12
|
+
date: 2016-08-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rusage
|
@@ -137,6 +137,7 @@ files:
|
|
137
137
|
- lib/scout_apm/framework_integrations/ruby.rb
|
138
138
|
- lib/scout_apm/framework_integrations/sinatra.rb
|
139
139
|
- lib/scout_apm/histogram.rb
|
140
|
+
- lib/scout_apm/ignored_uris.rb
|
140
141
|
- lib/scout_apm/instant/assets/xmlhttp_instrumentation.html
|
141
142
|
- lib/scout_apm/instant/middleware.rb
|
142
143
|
- lib/scout_apm/instant_reporting.rb
|
@@ -226,6 +227,7 @@ files:
|
|
226
227
|
- test/unit/config_test.rb
|
227
228
|
- test/unit/environment_test.rb
|
228
229
|
- test/unit/histogram_test.rb
|
230
|
+
- test/unit/ignored_uris_test.rb
|
229
231
|
- test/unit/instruments/active_record_instruments_test.rb
|
230
232
|
- test/unit/layaway_test.rb
|
231
233
|
- test/unit/metric_set_test.rb
|
@@ -236,6 +238,7 @@ files:
|
|
236
238
|
- test/unit/slow_request_policy_test.rb
|
237
239
|
- test/unit/sql_sanitizer_test.rb
|
238
240
|
- test/unit/utils/active_record_metric_name_test.rb
|
241
|
+
- test/unit/utils/backtrace_parser_test.rb
|
239
242
|
homepage: https://github.com/scoutapp/scout_apm_ruby
|
240
243
|
licenses:
|
241
244
|
- Proprietary (See LICENSE.md)
|
@@ -257,7 +260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
257
260
|
version: '0'
|
258
261
|
requirements: []
|
259
262
|
rubyforge_project: scout_apm
|
260
|
-
rubygems_version: 2.
|
263
|
+
rubygems_version: 2.6.2
|
261
264
|
signing_key:
|
262
265
|
specification_version: 4
|
263
266
|
summary: Ruby application performance monitoring
|
@@ -268,6 +271,7 @@ test_files:
|
|
268
271
|
- test/unit/config_test.rb
|
269
272
|
- test/unit/environment_test.rb
|
270
273
|
- test/unit/histogram_test.rb
|
274
|
+
- test/unit/ignored_uris_test.rb
|
271
275
|
- test/unit/instruments/active_record_instruments_test.rb
|
272
276
|
- test/unit/layaway_test.rb
|
273
277
|
- test/unit/metric_set_test.rb
|
@@ -278,3 +282,5 @@ test_files:
|
|
278
282
|
- test/unit/slow_request_policy_test.rb
|
279
283
|
- test/unit/sql_sanitizer_test.rb
|
280
284
|
- test/unit/utils/active_record_metric_name_test.rb
|
285
|
+
- test/unit/utils/backtrace_parser_test.rb
|
286
|
+
has_rdoc:
|