scout_apm 2.1.2 → 2.1.3

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: c4c35583425f2cec75988d311df46da4b1cea195
4
- data.tar.gz: 3e2b125f355a1a5e89037ecf20e2c2d71e7674e6
3
+ metadata.gz: ee80eaab8042686350c223a715ff7a12ff7b5eee
4
+ data.tar.gz: 13d93be3815d1b014716081d130466764a79aeed
5
5
  SHA512:
6
- metadata.gz: f9a78acc7da99ee83c64974b0b32703016acf296855558c09bf7e72b640cef692d9e366f019e687b8d7cf1f337b8085712efa8d2e8a31b7530d314d8a4f483b2
7
- data.tar.gz: 876fd43670907912db636a647cdc858205b73733a4f06ee739bf4c592f70aceb1ed3e153b4a58437cbe63146f2af28a44d15e08924d6f1ab6f58f8b0cc325db8
6
+ metadata.gz: bba413fa76099cdede351d9fa6263ea53e93448a8235918ddac4a9c1072174aff0fb084a5e3cd591079e656d779c369838bc6866b2644c75772e4ab0dc85442c
7
+ data.tar.gz: ccbc7c25bc0aae891b79563b14c4bc4d72ca8f1ebe92b1a32c19e7e74861e61a3003208117a5d9a26dba3c2d83f7b49b7fc351d5df3670ff189c59cacd865869
data/CHANGELOG.markdown CHANGED
@@ -1,5 +1,14 @@
1
1
  # master
2
2
 
3
+ # 2.1.3
4
+
5
+ * Less noisy output on errors with Context
6
+ * Not logging errors w/nil keys or values
7
+ * Bumping log level down from WARN => INFO on errors
8
+ * Fix error with complicated AR queries
9
+ * Caused high log noise
10
+ * Sidekiq instrumentation changes to handle a variety of edge cases
11
+
3
12
  # 2.1.2
4
13
 
5
14
  * Applies `Rails.application.config.filter_parameters` settings to reported transaction trace uris
@@ -8,7 +8,7 @@ module ScoutApm
8
8
  end
9
9
 
10
10
  def present?
11
- defined?(::Sidekiq) && (File.basename($0) =~ /\Asidekiq/)
11
+ defined?(::Sidekiq) && File.basename($PROGRAM_NAME).start_with?('sidekiq')
12
12
  end
13
13
 
14
14
  def forking?
@@ -16,22 +16,33 @@ module ScoutApm
16
16
  end
17
17
 
18
18
  def install
19
+ install_tracer
20
+ add_middleware
21
+ install_processor
22
+ end
23
+
24
+ def install_tracer
19
25
  # ScoutApm::Tracer is not available when this class is defined
20
26
  SidekiqMiddleware.class_eval do
21
27
  include ScoutApm::Tracer
22
28
  end
29
+ end
23
30
 
31
+ def add_middleware
24
32
  ::Sidekiq.configure_server do |config|
25
33
  config.server_middleware do |chain|
26
34
  chain.add SidekiqMiddleware
27
35
  end
28
36
  end
37
+ end
29
38
 
39
+ def install_processor
30
40
  require 'sidekiq/processor' # sidekiq v4 has not loaded this file by this point
31
41
 
32
42
  ::Sidekiq::Processor.class_eval do
33
43
  def initialize_with_scout(boss)
34
- ::ScoutApm::Agent.instance.start_background_worker unless ::ScoutApm::Agent.instance.background_worker_running?
44
+ agent = ::ScoutApm::Agent.instance
45
+ agent.start_background_worker
35
46
  initialize_without_scout(boss)
36
47
  end
37
48
 
@@ -41,31 +52,53 @@ module ScoutApm
41
52
  end
42
53
  end
43
54
 
55
+ # We insert this middleware into the Sidekiq stack, to capture each job,
56
+ # and time them.
44
57
  class SidekiqMiddleware
45
- def call(worker, msg, queue)
46
- job_class = msg["class"] # TODO: Validate this across different versions of Sidekiq
47
- if job_class == "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper" && msg.has_key?("wrapped")
48
- job_class = msg["wrapped"]
49
- end
50
-
51
- latency = (Time.now.to_f - (msg['enqueued_at'] || msg['created_at']))
58
+ ACTIVE_JOB_KLASS = 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper'.freeze
52
59
 
60
+ def call(_worker, msg, queue)
53
61
  req = ScoutApm::RequestManager.lookup
54
62
  req.job!
