scout_apm 2.0.0 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a0a32ed17d0fae5e86bf5e64cfe7f25dfe9d1a77
4
- data.tar.gz: 0e910af3eebdef26c093df1f522795aea983d890
3
+ metadata.gz: bf1eaf130d67f52fcfcf414c6db4347fa4b94521
4
+ data.tar.gz: 0386d415a40a707645a65662237f0d44b9bbbdd8
5
5
  SHA512:
6
- metadata.gz: f125f45638df4302321104e62d147725636ce4a31ea69d4ee9ca6b5730fb9673ac4ea3cca1e2cf58ff5874ac4c90ccce2eb737b940ca3780a4741fa531763784
7
- data.tar.gz: 78eeec2d2700d0d02c937be30abc4322820935e89bd178a6d31114b86ea2e5d040b57578eccd7043e9c1386b4f2b6ffa1e26c94d52ae8d1c7afbbdf624cdbc1a
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.
@@ -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
@@ -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
- 'ignore_traces' => [],
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
- # Add the payload as a header if it's an AJAX call
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
- # Removes actual values from SQL. Used to both obfuscate the SQL and group
4
- # similar queries in the UI.
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
- def initialize(call_stack)
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 ||= /\A(#{ScoutApm::Environment.instance.root.to_s.gsub('/','\/')}\/)(app\/(.+))/.freeze
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
- @call_stack.each_with_index do |c,i|
22
+ call_stack.each do |c|
21
23
  if m = c.match(@@app_dir_regex)
22
- stack << m[2]
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
@@ -1,4 +1,4 @@
1
1
  module ScoutApm
2
- VERSION = "2.0.0"
2
+ VERSION = "2.1.0"
3
3
  end
4
4
 
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 "scout_apm.start" do |app|
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 "scout_apm.start" do |app|
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.0.0
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-08 00:00:00.000000000 Z
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.4.6
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: