scout_apm 2.4.0.pre3 → 2.4.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: 29cede8ad70dd0d7cb0be6a777da5d346309ff24
4
- data.tar.gz: 17f98116601cc9b75c3d0da0e9f315f2250af174
3
+ metadata.gz: 35153897d05053c188035cea90a9c9821abcd4cf
4
+ data.tar.gz: 94fe5aee4fa214256fe31ffe643973274f2d66fb
5
5
  SHA512:
6
- metadata.gz: 4b5717805b91c5f13ab00e22c5739b4bba32ab7f4bf1eae18118607019937cfba7ad53dfd76086ddc1c5d8fcd2f2350686b594cbb5b6b3a44a61b4759305cad3
7
- data.tar.gz: 60b82a60a84001d6a480f2ebdbf16cb5c964e729b30bcbc82c47b0cd526aea71802e3b753bb568176d5a4beca02a1a2d5bdf9b012a0bb48318fc6fb6b2908307
6
+ metadata.gz: efef5649e203008c653a8fba69e1c28de35f510d831ff3e47a4483fda8e92116cd8aa5e1e64d3ab7ffe63cef9f03113987f953cc78f65a05af076be04fa1fb7b
7
+ data.tar.gz: 37966ab847e9c50ce354c9f8a2fe3c4ecfda397e091fc2d89f02b7816b2b14bfdd8e4bf56528a1f6572e9bb92b26ab6438b7a4bb83709969c887a8f904a810a0
data/CHANGELOG.markdown CHANGED
@@ -1,10 +1,27 @@
1
1
  # 2.4.0
2
2
 
3
3
  * Rework agent startup sequence
4
+ * Install all background job instrumentations if you're running more than one
5
+ * Capture longer individual SQL statements
6
+ * Capture multiple SQL statements if multiple are run during a single AR call.
7
+
8
+ # 2.3.5
9
+
10
+ * More robust recovery from stale layaway files
11
+ * Quiet logging when hitting unusual layaway file limits
12
+ * Better naming for Sidekiq delayed method jobs
13
+ * Webrick is only required if actually needed
14
+
15
+ # 2.3.4
16
+
17
+ * Capture 300 characters of a url from net/http and httpclient instruments (up from 100).
4
18
 
5
19
  # 2.3.3
6
20
 
7
21
  * Capture ActiveRecord calls that generate more complex queries
22
+ * More aggressively determine names of complex queries (to determine "User/find", "Account/create" and similar)
23
+ * Increases the maximum size of SQL queries that are sanitized to 16KB from 4 KB
24
+ * Captures all SQL individual queries generated in a given AR call (previous only a single query was captured)
8
25
 
9
26
  # 2.3.2
10
27
 
data/README.markdown CHANGED
@@ -1,14 +1,21 @@
1
- # ScoutApm
1
+ # ScoutApm Ruby Agent
2
2
 
