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 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: