scout_apm 2.1.2 → 2.1.3
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 +4 -4
- data/CHANGELOG.markdown +9 -0
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +49 -16
- data/lib/scout_apm/context.rb +9 -5
- data/lib/scout_apm/tracked_request.rb +20 -6
- data/lib/scout_apm/version.rb +1 -1
- data/scout_apm.gemspec +1 -0
- data/test/test_helper.rb +1 -1
- data/test/unit/background_job_integrations/sidekiq_test.rb +104 -0
- data/test/unit/context_test.rb +30 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee80eaab8042686350c223a715ff7a12ff7b5eee
|
4
|
+
data.tar.gz: 13d93be3815d1b014716081d130466764a79aeed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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) &&
|
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
|
-
|
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
|
-
|
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
|
-
|
67
|
-
|
68
|
-
req.stop_layer # Queue
|
100
|
+
rescue
|
101
|
+
0
|
69
102
|
end
|
70
103
|
end
|
71
104
|
end
|
data/lib/scout_apm/context.rb
CHANGED
@@ -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
|
79
|
-
|
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
|
-
|
91
|
-
ScoutApm::Agent.instance.logger.
|
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.
|
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 =
|
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
|
319
|
+
@ignoring_children += 1
|
307
320
|
end
|
308
321
|
|
309
322
|
def acknowledge_children!
|
310
|
-
@ignoring_children
|
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
|
data/lib/scout_apm/version.rb
CHANGED
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
@@ -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.
|
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-
|
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
|