55
- req.annotate_request(:queue_latency => latency)
56
-
57
- req.start_layer( ScoutApm::Layer.new("Queue", queue) )
58
- req.start_layer( ScoutApm::Layer.new("Job", job_class) )
63
+ req.annotate_request(:queue_latency => latency(msg))
59
64
 
60
65
  begin
66
+ req.start_layer(ScoutApm::Layer.new('Queue', queue))
67
+ started_queue = true
68
+ req.start_layer(ScoutApm::Layer.new('Job', job_class(msg)))
69
+ started_job = true
70
+
61
71
  yield
62
72
  rescue
63
73
  req.error!
64
74
  raise
75
+ ensure
76
+ req.stop_layer if started_job
77
+ req.stop_layer if started_queue
78
+ end
79
+ end
80
+
81
+ UNKNOWN_CLASS_PLACEHOLDER = 'UnknownJob'.freeze
82
+
83
+ def job_class(msg)
84
+ job_class = msg.fetch('class', UNKNOWN_CLASS_PLACEHOLDER)
85
+ if job_class == ACTIVE_JOB_KLASS && msg.key?('wrapped')
86
+ job_class = msg['wrapped']
87
+ end
88
+ job_class
89
+ rescue
90
+ UNKNOWN_CLASS_PLACEHOLDER
91
+ end
92
+
93
+ def latency(msg, time = Time.now.to_f)
94
+ created_at = msg['enqueued_at'] || msg['created_at']
95
+ if created_at
96
+ (time - created_at)
97
+ else
98
+ 0
65
99
  end
66
- ensure
67
- req.stop_layer # Job
68
- req.stop_layer # Queue
100
+ rescue
101
+ 0
69
102
  end
70
103
  end
71
104
  end
@@ -75,8 +75,10 @@ module ScoutApm
75
75
  def value_valid?(key_value)
76
76
  # ensure one of our accepted types.
77
77
  value = key_value.values.last
78
- if !valid_type?([String, Symbol, Numeric, Time, Date, TrueClass, FalseClass],value)
79
- ScoutApm::Agent.instance.logger.warn "The value for [#{key_value.keys.first}] is not a valid type [#{value.class}]."
78
+ if value.nil?
79
+ false # don't log this ... easy to happen
80
+ elsif !valid_type?([String, Symbol, Numeric, Time, Date, TrueClass, FalseClass],value)
81
+ ScoutApm::Agent.instance.logger.info "The value for [#{key_value.keys.first}] is not a valid type [#{value.class}]."
80
82
  false
81
83
  else
82
84
  true
@@ -86,14 +88,16 @@ module ScoutApm
86
88
  # for consistently with #value_valid?, takes a hash eventhough the value isn't yet used.
87
89
  def key_valid?(key_value)
88
90
  key = key_value.keys.first
91
+ if key.nil?
92
+ false # don't log this ... easy to happen
89
93
  # ensure a string or a symbol
90
- if !valid_type?([String, Symbol],key)
91
- ScoutApm::Agent.instance.logger.warn "The key [#{key}] is not a valid type [#{key.class}]."
94
+ elsif !valid_type?([String, Symbol],key)
95
+ ScoutApm::Agent.instance.logger.info "The key [#{key}] is not a valid type [#{key.class}]."
92
96
  return false
93
97
  end
94
98
  # only alphanumeric, dash, and underscore allowed.
95
99
  if key.to_s.match(/[^\w-]/)
96
- ScoutApm::Agent.instance.logger.warn "They key name [#{key}] is not valid."
100
+ ScoutApm::Agent.instance.logger.info "They key name [#{key}] is not valid."
97
101
  return false
98
102
  end
99
103
  true
@@ -47,7 +47,7 @@ module ScoutApm
47
47
  @layers = []
48
48
  @call_set = Hash.new { |h, k| h[k] = CallSet.new }
49
49
  @annotations = {}
50
- @ignoring_children = false
50
+ @ignoring_children = 0
51
51
  @context = Context.new
52
52
  @root_layer = nil
53
53
  @error = false
@@ -297,27 +297,41 @@ module ScoutApm
297
297
  # specific, and useful than the fact that InfluxDB happens to use Net::HTTP
298
298
  # internally
299
299
  #
300
- # When enabled, new layers won't be added to the current Request.
300
+ # When enabled, new layers won't be added to the current Request, and calls
301
+ # to stop_layer will be ignored.
301
302
  #
302
303
  # Do not forget to turn if off when leaving a layer, it is the
