scout_apm 2.1.29 → 2.1.30
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/.gitignore +1 -0
- data/CHANGELOG.markdown +4 -0
- data/Guardfile +42 -0
- data/lib/scout_apm/agent/logging.rb +6 -1
- data/lib/scout_apm/agent.rb +52 -2
- data/lib/scout_apm/background_job_integrations/resque.rb +85 -0
- data/lib/scout_apm/background_recorder.rb +43 -0
- data/lib/scout_apm/config.rb +11 -3
- data/lib/scout_apm/environment.rb +1 -0
- data/lib/scout_apm/instruments/resque.rb +30 -0
- data/lib/scout_apm/layer_children_set.rb +7 -2
- data/lib/scout_apm/remote/message.rb +23 -0
- data/lib/scout_apm/remote/recorder.rb +57 -0
- data/lib/scout_apm/remote/router.rb +49 -0
- data/lib/scout_apm/remote/server.rb +58 -0
- data/lib/scout_apm/request_manager.rb +1 -1
- data/lib/scout_apm/synchronous_recorder.rb +26 -0
- data/lib/scout_apm/tracked_request.rb +51 -4
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +10 -0
- data/scout_apm.gemspec +2 -0
- data/test/unit/remote/test_message.rb +13 -0
- data/test/unit/remote/test_router.rb +33 -0
- data/test/unit/remote/test_server.rb +15 -0
- data/test/unit/test_tracked_request.rb +87 -0
- metadata +76 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 94cee593281d02b80d99aa3bf55a66fb4ef1adf9
|
|
4
|
+
data.tar.gz: a5aff2d47256429910ff7eb2e4f0c145bdca69f7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4a38d11380c58574636bf7ed6e086f890989edd7e7ef5a395e4d9cfecd11dc12c30206a85ea4f25324b28c4d92e49cad7be3e2e0ccd332a75f93497cc03b96d8
|
|
7
|
+
data.tar.gz: dd758e13ce68396da594a7d6bc8a04dc9f3f300d7bda905cb3676ca36a74b347ea9ace9f818b652b92150dfdb85d23656ad802c713660098f095b14d8ab88c11
|
data/.gitignore
CHANGED
data/CHANGELOG.markdown
CHANGED
data/Guardfile
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# A sample Guardfile
|
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
|
3
|
+
|
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
|
5
|
+
# directories %w(app lib config test spec features) \
|
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
|
7
|
+
|
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
|
11
|
+
#
|
|
12
|
+
# $ mkdir config
|
|
13
|
+
# $ mv Guardfile config/
|
|
14
|
+
# $ ln -s config/Guardfile .
|
|
15
|
+
#
|
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
|
17
|
+
|
|
18
|
+
guard :minitest do
|
|
19
|
+
# with Minitest::Unit
|
|
20
|
+
watch(%r{^test/(.*)\/?test_(.*)\.rb$})
|
|
21
|
+
watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
|
|
22
|
+
watch(%r{^test/test_helper\.rb$}) { 'test' }
|
|
23
|
+
|
|
24
|
+
# with Minitest::Spec
|
|
25
|
+
# watch(%r{^spec/(.*)_spec\.rb$})
|
|
26
|
+
# watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
|
27
|
+
# watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
|
|
28
|
+
|
|
29
|
+
# Rails 4
|
|
30
|
+
# watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
|
|
31
|
+
# watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }
|
|
32
|
+
# watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" }
|
|
33
|
+
# watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
|
|
34
|
+
# watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb" }
|
|
35
|
+
# watch(%r{^test/.+_test\.rb$})
|
|
36
|
+
# watch(%r{^test/test_helper\.rb$}) { 'test' }
|
|
37
|
+
|
|
38
|
+
# Rails < 4
|
|
39
|
+
# watch(%r{^app/controllers/(.*)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb" }
|
|
40
|
+
# watch(%r{^app/helpers/(.*)\.rb$}) { |m| "test/helpers/#{m[1]}_test.rb" }
|
|
41
|
+
# watch(%r{^app/models/(.*)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
|
|
42
|
+
end
|
data/lib/scout_apm/agent.rb
CHANGED
|
@@ -10,6 +10,7 @@ module ScoutApm
|
|
|
10
10
|
|
|
11
11
|
# Accessors below are for associated classes
|
|
12
12
|
attr_accessor :store
|
|
13
|
+
attr_reader :recorder
|
|
13
14
|
attr_accessor :layaway
|
|
14
15
|
attr_accessor :config
|
|
15
16
|
attr_accessor :logger
|
|
@@ -41,6 +42,9 @@ module ScoutApm
|
|
|
41
42
|
@process_start_time = Time.now
|
|
42
43
|
@options ||= options
|
|
43
44
|
|
|
45
|
+
# until the agent is started, there's no recorder
|
|
46
|
+
@recorder = nil
|
|
47
|
+
|
|
44
48
|
# Start up without attempting to load a configuration file. We need to be
|
|
45
49
|
# able to lookup configuration options like "application_root" which would
|
|
46
50
|
# then in turn influence where the configuration file came from.
|
|
@@ -54,6 +58,7 @@ module ScoutApm
|
|
|
54
58
|
@request_histograms_by_time = Hash.new { |h, k| h[k] = ScoutApm::RequestHistograms.new }
|
|
55
59
|
|
|
56
60
|
@store = ScoutApm::Store.new
|
|
61
|
+
|
|
57
62
|
@layaway = ScoutApm::Layaway.new(config, environment)
|
|
58
63
|
@metric_lookup = Hash.new
|
|
59
64
|
|
|
@@ -111,13 +116,14 @@ module ScoutApm
|
|
|
111
116
|
def start(options = {})
|
|
112
117
|
@options.merge!(options)
|
|
113
118
|
|
|
114
|
-
|
|
115
119
|
@config = ScoutApm::Config.with_file(@config.value("config_file"))
|
|
116
120
|
layaway.config = config
|
|
117
121
|
|
|
118
122
|
init_logger
|
|
119
123
|
logger.info "Attempting to start Scout Agent [#{ScoutApm::VERSION}] on [#{environment.hostname}]"
|
|
120
124
|
|
|
125
|
+
@recorder = create_recorder
|
|
126
|
+
|
|
121
127
|
@config.log_settings
|
|
122
128
|
|
|
123
129
|
@ignored_uris = ScoutApm::IgnoredUris.new(config.value('ignore'))
|
|
@@ -134,7 +140,6 @@ module ScoutApm
|
|
|
134
140
|
@started = true
|
|
135
141
|
logger.info "Starting monitoring for [#{environment.application_name}]. Framework [#{environment.framework}] App Server [#{environment.app_server}] Background Job Framework [#{environment.background_job_name}]."
|
|
136
142
|
|
|
137
|
-
|
|
138
143
|
[ ScoutApm::Instruments::Process::ProcessCpu.new(environment.processors, logger),
|
|
139
144
|
ScoutApm::Instruments::Process::ProcessMemory.new(logger),
|
|
140
145
|
ScoutApm::Instruments::PercentileSampler.new(logger, request_histograms_by_time),
|
|
@@ -256,6 +261,9 @@ module ScoutApm
|
|
|
256
261
|
|
|
257
262
|
install_exit_handler
|
|
258
263
|
|
|
264
|
+
@recorder = create_recorder
|
|
265
|
+
logger.info("recorder is now: #{@recorder.class}")
|
|
266
|
+
|
|
259
267
|
@background_worker = ScoutApm::BackgroundWorker.new
|
|
260
268
|
@background_worker_thread = Thread.new do
|
|
261
269
|
@background_worker.start {
|
|
@@ -336,5 +344,47 @@ module ScoutApm
|
|
|
336
344
|
def background_job_missing?(options = {})
|
|
337
345
|
environment.background_job_integration.nil? && !options[:skip_background_job_check]
|
|
338
346
|
end
|
|
347
|
+
|
|
348
|
+
def clear_recorder
|
|
349
|
+
@recorder = nil
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def create_recorder
|
|
353
|
+
if @recorder
|
|
354
|
+
return @recorder
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
if config.value("async_recording")
|
|
358
|
+
logger.debug("Using asynchronous recording")
|
|
359
|
+
ScoutApm::BackgroundRecorder.new(logger).start
|
|
360
|
+
else
|
|
361
|
+
logger.debug("Using synchronous recording")
|
|
362
|
+
ScoutApm::SynchronousRecorder.new(logger).start
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def start_remote_server(bind, port)
|
|
367
|
+
return if @remote_server && @remote_server.running?
|
|
368
|
+
|
|
369
|
+
logger.info("Starting Remote Agent Server")
|
|
370
|
+
|
|
371
|
+
# Start the listening web server only in parent process.
|
|
372
|
+
@remote_server = ScoutApm::Remote::Server.new(
|
|
373
|
+
bind,
|
|
374
|
+
port,
|
|
375
|
+
ScoutApm::Remote::Router.new(ScoutApm::SynchronousRecorder.new(logger), logger),
|
|
376
|
+
logger
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
@remote_server.start
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# Execute this in the child process of a remote agent. The parent is
|
|
383
|
+
# expected to have its accepting webserver up and running
|
|
384
|
+
def use_remote_recorder(host, port)
|
|
385
|
+
logger.debug("Becoming Remote Agent (reporting to: #{host}:#{port})")
|
|
386
|
+
@recorder = ScoutApm::Remote::Recorder.new(host, port, logger)
|
|
387
|
+
@store = ScoutApm::FakeStore.new
|
|
388
|
+
end
|
|
339
389
|
end
|
|
340
390
|
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module ScoutApm
|
|
2
|
+
module BackgroundJobIntegrations
|
|
3
|
+
class Resque
|
|
4
|
+
def name
|
|
5
|
+
:resque
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def present?
|
|
9
|
+
defined?(::Resque) &&
|
|
10
|
+
::Resque.respond_to?(:before_first_fork) &&
|
|
11
|
+
::Resque.respond_to?(:after_fork)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Lies. This forks really aggressively, but we have to do handling
|
|
15
|
+
# of it manually here, rather than via any sort of automatic
|
|
16
|
+
# background worker starting
|
|
17
|
+
def forking?
|
|
18
|
+
false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def install
|
|
22
|
+
install_before_fork
|
|
23
|
+
install_after_fork
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def install_before_fork
|
|
27
|
+
::Resque.before_first_fork do
|
|
28
|
+
begin
|
|
29
|
+
ScoutApm::Agent.instance.start(:skip_app_server_check => true)
|
|
30
|
+
ScoutApm::Agent.instance.start_background_worker
|
|
31
|
+
ScoutApm::Agent.instance.start_remote_server(bind, port)
|
|
32
|
+
rescue Errno::EADDRINUSE
|
|
33
|
+
ScoutApm::Agent.instance.logger.warn "Error while Installing Resque Instruments, Port #{port} already in use. Set via the `remote_agent_port` configuration option"
|
|
34
|
+
rescue => e
|
|
35
|
+
ScoutApm::Agent.instance.logger.warn "Error while Installing Resque before_first_fork: #{e.inspect}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def install_after_fork
|
|
41
|
+
::Resque.after_fork do
|
|
42
|
+
begin
|
|
43
|
+
ScoutApm::Agent.instance.use_remote_recorder(bind, port)
|
|
44
|
+
inject_job_instrument
|
|
45
|
+
rescue => e
|
|
46
|
+
ScoutApm::Agent.instance.logger.warn "Error while Installing Resque after_fork: #{e.inspect}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Insert ourselves into the point when resque turns a string "TestJob"
|
|
52
|
+
# into the class constant TestJob, and insert our instrumentation plugin
|
|
53
|
+
# into that constantized class
|
|
54
|
+
#
|
|
55
|
+
# This automates away any need for the user to insert our instrumentation into
|
|
56
|
+
# each of their jobs
|
|
57
|
+
def inject_job_instrument
|
|
58
|
+
::Resque::Job.class_eval do
|
|
59
|
+
def payload_class_with_scout_instruments
|
|
60
|
+
klass = payload_class_without_scout_instruments
|
|
61
|
+
klass.extend(ScoutApm::Instruments::Resque)
|
|
62
|
+
klass
|
|
63
|
+
end
|
|
64
|
+
alias_method :payload_class_without_scout_instruments, :payload_class
|
|
65
|
+
alias_method :payload_class, :payload_class_with_scout_instruments
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def bind
|
|
72
|
+
config.value("remote_agent_host")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def port
|
|
76
|
+
config.value("remote_agent_port")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def config
|
|
80
|
+
@config || ScoutApm::Agent.instance.config
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Provide a background thread queue to do the processing of
|
|
2
|
+
# TrackedRequest objects, to remove it from the hot-path of returning a
|
|
3
|
+
# web response
|
|
4
|
+
|
|
5
|
+
module ScoutApm
|
|
6
|
+
class BackgroundRecorder
|
|
7
|
+
attr_reader :queue
|
|
8
|
+
attr_reader :thread
|
|
9
|
+
attr_reader :logger
|
|
10
|
+
|
|
11
|
+
def initialize(logger)
|
|
12
|
+
@logger = logger
|
|
13
|
+
@queue = Queue.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def start
|
|
17
|
+
logger.info("Starting BackgroundRecorder")
|
|
18
|
+
@thread = Thread.new(&method(:thread_func))
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def stop
|
|
23
|
+
@thread.kill
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def record!(request)
|
|
27
|
+
start unless @thread.alive?
|
|
28
|
+
@queue.push(request)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def thread_func
|
|
32
|
+
while req = queue.pop
|
|
33
|
+
begin
|
|
34
|
+
logger.debug("recording in thread. Queue size: #{queue.size}")
|
|
35
|
+
# For now, just proxy right back into the TrackedRequest object's record function
|
|
36
|
+
req.record!
|
|
37
|
+
rescue => e
|
|
38
|
+
logger.warn("Error in BackgroundRecorder - #{e.message} : #{e.backtrace}")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/scout_apm/config.rb
CHANGED
|
@@ -27,6 +27,8 @@ require 'scout_apm/environment'
|
|
|
27
27
|
# report_format - 'json' or 'marshal'. Marshal is legacy and will be removed.
|
|
28
28
|
# scm_subdirectory - if the app root lives in source management in a subdirectory. E.g. #{SCM_ROOT}/src
|
|
29
29
|
# uri_reporting - 'path' or 'full_path' default is 'full_path', which reports URL params as well as the path.
|
|
30
|
+
# remote_agent_host - Internal: What host to bind to, and also send messages to for remote. Default: 127.0.0.1.
|
|
31
|
+
# remote_agent_port - What port to bind the remote webserver to
|
|
30
32
|
#
|
|
31
33
|
# Any of these config settings can be set with an environment variable prefixed
|
|
32
34
|
# by SCOUT_ and uppercasing the key: SCOUT_LOG_LEVEL for instance.
|
|
@@ -35,6 +37,7 @@ module ScoutApm
|
|
|
35
37
|
class Config
|
|
36
38
|
KNOWN_CONFIG_OPTIONS = [
|
|
37
39
|
'application_root',
|
|
40
|
+
'async_recording',
|
|
38
41
|
'compress_payload',
|
|
39
42
|
'config_file',
|
|
40
43
|
'data_file',
|
|
@@ -53,6 +56,8 @@ module ScoutApm
|
|
|
53
56
|
'name',
|
|
54
57
|
'profile',
|
|
55
58
|
'proxy',
|
|
59
|
+
'remote_agent_host',
|
|
60
|
+
'remote_agent_port',
|
|
56
61
|
'report_format',
|
|
57
62
|
'scm_subdirectory',
|
|
58
63
|
'uri_reporting',
|
|
@@ -129,11 +134,12 @@ module ScoutApm
|
|
|
129
134
|
|
|
130
135
|
|
|
131
136
|
SETTING_COERCIONS = {
|
|
132
|
-
"
|
|
133
|
-
"enable_background_jobs" => BooleanCoercion.new,
|
|
134
|
-
"dev_trace" => BooleanCoercion.new,
|
|
137
|
+
"async_recording" => BooleanCoercion.new,
|
|
135
138
|
"detailed_middleware" => BooleanCoercion.new,
|
|
139
|
+
"dev_trace" => BooleanCoercion.new,
|
|
140
|
+
"enable_background_jobs" => BooleanCoercion.new,
|
|
136
141
|
"ignore" => JsonCoercion.new,
|
|
142
|
+
"monitor" => BooleanCoercion.new,
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
|
|
@@ -219,6 +225,8 @@ module ScoutApm
|
|
|
219
225
|
'report_format' => 'json',
|
|
220
226
|
'scm_subdirectory' => '',
|
|
221
227
|
'uri_reporting' => 'full_path',
|
|
228
|
+
'remote_agent_host' => '127.0.0.1',
|
|
229
|
+
'remote_agent_port' => 7721, # picked at random
|
|
222
230
|
}.freeze
|
|
223
231
|
|
|
224
232
|
def value(key)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module ScoutApm
|
|
2
|
+
module Instruments
|
|
3
|
+
module Resque
|
|
4
|
+
def around_perform_with_scout_instruments(*args)
|
|
5
|
+
job_name = self.to_s
|
|
6
|
+
queue = @queue
|
|
7
|
+
|
|
8
|
+
req = ScoutApm::RequestManager.lookup
|
|
9
|
+
req.job!
|
|
10
|
+
# req.annotate_request(:queue_latency => latency(msg))
|
|
11
|
+
|
|
12
|
+
begin
|
|
13
|
+
req.start_layer(ScoutApm::Layer.new('Queue', queue))
|
|
14
|
+
started_queue = true
|
|
15
|
+
req.start_layer(ScoutApm::Layer.new('Job', job_name))
|
|
16
|
+
started_job = true
|
|
17
|
+
|
|
18
|
+
yield
|
|
19
|
+
rescue => e
|
|
20
|
+
req.error!
|
|
21
|
+
raise
|
|
22
|
+
ensure
|
|
23
|
+
req.stop_layer if started_job
|
|
24
|
+
req.stop_layer if started_queue
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
@@ -27,18 +27,23 @@ module ScoutApm
|
|
|
27
27
|
private :children
|
|
28
28
|
|
|
29
29
|
def initialize(unique_cutoff = DEFAULT_UNIQUE_CUTOFF)
|
|
30
|
-
@children = Hash.new
|
|
30
|
+
@children = Hash.new
|
|
31
31
|
@limited_layers = nil # populated when needed
|
|
32
32
|
@unique_cutoff = unique_cutoff
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def child_set(metric_type)
|
|
36
|
+
children[metric_type] = Set.new if !children.has_key?(metric_type)
|
|
37
|
+
children[metric_type]
|
|
38
|
+
end
|
|
39
|
+
|
|
35
40
|
# Add a new layer into this set
|
|
36
41
|
# Only add completed layers - otherwise this will collect up incorrect info
|
|
37
42
|
# into the created LimitedLayer, since it will "freeze" any current data for
|
|
38
43
|
# total_call_time and similar methods.
|
|
39
44
|
def <<(child)
|
|
40
45
|
metric_type = child.type
|
|
41
|
-
set =
|
|
46
|
+
set = child_set(metric_type)
|
|
42
47
|
|
|
43
48
|
if set.size >= unique_cutoff
|
|
44
49
|
# find limited_layer
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module ScoutApm
|
|
2
|
+
module Remote
|
|
3
|
+
class Message
|
|
4
|
+
attr_reader :type
|
|
5
|
+
attr_reader :command
|
|
6
|
+
attr_reader :args
|
|
7
|
+
|
|
8
|
+
def initialize(type, command, *args)
|
|
9
|
+
@type = type
|
|
10
|
+
@command = command
|
|
11
|
+
@args = args
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.decode(msg)
|
|
15
|
+
Marshal.load(msg)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def encode
|
|
19
|
+
Marshal.dump(self)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module ScoutApm
|
|
2
|
+
module Remote
|
|
3
|
+
class Recorder
|
|
4
|
+
attr_reader :logger
|
|
5
|
+
attr_reader :remote_agent_host
|
|
6
|
+
attr_reader :remote_agent_port
|
|
7
|
+
|
|
8
|
+
def initialize(remote_agent_host, remote_agent_port, logger)
|
|
9
|
+
@remote_agent_host = remote_agent_host
|
|
10
|
+
@remote_agent_port = remote_agent_port
|
|
11
|
+
@logger = logger
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def start
|
|
15
|
+
# nothing to do
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def stop
|
|
20
|
+
# nothing to do
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def record!(request)
|
|
24
|
+
begin
|
|
25
|
+
t1 = Time.now
|
|
26
|
+
# Mark this request as recorded, so the next lookup on this thread, it
|
|
27
|
+
# can be recreated
|
|
28
|
+
request.recorded!
|
|
29
|
+
|
|
30
|
+
# Only send requests that we actually want. Incidental http &
|
|
31
|
+
# background thread stuff can just be dropped
|
|
32
|
+
unless request.job? || request.web?
|
|
33
|
+
return
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
request.prepare_to_dump!
|
|
37
|
+
message = ScoutApm::Remote::Message.new('record', 'record!', request)
|
|
38
|
+
encoded = message.encode
|
|
39
|
+
logger.debug "Remote Agent: Posting a message of length: #{encoded.length}"
|
|
40
|
+
post(encoded)
|
|
41
|
+
t2 = Time.now
|
|
42
|
+
|
|
43
|
+
logger.debug("Remote Recording took: #{t2.to_f - t1.to_f} seconds")
|
|
44
|
+
rescue => e
|
|
45
|
+
logger.debug "Remote: Error while sending to collector: #{e.inspect}, #{e.backtrace.join("\n")}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def post(encoded_message)
|
|
50
|
+
http = Net::HTTP.new(remote_agent_host, remote_agent_port)
|
|
51
|
+
request = Net::HTTP::Post.new("/users")
|
|
52
|
+
request.body = encoded_message
|
|
53
|
+
response = http.request(request)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module ScoutApm
|
|
2
|
+
module Remote
|
|
3
|
+
class Router
|
|
4
|
+
attr_reader :logger
|
|
5
|
+
attr_reader :routes
|
|
6
|
+
|
|
7
|
+
# If/When we add different types, this signature should change to a hash
|
|
8
|
+
# of {type => Object}, rather than building it in the initializer here.
|
|
9
|
+
#
|
|
10
|
+
# Keys of routes should be strings
|
|
11
|
+
def initialize(recorder, logger)
|
|
12
|
+
@routes = {
|
|
13
|
+
'record' => recorder
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@logger = logger
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# A message is a 2 element array [:type, :command, [args]].
|
|
20
|
+
# For this first creation, this should be ['record', 'record', [TrackedRequest]] (the args arg should always be an array, even w/ only 1 item)
|
|
21
|
+
#
|
|
22
|
+
# Where
|
|
23
|
+
# type: ['recorder']
|
|
24
|
+
# command: any function supported on that type of object
|
|
25
|
+
# args: any array of arguments
|
|
26
|
+
#
|
|
27
|
+
# Raises on unknown message
|
|
28
|
+
#
|
|
29
|
+
# Returns whatever the recipient object returns
|
|
30
|
+
def handle(msg)
|
|
31
|
+
message = Remote::Message.decode(msg)
|
|
32
|
+
assert_type(message)
|
|
33
|
+
call_route(message)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def assert_type(message)
|
|
39
|
+
if ! routes.keys.include?(message.type.to_s)
|
|
40
|
+
raise "Unknown type: #{message.type.to_s}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def call_route(message)
|
|
45
|
+
routes[message.type].send(message.command, *message.args)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Web Server bound to localhost that listens for remote agent reports. Forwards
|
|
2
|
+
# onto the router
|
|
3
|
+
module ScoutApm
|
|
4
|
+
module Remote
|
|
5
|
+
class Server
|
|
6
|
+
attr_reader :router
|
|
7
|
+
attr_reader :bind
|
|
8
|
+
attr_reader :port
|
|
9
|
+
attr_reader :logger
|
|
10
|
+
|
|
11
|
+
def initialize(bind, port, router, logger)
|
|
12
|
+
@router = router
|
|
13
|
+
@logger = logger
|
|
14
|
+
@bind = bind
|
|
15
|
+
@port = port
|
|
16
|
+
@server = nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def start
|
|
20
|
+
@server = WEBrick::HTTPServer.new(
|
|
21
|
+
:BindAddress => bind,
|
|
22
|
+
:Port => port,
|
|
23
|
+
:AccessLog => [],
|
|
24
|
+
:Logger => @logger
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
@server.mount_proc '/' do |request, response|
|
|
28
|
+
router.handle(request.body)
|
|
29
|
+
|
|
30
|
+
# arbitrary response, client doesn't expect anything in particular
|
|
31
|
+
response.body = 'Ok'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
@thread = Thread.new do
|
|
35
|
+
begin
|
|
36
|
+
logger.debug("Remote: Starting Server on #{bind}:#{port}")
|
|
37
|
+
|
|
38
|
+
@server.start
|
|
39
|
+
|
|
40
|
+
logger.debug("Remote: Server returned after #start call, thread exiting")
|
|
41
|
+
rescue => e
|
|
42
|
+
logger.debug("Remote: Server Exception, #{e}")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def running?
|
|
48
|
+
@thread.alive?
|
|
49
|
+
@server && @server.status == :Running
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def stop
|
|
53
|
+
@server.stop
|
|
54
|
+
@thread.kill
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Provide a synchronous approach to recording TrackedRequests
|
|
2
|
+
# Doesn't attempt to background the work, or do it elsewhere. It happens
|
|
3
|
+
# inline, in the caller thread right when record! is called
|
|
4
|
+
|
|
5
|
+
module ScoutApm
|
|
6
|
+
class SynchronousRecorder
|
|
7
|
+
attr_reader :logger
|
|
8
|
+
|
|
9
|
+
def initialize(logger)
|
|
10
|
+
@logger = logger
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def start
|
|
14
|
+
# nothing to do
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def stop
|
|
19
|
+
# nothing to do
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def record!(request)
|
|
23
|
+
request.record!
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -42,6 +42,9 @@ module ScoutApm
|
|
|
42
42
|
# Whereas the instant_key gets set per-request in reponse to a URL param, dev_trace is set in the config file
|
|
43
43
|
attr_accessor :dev_trace
|
|
44
44
|
|
|
45
|
+
# An object that responds to `record!(TrackedRequest)` to store this tracked request
|
|
46
|
+
attr_reader :recorder
|
|
47
|
+
|
|
45
48
|
def initialize(store)
|
|
46
49
|
@store = store #this is passed in so we can use a real store (normal operation) or fake store (instant mode only)
|
|
47
50
|
@layers = []
|
|
@@ -51,12 +54,19 @@ module ScoutApm
|
|
|
51
54
|
@context = Context.new
|
|
52
55
|
@root_layer = nil
|
|
53
56
|
@error = false
|
|
57
|
+
@stopping = false
|
|
54
58
|
@instant_key = nil
|
|
55
59
|
@mem_start = mem_usage
|
|
56
60
|
@dev_trace = ScoutApm::Agent.instance.config.value('dev_trace') && ScoutApm::Agent.instance.environment.env == "development"
|
|
61
|
+
@recorder = ScoutApm::Agent.instance.recorder
|
|
62
|
+
|
|
63
|
+
ignore_request! if @recorder.nil?
|
|
57
64
|
end
|
|
58
65
|
|
|
59
66
|
def start_layer(layer)
|
|
67
|
+
# If we're already stopping, don't do additional layers
|
|
68
|
+
return if stopping?
|
|
69
|
+
|
|
60
70
|
return if ignoring_children?
|
|
61
71
|
|
|
62
72
|
return ignoring_start_layer if ignoring_request?
|
|
@@ -66,6 +76,9 @@ module ScoutApm
|
|
|
66
76
|
end
|
|
67
77
|
|
|
68
78
|
def stop_layer
|
|
79
|
+
# If we're already stopping, don't do additional layers
|
|
80
|
+
return if stopping?
|
|
81
|
+
|
|
69
82
|
return if ignoring_children?
|
|
70
83
|
|
|
71
84
|
return ignoring_stop_layer if ignoring_request?
|
|
@@ -77,7 +90,7 @@ module ScoutApm
|
|
|
77
90
|
# lined up correctly. If stop_layer gets called twice, when it should
|
|
78
91
|
# only have been called once you'll end up with this error.
|
|
79
92
|
if layer.nil?
|
|
80
|
-
|
|
93
|
+
logger.warn("Error stopping layer, was nil. Root Layer: #{@root_layer.inspect}")
|
|
81
94
|
stop_request
|
|
82
95
|
return
|
|
83
96
|
end
|
|
@@ -169,7 +182,15 @@ module ScoutApm
|
|
|
169
182
|
#
|
|
170
183
|
# * Send the request off to be stored
|
|
171
184
|
def stop_request
|
|
172
|
-
|
|
185
|
+
@stopping = true
|
|
186
|
+
|
|
187
|
+
if recorder
|
|
188
|
+
recorder.record!(self)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def stopping?
|
|
193
|
+
@stopping
|
|
173
194
|
end
|
|
174
195
|
|
|
175
196
|
###################################
|
|
@@ -224,13 +245,21 @@ module ScoutApm
|
|
|
224
245
|
# Persist the Request
|
|
225
246
|
###################################
|
|
226
247
|
|
|
248
|
+
def recorded!
|
|
249
|
+
@recorded = true
|
|
250
|
+
end
|
|
251
|
+
|
|
227
252
|
# Convert this request to the appropriate structure, then report it into
|
|
228
253
|
# the peristent Store object
|
|
229
254
|
def record!
|
|
230
|
-
|
|
255
|
+
recorded!
|
|
231
256
|
|
|
232
257
|
return if ignoring_request?
|
|
233
258
|
|
|
259
|
+
# If we didn't have store, but we're trying to record anyway, go
|
|
260
|
+
# figure that out. (this happens in Remote Agent scenarios)
|
|
261
|
+
restore_store if @store.nil?
|
|
262
|
+
|
|
234
263
|
# Bail out early if the user asked us to ignore this uri
|
|
235
264
|
return if ScoutApm::Agent.instance.ignored_uris.ignore?(annotations[:uri])
|
|
236
265
|
|
|
@@ -275,7 +304,6 @@ module ScoutApm
|
|
|
275
304
|
|
|
276
305
|
allocation_metrics = LayerConverters::AllocationMetricConverter.new(self).call
|
|
277
306
|
@store.track!(allocation_metrics)
|
|
278
|
-
|
|
279
307
|
end
|
|
280
308
|
|
|
281
309
|
# Only call this after the request is complete
|
|
@@ -389,5 +417,24 @@ module ScoutApm
|
|
|
389
417
|
def ignoring_recorded?
|
|
390
418
|
@ignoring_depth <= 0
|
|
391
419
|
end
|
|
420
|
+
|
|
421
|
+
def logger
|
|
422
|
+
ScoutApm::Agent.instance.logger
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Actually go fetch & make-real any lazily created data.
|
|
426
|
+
# Clean up any cleverness in objects.
|
|
427
|
+
# Makes this object ready to be Marshal Dumped (or otherwise serialized)
|
|
428
|
+
def prepare_to_dump!
|
|
429
|
+
@call_set = nil
|
|
430
|
+
@store = nil
|
|
431
|
+
@recorder = nil
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
# Go re-fetch the store based on what the Agent's official one is. Used
|
|
435
|
+
# after hydrating a dumped TrackedRequest
|
|
436
|
+
def restore_store
|
|
437
|
+
@store = ScoutApm::Agent.instance.store
|
|
438
|
+
end
|
|
392
439
|
end
|
|
393
440
|
end
|
data/lib/scout_apm/version.rb
CHANGED
data/lib/scout_apm.rb
CHANGED
|
@@ -14,6 +14,7 @@ require 'socket'
|
|
|
14
14
|
require 'thread'
|
|
15
15
|
require 'time'
|
|
16
16
|
require 'yaml'
|
|
17
|
+
require 'webrick'
|
|
17
18
|
|
|
18
19
|
#####################################
|
|
19
20
|
# Gem Requires
|
|
@@ -53,6 +54,7 @@ require 'scout_apm/server_integrations/null'
|
|
|
53
54
|
|
|
54
55
|
require 'scout_apm/background_job_integrations/sidekiq'
|
|
55
56
|
require 'scout_apm/background_job_integrations/delayed_job'
|
|
57
|
+
require 'scout_apm/background_job_integrations/resque'
|
|
56
58
|
|
|
57
59
|
require 'scout_apm/framework_integrations/rails_2'
|
|
58
60
|
require 'scout_apm/framework_integrations/rails_3_or_4'
|
|
@@ -118,6 +120,8 @@ require 'scout_apm/fake_store'
|
|
|
118
120
|
require 'scout_apm/tracer'
|
|
119
121
|
require 'scout_apm/context'
|
|
120
122
|
require 'scout_apm/instant_reporting'
|
|
123
|
+
require 'scout_apm/background_recorder'
|
|
124
|
+
require 'scout_apm/synchronous_recorder'
|
|
121
125
|
|
|
122
126
|
require 'scout_apm/metric_meta'
|
|
123
127
|
require 'scout_apm/metric_stats'
|
|
@@ -147,6 +151,12 @@ require 'scout_apm/instant/middleware'
|
|
|
147
151
|
|
|
148
152
|
require 'scout_apm/rack'
|
|
149
153
|
|
|
154
|
+
require 'scout_apm/remote/server'
|
|
155
|
+
require 'scout_apm/remote/router'
|
|
156
|
+
require 'scout_apm/remote/message'
|
|
157
|
+
require 'scout_apm/remote/recorder'
|
|
158
|
+
require 'scout_apm/instruments/resque'
|
|
159
|
+
|
|
150
160
|
if defined?(Rails) && defined?(Rails::VERSION) && defined?(Rails::VERSION::MAJOR) && Rails::VERSION::MAJOR >= 3 && defined?(Rails::Railtie)
|
|
151
161
|
module ScoutApm
|
|
152
162
|
class Railtie < Rails::Railtie
|
data/scout_apm.gemspec
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class MessageTest < Minitest::Test
|
|
4
|
+
def test_message_encode_decode_roundtrip
|
|
5
|
+
message = ScoutApm::Remote::Message.new('type', 'command', ['arg'])
|
|
6
|
+
encoded = message.encode
|
|
7
|
+
decoded = ScoutApm::Remote::Message.decode(encoded)
|
|
8
|
+
assert_equal message.type, decoded.type
|
|
9
|
+
assert_equal message.command, decoded.command
|
|
10
|
+
assert_equal message.args, decoded.args
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class RouterTest < Minitest::Test
|
|
4
|
+
def test_router_handles_record
|
|
5
|
+
recorder = stub
|
|
6
|
+
router = ScoutApm::Remote::Router.new(recorder, logger)
|
|
7
|
+
message = ScoutApm::Remote::Message.new("record", "foo", 1, 2).encode
|
|
8
|
+
|
|
9
|
+
recorder.expects(:foo).with(1, 2)
|
|
10
|
+
|
|
11
|
+
router.handle(message)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_router_raises_on_unknown_types
|
|
15
|
+
recorder = stub
|
|
16
|
+
router = ScoutApm::Remote::Router.new(recorder, logger)
|
|
17
|
+
message = ScoutApm::Remote::Message.new("something_else", "foo", 1, 2).encode
|
|
18
|
+
|
|
19
|
+
recorder.expects(:foo).never
|
|
20
|
+
assert_raises do
|
|
21
|
+
router.handle(message)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def logger
|
|
26
|
+
@logger ||= Logger.new(logger_io)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def logger_io
|
|
30
|
+
@logger_io ||= StringIO.new
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class TestRemoteServer < Minitest::Test
|
|
4
|
+
def test_start_and_bind
|
|
5
|
+
bind = "127.0.0.1"
|
|
6
|
+
port = 8938
|
|
7
|
+
router = stub(:router)
|
|
8
|
+
logger_io = StringIO.new
|
|
9
|
+
server = ScoutApm::Remote::Server.new(bind, port, router, Logger.new(logger_io))
|
|
10
|
+
|
|
11
|
+
server.start
|
|
12
|
+
sleep 0.01 # Let the server finish starting. The assert should instead allow a time
|
|
13
|
+
assert server.running?
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class TrackedRequestDumpAndLoadTest < Minitest::Test
|
|
4
|
+
# TrackedRequest must be marshalable
|
|
5
|
+
def test_marshal_dump_load
|
|
6
|
+
tr = ScoutApm::TrackedRequest.new(ScoutApm::FakeStore.new)
|
|
7
|
+
tr.prepare_to_dump!
|
|
8
|
+
|
|
9
|
+
dumped = Marshal.dump(tr)
|
|
10
|
+
loaded = Marshal.load(dumped)
|
|
11
|
+
assert_false loaded.nil?
|
|
12
|
+
end
|
|
13
|
+
|
|
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")
|
|
18
|
+
|
|
19
|
+
tr.prepare_to_dump!
|
|
20
|
+
assert_nil tr.instance_variable_get("@store")
|
|
21
|
+
|
|
22
|
+
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?
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class TrackedRequestLayerManipulationTest < Minitest::Test
|
|
65
|
+
def test_start_layer
|
|
66
|
+
tr = ScoutApm::TrackedRequest.new(ScoutApm::FakeStore.new)
|
|
67
|
+
tr.start_layer(ScoutApm::Layer.new("Foo", "Bar"))
|
|
68
|
+
|
|
69
|
+
assert_equal "Foo", tr.current_layer.type
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_start_several_layers
|
|
73
|
+
# layers are Controller -> ActiveRecord
|
|
74
|
+
controller_layer = ScoutApm::Layer.new("Controller", "users/index")
|
|
75
|
+
ar_layer = ScoutApm::Layer.new("ActiveRecord", "Users#find")
|
|
76
|
+
|
|
77
|
+
tr = ScoutApm::TrackedRequest.new(ScoutApm::FakeStore.new)
|
|
78
|
+
tr.start_layer(controller_layer)
|
|
79
|
+
tr.start_layer(ar_layer)
|
|
80
|
+
|
|
81
|
+
assert_equal "ActiveRecord", tr.current_layer.type
|
|
82
|
+
|
|
83
|
+
tr.stop_layer
|
|
84
|
+
|
|
85
|
+
assert_equal "Controller", tr.current_layer.type
|
|
86
|
+
end
|
|
87
|
+
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.1.
|
|
4
|
+
version: 2.1.30
|
|
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-08-
|
|
12
|
+
date: 2017-08-15 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: minitest
|
|
@@ -109,6 +109,34 @@ dependencies:
|
|
|
109
109
|
- - ">="
|
|
110
110
|
- !ruby/object:Gem::Version
|
|
111
111
|
version: '0'
|
|
112
|
+
- !ruby/object:Gem::Dependency
|
|
113
|
+
name: guard
|
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - ">="
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '0'
|
|
119
|
+
type: :development
|
|
120
|
+
prerelease: false
|
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - ">="
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '0'
|
|
126
|
+
- !ruby/object:Gem::Dependency
|
|
127
|
+
name: guard-minitest
|
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '0'
|
|
133
|
+
type: :development
|
|
134
|
+
prerelease: false
|
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
136
|
+
requirements:
|
|
137
|
+
- - ">="
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: '0'
|
|
112
140
|
description: Monitors Ruby apps and reports detailed metrics on performance to Scout.
|
|
113
141
|
email:
|
|
114
142
|
- support@scoutapp.com
|
|
@@ -122,6 +150,7 @@ files:
|
|
|
122
150
|
- ".rubocop.yml"
|
|
123
151
|
- CHANGELOG.markdown
|
|
124
152
|
- Gemfile
|
|
153
|
+
- Guardfile
|
|
125
154
|
- LICENSE.md
|
|
126
155
|
- README.markdown
|
|
127
156
|
- Rakefile
|
|
@@ -138,7 +167,9 @@ files:
|
|
|
138
167
|
- lib/scout_apm/app_server_load.rb
|
|
139
168
|
- lib/scout_apm/attribute_arranger.rb
|
|
140
169
|
- lib/scout_apm/background_job_integrations/delayed_job.rb
|
|
170
|
+
- lib/scout_apm/background_job_integrations/resque.rb
|
|
141
171
|
- lib/scout_apm/background_job_integrations/sidekiq.rb
|
|
172
|
+
- lib/scout_apm/background_recorder.rb
|
|
142
173
|
- lib/scout_apm/background_worker.rb
|
|
143
174
|
- lib/scout_apm/bucket_name_splitter.rb
|
|
144
175
|
- lib/scout_apm/call_set.rb
|
|
@@ -176,6 +207,7 @@ files:
|
|
|
176
207
|
- lib/scout_apm/instruments/process/process_memory.rb
|
|
177
208
|
- lib/scout_apm/instruments/rails_router.rb
|
|
178
209
|
- lib/scout_apm/instruments/redis.rb
|
|
210
|
+
- lib/scout_apm/instruments/resque.rb
|
|
179
211
|
- lib/scout_apm/instruments/sinatra.rb
|
|
180
212
|
- lib/scout_apm/job_record.rb
|
|
181
213
|
- lib/scout_apm/layaway.rb
|
|
@@ -200,6 +232,10 @@ files:
|
|
|
200
232
|
- lib/scout_apm/platform_integrations/heroku.rb
|
|
201
233
|
- lib/scout_apm/platform_integrations/server.rb
|
|
202
234
|
- lib/scout_apm/rack.rb
|
|
235
|
+
- lib/scout_apm/remote/message.rb
|
|
236
|
+
- lib/scout_apm/remote/recorder.rb
|
|
237
|
+
- lib/scout_apm/remote/router.rb
|
|
238
|
+
- lib/scout_apm/remote/server.rb
|
|
203
239
|
- lib/scout_apm/reporter.rb
|
|
204
240
|
- lib/scout_apm/request_histograms.rb
|
|
205
241
|
- lib/scout_apm/request_manager.rb
|
|
@@ -225,6 +261,7 @@ files:
|
|
|
225
261
|
- lib/scout_apm/slow_transaction.rb
|
|
226
262
|
- lib/scout_apm/stack_item.rb
|
|
227
263
|
- lib/scout_apm/store.rb
|
|
264
|
+
- lib/scout_apm/synchronous_recorder.rb
|
|
228
265
|
- lib/scout_apm/tracer.rb
|
|
229
266
|
- lib/scout_apm/tracked_request.rb
|
|
230
267
|
- lib/scout_apm/utils/active_record_metric_name.rb
|
|
@@ -259,12 +296,16 @@ files:
|
|
|
259
296
|
- test/unit/layer_children_set_test.rb
|
|
260
297
|
- test/unit/limited_layer_test.rb
|
|
261
298
|
- test/unit/metric_set_test.rb
|
|
299
|
+
- test/unit/remote/test_message.rb
|
|
300
|
+
- test/unit/remote/test_router.rb
|
|
301
|
+
- test/unit/remote/test_server.rb
|
|
262
302
|
- test/unit/scored_item_set_test.rb
|
|
263
303
|
- test/unit/serializers/payload_serializer_test.rb
|
|
264
304
|
- test/unit/slow_job_policy_test.rb
|
|
265
305
|
- test/unit/slow_request_policy_test.rb
|
|
266
306
|
- test/unit/sql_sanitizer_test.rb
|
|
267
307
|
- test/unit/store_test.rb
|
|
308
|
+
- test/unit/test_tracked_request.rb
|
|
268
309
|
- test/unit/utils/active_record_metric_name_test.rb
|
|
269
310
|
- test/unit/utils/backtrace_parser_test.rb
|
|
270
311
|
- test/unit/utils/numbers_test.rb
|
|
@@ -290,8 +331,39 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
290
331
|
version: '0'
|
|
291
332
|
requirements: []
|
|
292
333
|
rubyforge_project: scout_apm
|
|
293
|
-
rubygems_version: 2.
|
|
334
|
+
rubygems_version: 2.4.5.2
|
|
294
335
|
signing_key:
|
|
295
336
|
specification_version: 4
|
|
296
337
|
summary: Ruby application performance monitoring
|
|
297
|
-
test_files:
|
|
338
|
+
test_files:
|
|
339
|
+
- test/data/config_test_1.yml
|
|
340
|
+
- test/test_helper.rb
|
|
341
|
+
- test/unit/agent_test.rb
|
|
342
|
+
- test/unit/background_job_integrations/sidekiq_test.rb
|
|
343
|
+
- test/unit/config_test.rb
|
|
344
|
+
- test/unit/context_test.rb
|
|
345
|
+
- test/unit/environment_test.rb
|
|
346
|
+
- test/unit/git_revision_test.rb
|
|
347
|
+
- test/unit/histogram_test.rb
|
|
348
|
+
- test/unit/ignored_uris_test.rb
|
|
349
|
+
- test/unit/instruments/active_record_instruments_test.rb
|
|
350
|
+
- test/unit/instruments/net_http_test.rb
|
|
351
|
+
- test/unit/instruments/percentile_sampler_test.rb
|
|
352
|
+
- test/unit/layaway_test.rb
|
|
353
|
+
- test/unit/layer_children_set_test.rb
|
|
354
|
+
- test/unit/limited_layer_test.rb
|
|
355
|
+
- test/unit/metric_set_test.rb
|
|
356
|
+
- test/unit/remote/test_message.rb
|
|
357
|
+
- test/unit/remote/test_router.rb
|
|
358
|
+
- test/unit/remote/test_server.rb
|
|
359
|
+
- test/unit/scored_item_set_test.rb
|
|
360
|
+
- test/unit/serializers/payload_serializer_test.rb
|
|
361
|
+
- test/unit/slow_job_policy_test.rb
|
|
362
|
+
- test/unit/slow_request_policy_test.rb
|
|
363
|
+
- test/unit/sql_sanitizer_test.rb
|
|
364
|
+
- test/unit/store_test.rb
|
|
365
|
+
- test/unit/test_tracked_request.rb
|
|
366
|
+
- test/unit/utils/active_record_metric_name_test.rb
|
|
367
|
+
- test/unit/utils/backtrace_parser_test.rb
|
|
368
|
+
- test/unit/utils/numbers_test.rb
|
|
369
|
+
- test/unit/utils/scm.rb
|