3
- A Ruby gem for detailed Rails application performance analysis. Metrics are reported to [Scout](https://scoutapp.com), a hosted application monitoring service.
3
+ A Ruby gem for detailed Rails application performance analysis. Metrics are
4
+ reported to [Scout](https://scoutapp.com), a hosted application monitoring
5
+ service.
4
6
 
5
7
  ## Getting Started
6
8
 
7
- Install the gem:
9
+ Add the gem to your Gemfile
8
10
 
9
- gem install scout_apm
10
-
11
- Signup for a [Scout](https://apm.scoutapp.com) account and copy the config file to `RAILS_ROOT/config/scout_apm.yml`.
11
+ gem 'scout_apm'
12
+
13
+ Update your Gemfile
14
+
15
+ bundle install
16
+
17
+ Signup for a [Scout](https://apm.scoutapp.com) account and put the provided
18
+ config file at `RAILS_ROOT/config/scout_apm.yml`.
12
19
 
13
20
  Your config file should look like:
14
21
 
@@ -22,7 +29,8 @@ Your config file should look like:
22
29
 
23
30
  ## Docs
24
31
 
25
- For the complete list of supported frameworks, Rubies, etc, see our [help site](http://help.apm.scoutapp.com/).
32
+ For the complete list of supported frameworks, Rubies, configuration options
33
+ and more, see our [help site](http://help.apm.scoutapp.com/).
26
34
 
27
35
  ## Help
28
36
 
data/lib/scout_apm.rb CHANGED
@@ -14,7 +14,6 @@ require 'socket'
14
14
  require 'thread'
15
15
  require 'time'
16
16
  require 'yaml'
17
- require 'webrick'
18
17
 
19
18
  #####################################
20
19
  # Gem Requires
@@ -43,18 +43,18 @@ module ScoutApm
43
43
  install_background_job_integrations
44
44
  install_app_server_integration
45
45
 
46
- # XXX: Should this happen at application start?
47
- # Should this ever happen after fork?
48
- # We start a thread in this, which can screw stuff up when we then fork.
49
- #
50
- # Save it into a variable to prevent it from ever running twice
51
- @app_server_load ||= AppServerLoad.new(context).run
52
-
53
46
  logger.info "Scout Agent [#{ScoutApm::VERSION}] installed"
54
47
 
55
48
  context.installed!
56
49
 
57
50
  if ScoutApm::Agent::Preconditions.check?(context) || force
51
+ # XXX: Should this happen at application start?
52
+ # Should this ever happen after fork?
53
+ # We start a thread in this, which can screw stuff up when we then fork.
54
+ #
55
+ # Save it into a variable to prevent it from ever running twice
56
+ @app_server_load ||= AppServerLoad.new(context).run
57
+
58
58
  start
59
59
  end
60
60
  end
@@ -124,9 +124,8 @@ module ScoutApm
124
124
 
125
125
  def should_load_instruments?
126
126
  return true if context.config.value('dev_trace')
127
- # XXX: If monitor is true, we want to install, right?
128
- # return false if context.config.value('monitor')
129
- context.environment.app_server_integration.found? || context.environment.background_job_integration
127
+ return false unless context.config.value('monitor')
128
+ context.environment.app_server_integration.found? || context.environment.background_job_integrations.any?
130
129
  end
131
130
 
132
131
  #################################
@@ -51,7 +51,6 @@ module ScoutApm
51
51
  queue = job.queue || "default"
52
52
 
53
53
  req = ScoutApm::RequestManager.lookup
54
- req.job!
55
54
 
56
55
  begin
57
56
  latency = Time.now - job.created_at
@@ -57,7 +57,6 @@ module ScoutApm
57
57
  class SidekiqMiddleware
58
58
  def call(_worker, msg, queue)
59
59
  req = ScoutApm::RequestManager.lookup
60
- req.job!
61
60
  req.annotate_request(:queue_latency => latency(msg))
62
61
 
63
62
  begin
@@ -92,10 +91,16 @@ module ScoutApm
92
91
  end
93
92
  elsif job_class == DELAYED_WRAPPER_KLASS
94
93
  begin
94
+ # Extract the info out of the wrapper
95
95
  yml = msg['args'].first
96
96
  deserialized_args = YAML.load(yml)
97
97
  klass, method, *rest = deserialized_args
98
- job_class = [klass,method].map(&:to_s).join(".")
98
+
99
+ # If this is an instance of a class, get the class itself
100
+ # Prevents instances from coming through named like "#<Foo:0x007ffd7a9dd8a0>"
101
+ klass = klass.class unless klass.is_a? Module
102
+
103
+ job_class = [klass, method].map(&:to_s).join(".")
99
104
  rescue
100
105
  DELAYED_WRAPPER_KLASS
101
106
  end
@@ -66,6 +66,7 @@ module ScoutApm
66
66
  'report_format',
67
67
  'scm_subdirectory',
68
68
  'uri_reporting',
69
+ 'instrument_http_url_length',
69
70
  ]
70
71
 
71
72
  ################################################################################
@@ -153,6 +154,7 @@ module ScoutApm
153
154
  "monitor" => BooleanCoercion.new,
154
155
  'database_metric_limit' => IntegerCoercion.new,
155
156
  'database_metric_report_limit' => IntegerCoercion.new,
157
+ 'instrument_http_url_length' => IntegerCoercion.new,
156
158
  }
157
159
 
158
160
 
@@ -256,6 +258,7 @@ module ScoutApm
256
258
  'remote_agent_port' => 7721, # picked at random
257
259
  'database_metric_limit' => 5000, # The hard limit on db metrics
258
260
  'database_metric_report_limit' => 1000,
261
+ 'instrument_http_url_length' => 300,
259
262
  }.freeze
260
263
 
261
264
  def value(key)
@@ -57,7 +57,6 @@ module ScoutApm
57
57
  req.context.add_user(:ip => request.remote_ip)
58
58
  req.set_headers(request.headers)
59
59
  req.start_layer( ScoutApm::Layer.new("Controller", "#{controller_path}/#{action_name}") )
60
- req.web!
61
60
 
62
61
  begin
63
62
  perform_action_without_scout_instruments(*args, &block)
@@ -77,8 +77,6 @@ module ScoutApm
77
77
  req.context.add_user(:ip => request.remote_ip) rescue nil
78
78
  req.set_headers(request.headers)
79
79
 
80
- req.web!
81
-
82
80
  resolved_name = scout_action_name(*args)
83
81
  req.start_layer( ScoutApm::Layer.new("Controller", "#{controller_path}/#{resolved_name}") )
84
82
  begin
@@ -44,7 +44,6 @@ module ScoutApm
44
44
  req.context.add_user(:ip => request.ip) rescue nil
45
45
 
46
46
  req.set_headers(request.headers)
47
- req.web!
48
47
 
49
48
  begin
50
49
  name = ["Grape",
@@ -26,9 +26,12 @@ module ScoutApm
26
26
  include ScoutApm::Tracer
27
27
 
28
28
  def request_with_scout_instruments(*args, &block)
29
+
29
30
  method = args[0].to_s
30
31
  url = args[1]
31
- url = url && url.to_s[0..99]
32
+
33
+ max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
34
+ url = url && url.to_s[0..(max_length - 1)]
32
35
 
33
36
  self.class.instrument("HTTP", method, :desc => url) do
34
37
  request_without_scout_instruments(*args, &block)
@@ -34,7 +34,9 @@ module ScoutApm
34
34
  def request_scout_description(req)
35
35
  path = req.path
36
36
  path = path.path if path.respond_to?(:path)
37
- (@address + path.split('?').first)[0..99]
37
+
38
+ max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
39
+ (@address + path.split('?').first)[0..(max_length - 1)]
38
40
  end
39
41
 
40
42
  alias request_without_scout_instruments request
@@ -11,7 +11,6 @@ module ScoutApm
11
11
  end
12
12
 
13
13
  req = ScoutApm::RequestManager.lookup
14
- req.job!
15
14
 
16
15
  begin
17
16
  req.start_layer(ScoutApm::Layer.new('Queue', queue))
@@ -51,7 +51,8 @@ module ScoutApm
51
51
 
52
52
  def write_reporting_period(reporting_period, files_limit = MAX_FILES_LIMIT)
53
53
  if at_layaway_file_limit?(files_limit)
54
- logger.error("Hit layaway file limit. Not writing to layaway file")
54
+ # This will happen constantly once we hit this case, so only log the first time
55
+ @wrote_layaway_limit_error_message ||= logger.error("Hit layaway file limit. Not writing to layaway file")
55
56
  return false
56
57
  end
57
58
  filename = file_for(reporting_period.timestamp)
@@ -59,9 +60,22 @@ module ScoutApm
59
60
  layaway_file.write(reporting_period)
60
61
  end
61
62
 
62
- # Claims a given timestamp (getting a lock on a particular filename),
63
- # then yields ReportingPeriods collected up from all the files.
64
- # If the yield returns truthy, delete the layaway files that made it up.
63
+ # Claims a given timestamp by getting an exclusive lock on a timestamped
64
+ # coordinator file. The coordinator file never contains data, it's just a
65
+ # syncronization mechanism.
66
+ #
67
+ # Once the 'claim' is obtained:
68
+ # * load and yield each ReportingPeriod from the layaway files.
69
+ # * if there are reporting periods:
70
+ # * yields any ReportingPeriods collected up from all the files.
71
+ # * deletes all of the layaway files (including the coordinator) for the timestamp
72
+ # * if not
73
+ # * delete the coordinator
74
+ # * remove any stale layaway files that may be hanging around.
75
+ # * Finally unlock and ensure the coordinator file is cleared.
76
+ #
77
+ # If a claim file can't be obtained, return false without doing any work
78
+ # Another process is handling the reporting.
65
79
  def with_claim(timestamp)
66
80
  coordinator_file = glob_pattern(timestamp, :coordinator)
67
81
 
@@ -86,14 +100,14 @@ module ScoutApm
86
100
 
87
101
  logger.debug("Deleting the now-reported layaway files for #{timestamp.to_s}")
88
102
  delete_files_for(timestamp) # also removes the coodinator_file
89
-
90
- logger.debug("Checking for any Stale layaway files")
91
- delete_stale_files(timestamp.to_time - STALE_AGE)
92
103
  else
93
104
  File.unlink(coordinator_file)
94
105
  logger.debug("No layaway files to report")
95
106
  end
96
107
 
108
+ logger.debug("Checking for any Stale layaway files")
109
+ delete_stale_files(timestamp.to_time - STALE_AGE)
110
+
97
111
  true
98
112
  rescue Exception => e
99
113
  logger.debug("Caught an exception in with_claim, with the coordination file locked: #{e.message}, #{e.backtrace.inspect}")
@@ -16,6 +16,10 @@ module ScoutApm
16
16
  @scope ||= call(["Controller", "Job"])
17
17
  end
18
18
 
19
+ def controller
20
+ @controller ||= call(["Controller"])
21
+ end
22
+
19
23
  def job
20
24
  @job ||= call(["Job"])
21
25
  end
@@ -10,8 +10,8 @@ module ScoutApm
10
10
  req.annotate_request(:uri => env["REQUEST_PATH"]) rescue nil
11
11
  req.context.add_user(:ip => env["REMOTE_ADDR"]) rescue nil
12
12
 
13
- req.web!
14
- req.start_layer(ScoutApm::Layer.new('Controller', endpoint_name))
13
+ layer = ScoutApm::Layer.new('Controller', endpoint_name)
14
+ req.start_layer(layer)
15
15
 
16
16
  begin
17
17
  yield
@@ -17,6 +17,8 @@ module ScoutApm
17
17
  end
18
18
 
19
19
  def start
20
+ require 'webrick'
21
+
20
22
  @server = WEBrick::HTTPServer.new(
21
23
  :BindAddress => bind,
22
24
  :Port => port,
@@ -26,10 +26,6 @@ module ScoutApm
26
26
  # Can be nil if we never reach a Rails Controller
27
27
  attr_reader :headers
28
28
 
29
- # What kind of request is this? A trace of a web request, or a background job?
30
- # Use job! and web! to set, and job? and web? to query
31
- attr_reader :request_type
32
-
33
29
  # This maintains a lookup hash of Layer names and call counts. It's used to trigger fetching a backtrace on n+1 calls.
34
30
  # Note that layer names might not be Strings - can alse be Utils::ActiveRecordMetricName. Also, this would fail for layers
35
31
  # with same names across multiple types.
@@ -222,20 +218,14 @@ module ScoutApm
222
218
  @headers = headers
223
219
  end
224
220
 
225
- def job!
226
- @request_type = "job"
227
- end
228
-
221
+ # This request is a job transaction iff it has a 'Job' layer
229
222
  def job?
230
- request_type == "job"
231
- end
232
-
233
- def web!
234
- @request_type = "web"
223
+ layer_finder.job != nil
235
224
  end
236
225
 
226
+ # This request is a web transaction iff it has a 'Controller' layer
237
227
  def web?
238
- request_type == "web"
228
+ layer_finder.controller != nil
239
229
  end
240
230
 
241
231
  def instant?
@@ -279,7 +269,6 @@ module ScoutApm
279
269
  LayerConverters::SlowRequestConverter,
280
270
  ]
281
271
 
282
- layer_finder = LayerConverters::FindLayerByType.new(self)
283
272
  walker = LayerConverters::DepthFirstWalker.new(self.root_layer)
284
273
  converters = converters.map do |klass|
285
274
  instance = klass.new(@agent_context, self, layer_finder, @store)
@@ -301,6 +290,10 @@ module ScoutApm
301
290
  end
302
291
  end
303
292
 
293
+ def layer_finder
294
+ @layer_finder ||= LayerConverters::FindLayerByType.new(self)
295
+ end
296
+
304
297
  # Ensure the background worker thread is up & running - a fallback if other
305
298
  # detection doesn't achieve this at boot.
306
299
  def ensure_background_worker
@@ -1,4 +1,4 @@
1
1
  module ScoutApm
2
- VERSION = "2.4.0.pre3"
2
+ VERSION = "2.4.0"
3
3
  end
4
4
 
@@ -11,7 +11,6 @@ class SidekiqTest < Minitest::Test
11
11
  def test_middleware_call_happy_path
12
12
  fake_request = mock
13
13
  fake_request.expects(:annotate_request)
14
- fake_request.expects(:job!)
15
14
  fake_request.expects(:start_layer).twice
16
15
  fake_request.expects(:stop_layer).twice
17
16
  fake_request.expects(:error!).never
@@ -29,7 +28,6 @@ class SidekiqTest < Minitest::Test
29
28
  def test_middleware_call_job_exception
30
29
  fake_request = mock
31
30
  fake_request.expects(:annotate_request)
32
- fake_request.expects(:job!)
33
31
  fake_request.expects(:start_layer).twice
34
32
  fake_request.expects(:stop_layer).twice
35
33
  fake_request.expects(:error!)
@@ -47,7 +45,6 @@ class SidekiqTest < Minitest::Test
47
45
  def test_middleware_call_edge_cases
48
46
  fake_request = mock
49
47
  fake_request.expects(:annotate_request)
50
- fake_request.expects(:job!)
51
48
  fake_request.expects(:start_layer).twice
52
49
  fake_request.expects(:stop_layer).twice
53
50
  fake_request.expects(:error!)
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
  class TrackedRequestDumpAndLoadTest < Minitest::Test
4
4
  # TrackedRequest must be marshalable
5
5
  def test_marshal_dump_load
6
- tr = ScoutApm::TrackedRequest.new(ScoutApm::FakeStore.new)
6
+ tr = ScoutApm::TrackedRequest.new(ScoutApm::AgentContext.new, ScoutApm::FakeStore.new)
7
7
  tr.prepare_to_dump!
8
8
 
9
9
  dumped = Marshal.dump(tr)
@@ -12,58 +12,22 @@ class TrackedRequestDumpAndLoadTest < Minitest::Test
12
12
  end
13
13
 
14
14
  def test_restore_store
15
- faux = ScoutApm::FakeStore.new
16
- tr = ScoutApm::TrackedRequest.new(faux)
17
- assert_equal faux, tr.instance_variable_get("@store")
15
+ faux_store = ScoutApm::FakeStore.new
16
+ context = ScoutApm::AgentContext.new
17
+ tr = ScoutApm::TrackedRequest.new(context, faux_store)
18
+ assert_equal faux_store, tr.instance_variable_get("@store")
18
19
 
19
20
  tr.prepare_to_dump!
20
21
  assert_nil tr.instance_variable_get("@store")
21
22
 
22
23
  tr.restore_store
23
- assert_equal ScoutApm::Agent.instance.store, tr.instance_variable_get("@store")
24
- end
25
- end
26
-
27
- class TrackedRequestFlagsTest < Minitest::Test
28
- def test_set_web
29
- tr = ScoutApm::TrackedRequest.new(ScoutApm::FakeStore.new)
30
- assert_false tr.web?
31
- tr.web!
32
- assert tr.web?
33
- end
34
-
35
- def test_set_job
36
- tr = ScoutApm::TrackedRequest.new(ScoutApm::FakeStore.new)
37
- assert ! tr.job?
38
- tr.job!
39
- assert tr.job?
40
- end
41
-
42
- def test_set_error
43
- tr = ScoutApm::TrackedRequest.new(ScoutApm::FakeStore.new)
44
- assert_false tr.error?
45
- tr.error!
46
- assert tr.error?
47
- end
48
-
49
- def test_set_error_and_web
50
- tr = ScoutApm::TrackedRequest.new(ScoutApm::FakeStore.new)
51
- assert_false tr.error?
52
- assert_false tr.web?
53
-
54
- tr.web!
55
- assert_false tr.error?
56
- assert tr.web?
57
-
58
- tr.error!
59
- assert tr.error?
60
- assert tr.web?
24
+ assert_equal context.store, tr.instance_variable_get("@store")
61
25
  end
62
26
  end
63
27
 
64
28
  class TrackedRequestLayerManipulationTest < Minitest::Test
65
29
  def test_start_layer
66
- tr = ScoutApm::TrackedRequest.new(ScoutApm::FakeStore.new)
30
+ tr = ScoutApm::TrackedRequest.new(ScoutApm::AgentContext.new, ScoutApm::FakeStore.new)
67
31
  tr.start_layer(ScoutApm::Layer.new("Foo", "Bar"))
68
32
 
69
33
  assert_equal "Foo", tr.current_layer.type
@@ -74,7 +38,7 @@ class TrackedRequestLayerManipulationTest < Minitest::Test
74
38
  controller_layer = ScoutApm::Layer.new("Controller", "users/index")
75
39
  ar_layer = ScoutApm::Layer.new("ActiveRecord", "Users#find")
76
40
 
77
- tr = ScoutApm::TrackedRequest.new(ScoutApm::FakeStore.new)
41
+ tr = ScoutApm::TrackedRequest.new(ScoutApm::AgentContext.new, ScoutApm::FakeStore.new)
78
42
  tr.start_layer(controller_layer)
79
43
  tr.start_layer(ar_layer)
80
44
 
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.0.pre3
4
+ version: 2.4.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: 2017-12-21 00:00:00.000000000 Z
12
+ date: 2018-01-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -360,9 +360,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
360
360
  version: '0'
361
361
  required_rubygems_version: !ruby/object:Gem::Requirement
362
362
  requirements:
363
- - - ">"
363
+ - - ">="
364
364
  - !ruby/object:Gem::Version
365
- version: 1.3.1
365
+ version: '0'
366
366
  requirements: []
367
367
  rubyforge_project: scout_apm
368
368
  rubygems_version: 2.4.5.2