303
304
  # instrumentation's task to do that.
305
+ #
306
+ # When you use this in code, be sure to use it in this order:
307
+ #
308
+ # start_layer
309
+ # ignore_children
310
+ # -> call
311
+ # acknowledge_children
312
+ # stop_layer
313
+ #
314
+ # If you don't call it in this order, it's possible to get out of sync, and
315
+ # have an ignored start and an actually-executed stop, causing layers to
316
+ # get out of sync
304
317
 
305
318
  def ignore_children!
306
- @ignoring_children = true
319
+ @ignoring_children += 1
307
320
  end
308
321
 
309
322
  def acknowledge_children!
310
- @ignoring_children = false
323
+ if @ignoring_children > 0
324
+ @ignoring_children -= 1
325
+ end
311
326
  end
312
327
 
313
328
  def ignoring_children?
314
- @ignoring_children
329
+ @ignoring_children > 0
315
330
  end
316
331
 
317
332
  # Grab backtraces more aggressively when running in dev trace mode
318
333
  def backtrace_threshold
319
334
  dev_trace ? 0.05 : 0.5 # the minimum threshold in seconds to record the backtrace for a metric.
320
335
  end
321
-
322
336
  end
323
337
  end
@@ -1,4 +1,4 @@
1
1
  module ScoutApm
2
- VERSION = "2.1.2"
2
+ VERSION = "2.1.3"
3
3
  end
4
4
 
data/scout_apm.gemspec CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.add_runtime_dependency "rusage", '~> 0.2.0'
24
24
 
25
25
  s.add_development_dependency "minitest"
26
+ s.add_development_dependency 'mocha'
26
27
  s.add_development_dependency "pry"
27
28
  s.add_development_dependency "m"
28
29
  s.add_development_dependency "simplecov"
data/test/test_helper.rb CHANGED
@@ -5,6 +5,7 @@ SimpleCov.start
5
5
  require 'minitest/autorun'
6
6
  require 'minitest/unit'
7
7
  require 'minitest/pride'
8
+ require 'mocha/mini_test'
8
9
  require 'pry'
9
10
 
10
11
 
@@ -82,4 +83,3 @@ class Minitest::Test
82
83
  DATA_FILE_PATH = "#{DATA_FILE_DIR}/scout_apm.db"
83
84
  end
84
85
 
85
-
@@ -0,0 +1,104 @@
1
+ require 'test_helper'
2
+ require 'scout_apm/request_manager'
3
+ require 'scout_apm/background_job_integrations/sidekiq'
4
+
5
+ class SidekiqTest < Minitest::Test
6
+ SidekiqMiddleware = ScoutApm::BackgroundJobIntegrations::SidekiqMiddleware
7
+
8
+ ########################################
9
+ # Middleware
10
+ ########################################
11
+ def test_middleware_call_happy_path
12
+ fake_request = mock
13
+ fake_request.expects(:annotate_request)
14
+ fake_request.expects(:job!)
15
+ fake_request.expects(:start_layer).twice
16
+ fake_request.expects(:stop_layer).twice
17
+ fake_request.expects(:error!).never
18
+
19
+ ScoutApm::RequestManager.stubs(:lookup).returns(fake_request)
20
+
21
+ block_called = false
22
+ msg = { 'class' => 'MyJobClass',
23
+ 'created_at' => Time.now }
24
+
25
+ SidekiqMiddleware.new.call(nil, msg, "defaultqueue") { block_called = true }
26
+ assert_equal true, block_called
27
+ end
28
+
29
+ def test_middleware_call_job_exception
30
+ fake_request = mock
31
+ fake_request.expects(:annotate_request)
32
+ fake_request.expects(:job!)
33
+ fake_request.expects(:start_layer).twice
34
+ fake_request.expects(:stop_layer).twice
35
+ fake_request.expects(:error!)
36
+
37
+ ScoutApm::RequestManager.stubs(:lookup).returns(fake_request)
38
+
39
+ msg = { 'class' => 'MyJobClass',
40
+ 'created_at' => Time.now }
41
+
42
+ assert_raises RuntimeError do
43
+ SidekiqMiddleware.new.call(nil, msg, "defaultqueue") { raise "TheJobFailed" }
44
+ end
45
+ end
46
+
47
+ def test_middleware_call_edge_cases
48
+ fake_request = mock
49
+ fake_request.expects(:annotate_request)
50
+ fake_request.expects(:job!)
51
+ fake_request.expects(:start_layer).twice
52
+ fake_request.expects(:stop_layer).twice
53
+ fake_request.expects(:error!)
54
+
55
+ ScoutApm::RequestManager.stubs(:lookup).returns(fake_request)
56
+
57
+ # msg doesn't have anything
58
+ msg = { }
59
+
60
+ assert_raises RuntimeError do
61
+ SidekiqMiddleware.new.call(nil, msg, "defaultqueue") { raise "TheJobFailed" }
62
+ end
63
+ end
64
+
65
+ ########################################
66
+ # Job Class Determination
67
+ ########################################
68
+ def test_job_class_name_normally
69
+ msg = { 'class' => 'AGreatJob' }
70
+ assert_equal 'AGreatJob', SidekiqMiddleware.new.job_class(msg)
71
+ end
72
+
73
+ def test_job_class_name_activejob
74
+ msg = { 'class' => 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper',
75
+ 'wrapped' => 'RealJobClass' }
76
+ assert_equal 'RealJobClass', SidekiqMiddleware.new.job_class(msg)
77
+ end
78
+
79
+ def test_job_class_name_error_default
80
+ msg = {}
81
+ assert_equal 'UnknownJob', SidekiqMiddleware.new.job_class(msg)
82
+ end
83
+
84
+ ########################################
85
+ # Latency Calculation
86
+ ########################################
87
+ def test_latency_from_created_at
88
+ # Created at time 80, but now it is 200. Latency was 120
89
+ msg = { 'created_at' => 80 }
90
+ assert_equal 120, SidekiqMiddleware.new.latency(msg, 200)
91
+ end
92
+
93
+ def test_latency_from_enqueued_at
94
+ # Created at time 80, but now it is 200. Latency was 120
95
+ msg = { 'enqueued_at' => 80 }
96
+ assert_equal 120, SidekiqMiddleware.new.latency(msg, 200)
97
+ end
98
+
99
+ def test_latency_fallback
100
+ # No created at time, so fall back to 0
101
+ msg = {}
102
+ assert_equal 0, SidekiqMiddleware.new.latency(msg, 200)
103
+ end
104
+ end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+
3
+ require 'scout_apm/context'
4
+
5
+ class ContextText < Minitest::Test
6
+ def test_ignore_nil_value
7
+ context = ScoutApm::Context.new
8
+ assert hash = context.add(:nil_key => nil, :org => 'org')
9
+ assert_equal ({:org => 'org'}), hash
10
+ end
11
+
12
+ def test_ignore_nil_key
13
+ context = ScoutApm::Context.new
14
+ assert hash = context.add(nil => nil, :org => 'org')
15
+ assert_equal ({:org => 'org'}), hash
16
+ end
17
+
18
+ def test_ignore_unsupported_value_type
19
+ context = ScoutApm::Context.new
20
+ assert hash = context.add(:array => [1,2,3,4], :org => 'org')
21
+ assert_equal ({:org => 'org'}), hash
22
+ end
23
+
24
+ def test_ignore_unsupported_key_type
25
+ context = ScoutApm::Context.new
26
+ assert hash = context.add([1,2,3,4] => 'hey', :org => 'org')
27
+ assert_equal ({:org => 'org'}), hash
28
+ end
29
+ end
30
+
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.1.2
4
+ version: 2.1.3
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-22 00:00:00.000000000 Z
12
+ date: 2016-08-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rusage
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: mocha
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: pry
44
58
  requirement: !ruby/object:Gem::Requirement
@@ -223,7 +237,9 @@ files:
223
237
  - test/data/config_test_1.yml
224
238
  - test/test_helper.rb
225
239
  - test/unit/agent_test.rb
240
+ - test/unit/background_job_integrations/sidekiq_test.rb
226
241
  - test/unit/config_test.rb
242
+ - test/unit/context_test.rb
227
243
  - test/unit/environment_test.rb
228
244
  - test/unit/histogram_test.rb
229
245
  - test/unit/ignored_uris_test.rb
@@ -267,7 +283,9 @@ test_files:
267
283
  - test/data/config_test_1.yml
268
284
  - test/test_helper.rb
269
285
  - test/unit/agent_test.rb
286
+ - test/unit/background_job_integrations/sidekiq_test.rb
270
287
  - test/unit/config_test.rb
288
+ - test/unit/context_test.rb
271
289
  - test/unit/environment_test.rb
272
290
  - test/unit/histogram_test.rb
273
291
  - test/unit/ignored_uris_test.rb