scout_apm 2.2.0.pre3 → 2.3.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/CHANGELOG.markdown +147 -2
- data/Guardfile +43 -0
- data/Rakefile +2 -2
- data/ext/allocations/allocations.c +6 -0
- data/ext/allocations/extconf.rb +1 -0
- data/ext/rusage/README.md +26 -0
- data/ext/rusage/extconf.rb +5 -0
- data/ext/rusage/rusage.c +52 -0
- data/lib/scout_apm.rb +28 -15
- data/lib/scout_apm/agent.rb +89 -37
- data/lib/scout_apm/agent/logging.rb +6 -1
- data/lib/scout_apm/agent/reporting.rb +9 -6
- data/lib/scout_apm/app_server_load.rb +21 -10
- data/lib/scout_apm/attribute_arranger.rb +6 -3
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +71 -1
- data/lib/scout_apm/background_job_integrations/resque.rb +85 -0
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +22 -20
- data/lib/scout_apm/background_recorder.rb +43 -0
- data/lib/scout_apm/background_worker.rb +19 -15
- data/lib/scout_apm/config.rb +138 -28
- data/lib/scout_apm/db_query_metric_set.rb +80 -0
- data/lib/scout_apm/db_query_metric_stats.rb +102 -0
- data/lib/scout_apm/debug.rb +37 -0
- data/lib/scout_apm/environment.rb +22 -15
- data/lib/scout_apm/git_revision.rb +51 -0
- data/lib/scout_apm/histogram.rb +11 -2
- data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
- data/lib/scout_apm/instant/middleware.rb +196 -54
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +89 -68
- data/lib/scout_apm/instruments/action_view.rb +49 -0
- data/lib/scout_apm/instruments/active_record.rb +127 -3
- data/lib/scout_apm/instruments/grape.rb +4 -3
- data/lib/scout_apm/instruments/middleware_detailed.rb +4 -6
- data/lib/scout_apm/instruments/mongoid.rb +24 -3
- data/lib/scout_apm/instruments/net_http.rb +7 -2
- data/lib/scout_apm/instruments/percentile_sampler.rb +36 -19
- data/lib/scout_apm/instruments/process/process_cpu.rb +3 -2
- data/lib/scout_apm/instruments/process/process_memory.rb +3 -3
- data/lib/scout_apm/instruments/resque.rb +40 -0
- data/lib/scout_apm/layaway.rb +67 -28
- data/lib/scout_apm/layer.rb +19 -59
- data/lib/scout_apm/layer_children_set.rb +77 -0
- data/lib/scout_apm/layer_converters/allocation_metric_converter.rb +5 -6
- data/lib/scout_apm/layer_converters/converter_base.rb +201 -14
- data/lib/scout_apm/layer_converters/database_converter.rb +55 -0
- data/lib/scout_apm/layer_converters/depth_first_walker.rb +22 -10
- data/lib/scout_apm/layer_converters/error_converter.rb +5 -7
- data/lib/scout_apm/layer_converters/find_layer_by_type.rb +34 -0
- data/lib/scout_apm/layer_converters/histograms.rb +14 -0
- data/lib/scout_apm/layer_converters/job_converter.rb +36 -50
- data/lib/scout_apm/layer_converters/metric_converter.rb +17 -19
- data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +10 -12
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +41 -115
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +33 -117
- data/lib/scout_apm/limited_layer.rb +126 -0
- data/lib/scout_apm/metric_meta.rb +0 -5
- data/lib/scout_apm/metric_set.rb +9 -1
- data/lib/scout_apm/metric_stats.rb +7 -8
- data/lib/scout_apm/rack.rb +26 -0
- 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/reporter.rb +51 -15
- data/lib/scout_apm/request_histograms.rb +4 -0
- data/lib/scout_apm/request_manager.rb +2 -1
- data/lib/scout_apm/scored_item_set.rb +7 -0
- data/lib/scout_apm/serializers/db_query_serializer_to_json.rb +15 -0
- data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +21 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +10 -3
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +6 -6
- data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +2 -1
- data/lib/scout_apm/server_integrations/puma.rb +5 -2
- data/lib/scout_apm/slow_job_policy.rb +1 -10
- data/lib/scout_apm/slow_job_record.rb +6 -1
- data/lib/scout_apm/slow_request_policy.rb +1 -10
- data/lib/scout_apm/slow_transaction.rb +20 -2
- data/lib/scout_apm/store.rb +66 -12
- data/lib/scout_apm/synchronous_recorder.rb +26 -0
- data/lib/scout_apm/tracked_request.rb +136 -71
- data/lib/scout_apm/utils/active_record_metric_name.rb +8 -4
- data/lib/scout_apm/utils/backtrace_parser.rb +3 -3
- data/lib/scout_apm/utils/gzip_helper.rb +24 -0
- data/lib/scout_apm/utils/numbers.rb +14 -0
- data/lib/scout_apm/utils/scm.rb +14 -0
- data/lib/scout_apm/version.rb +1 -1
- data/scout_apm.gemspec +5 -4
- data/test/test_helper.rb +18 -0
- data/test/unit/config_test.rb +59 -8
- data/test/unit/db_query_metric_set_test.rb +56 -0
- data/test/unit/db_query_metric_stats_test.rb +113 -0
- data/test/unit/git_revision_test.rb +15 -0
- data/test/unit/histogram_test.rb +14 -0
- data/test/unit/instruments/net_http_test.rb +21 -0
- data/test/unit/instruments/percentile_sampler_test.rb +137 -0
- data/test/unit/layaway_test.rb +20 -0
- data/test/unit/layer_children_set_test.rb +88 -0
- data/test/unit/layer_converters/depth_first_walker_test.rb +66 -0
- data/test/unit/layer_converters/metric_converter_test.rb +22 -0
- data/test/unit/layer_converters/stubs.rb +33 -0
- data/test/unit/limited_layer_test.rb +53 -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/serializers/payload_serializer_test.rb +3 -12
- data/test/unit/store_test.rb +66 -0
- data/test/unit/test_tracked_request.rb +87 -0
- data/test/unit/utils/active_record_metric_name_test.rb +8 -0
- data/test/unit/utils/backtrace_parser_test.rb +5 -0
- data/test/unit/utils/numbers_test.rb +15 -0
- data/test/unit/utils/scm.rb +17 -0
- metadata +125 -30
- data/ext/stacks/extconf.rb +0 -37
- data/ext/stacks/scout_atomics.h +0 -86
- data/ext/stacks/stacks.c +0 -811
- data/lib/scout_apm/capacity.rb +0 -57
- data/lib/scout_apm/deploy_integrations/capistrano_2.cap +0 -12
- data/lib/scout_apm/deploy_integrations/capistrano_2.rb +0 -83
- data/lib/scout_apm/deploy_integrations/capistrano_3.cap +0 -12
- data/lib/scout_apm/deploy_integrations/capistrano_3.rb +0 -88
- data/lib/scout_apm/instruments/delayed_job.rb +0 -57
- data/lib/scout_apm/serializers/deploy_serializer.rb +0 -16
- data/lib/scout_apm/trace_compactor.rb +0 -312
- data/lib/scout_apm/utils/fake_stacks.rb +0 -87
- data/tester.rb +0 -53
@@ -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
|
+
|
@@ -55,27 +55,15 @@ module ScoutApm
|
|
55
55
|
# We insert this middleware into the Sidekiq stack, to capture each job,
|
56
56
|
# and time them.
|
57
57
|
class SidekiqMiddleware
|
58
|
-
ACTIVE_JOB_KLASS = 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper'.freeze
|
59
|
-
|
60
58
|
def call(_worker, msg, queue)
|
61
59
|
req = ScoutApm::RequestManager.lookup
|
62
60
|
req.job!
|
63
61
|
req.annotate_request(:queue_latency => latency(msg))
|
64
62
|
|
65
|
-
queue_layer = ScoutApm::Layer.new('Queue', queue)
|
66
|
-
job_layer = ScoutApm::Layer.new('Job', job_class(msg))
|
67
|
-
|
68
|
-
if ScoutApm::Agent.instance.config.value('profile') && SidekiqMiddleware.version_supports_profiling?
|
69
|
-
# Capture ScoutProf if we can
|
70
|
-
#req.enable_profiled_thread!
|
71
|
-
#job_layer.set_root_class(job_class)
|
72
|
-
#job_layer.traced!
|
73
|
-
end
|
74
|
-
|
75
63
|
begin
|
76
|
-
req.start_layer(
|
64
|
+
req.start_layer(ScoutApm::Layer.new('Queue', queue))
|
77
65
|
started_queue = true
|
78
|
-
req.start_layer(
|
66
|
+
req.start_layer(ScoutApm::Layer.new('Job', job_class(msg)))
|
79
67
|
started_job = true
|
80
68
|
|
81
69
|
yield
|
@@ -89,12 +77,30 @@ module ScoutApm
|
|
89
77
|
end
|
90
78
|
|
91
79
|
UNKNOWN_CLASS_PLACEHOLDER = 'UnknownJob'.freeze
|
80
|
+
ACTIVE_JOB_KLASS = 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper'.freeze
|
81
|
+
DELAYED_WRAPPER_KLASS = 'Sidekiq::Extensions::DelayedClass'.freeze
|
82
|
+
|
92
83
|
|
93
84
|
def job_class(msg)
|
94
85
|
job_class = msg.fetch('class', UNKNOWN_CLASS_PLACEHOLDER)
|
86
|
+
|
95
87
|
if job_class == ACTIVE_JOB_KLASS && msg.key?('wrapped')
|
96
|
-
|
88
|
+
begin
|
89
|
+
job_class = msg['wrapped']
|
90
|
+
rescue
|
91
|
+
ACTIVE_JOB_KLASS
|
92
|
+
end
|
93
|
+
elsif job_class == DELAYED_WRAPPER_KLASS
|
94
|
+
begin
|
95
|
+
yml = msg['args'].first
|
96
|
+
deserialized_args = YAML.load(yml)
|
97
|
+
klass, method, *rest = deserialized_args
|
98
|
+
job_class = [klass,method].map(&:to_s).join(".")
|
99
|
+
rescue
|
100
|
+
DELAYED_WRAPPER_KLASS
|
101
|
+
end
|
97
102
|
end
|
103
|
+
|
98
104
|
job_class
|
99
105
|
rescue
|
100
106
|
UNKNOWN_CLASS_PLACEHOLDER
|
@@ -110,10 +116,6 @@ module ScoutApm
|
|
110
116
|
rescue
|
111
117
|
0
|
112
118
|
end
|
113
|
-
|
114
|
-
def self.version_supports_profiling?
|
115
|
-
@@sidekiq_supports_profling ||= defined?(::Sidekiq::VERSION) && Gem::Dependency.new('', '~> 4.0').match?('', ::Sidekiq::VERSION.to_s)
|
116
|
-
end
|
117
|
-
end # SidekiqMiddleware
|
119
|
+
end
|
118
120
|
end
|
119
121
|
end
|
@@ -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
|
@@ -11,6 +11,10 @@ module ScoutApm
|
|
11
11
|
@keep_running = true
|
12
12
|
end
|
13
13
|
|
14
|
+
def running?
|
15
|
+
@keep_running
|
16
|
+
end
|
17
|
+
|
14
18
|
def stop
|
15
19
|
ScoutApm::Agent.instance.logger.debug "Background Worker: stop requested"
|
16
20
|
@keep_running = false
|
@@ -25,19 +29,13 @@ module ScoutApm
|
|
25
29
|
def start(&block)
|
26
30
|
@task = block
|
27
31
|
|
28
|
-
|
29
|
-
ScoutApm::Agent.instance.logger.debug "Background Worker: Starting Background Worker, running every #{period} seconds"
|
32
|
+
ScoutApm::Agent.instance.logger.debug "Background Worker: Starting Background Worker, running every #{period} seconds"
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
loop do
|
35
|
-
# Bail out if @keep_running is false
|
36
|
-
unless @keep_running
|
37
|
-
ScoutApm::Agent.instance.logger.debug "Background Worker: breaking from loop"
|
38
|
-
break
|
39
|
-
end
|
34
|
+
# The first run should be 1 period of time from now
|
35
|
+
next_time = Time.now + period
|
40
36
|
|
37
|
+
loop do
|
38
|
+
begin
|
41
39
|
now = Time.now
|
42
40
|
|
43
41
|
# Sleep the correct amount of time to reach next_time
|
@@ -47,17 +45,23 @@ module ScoutApm
|
|
47
45
|
now = Time.now
|
48
46
|
end
|
49
47
|
|
48
|
+
# Bail out if @keep_running is false
|
49
|
+
unless @keep_running
|
50
|
+
ScoutApm::Agent.instance.logger.debug "Background Worker: breaking from loop"
|
51
|
+
break
|
52
|
+
end
|
53
|
+
|
50
54
|
@task.call
|
51
55
|
|
52
56
|
# Adjust the next time to run forward by @periods until it is in the future
|
53
57
|
while next_time <= now
|
54
58
|
next_time += period
|
55
59
|
end
|
60
|
+
rescue
|
61
|
+
ScoutApm::Agent.instance.logger.debug "Background Worker Exception!"
|
62
|
+
ScoutApm::Agent.instance.logger.debug $!.message
|
63
|
+
ScoutApm::Agent.instance.logger.debug $!.backtrace
|
56
64
|
end
|
57
|
-
rescue
|
58
|
-
ScoutApm::Agent.instance.logger.debug "Background Worker Exception!"
|
59
|
-
ScoutApm::Agent.instance.logger.debug $!.message
|
60
|
-
ScoutApm::Agent.instance.logger.debug $!.backtrace
|
61
65
|
end
|
62
66
|
end
|
63
67
|
end
|
data/lib/scout_apm/config.rb
CHANGED
@@ -5,30 +5,63 @@ require 'scout_apm/environment'
|
|
5
5
|
|
6
6
|
# Valid Config Options:
|
7
7
|
#
|
8
|
+
# This list is complete, but some are old and unused, or for developers of
|
9
|
+
# scout_apm itself. See the documentation at http://help.apm.scoutapp.com for
|
10
|
+
# customer-focused documentation.
|
11
|
+
#
|
8
12
|
# application_root - override the detected directory of the application
|
13
|
+
# compress_payload - true/false to enable gzipping of payload
|
9
14
|
# data_file - override the default temporary storage location. Must be a location in a writable directory
|
10
|
-
#
|
15
|
+
# dev_trace - true or false. Enables always-on tracing in development environmen only
|
11
16
|
# direct_host - override the default "direct" host. The direct_host bypasses the ingestion pipeline and goes directly to the webserver, and is primarily used for features under development.
|
17
|
+
# enable_background_jobs - true or false
|
18
|
+
# host - configuration used in development
|
19
|
+
# hostname - override the default hostname detection. Default varies by environment - either system hostname, or PAAS hostname
|
12
20
|
# key - the account key with Scout APM. Found in Settings in the Web UI
|
13
21
|
# log_file_path - either a directory or "STDOUT".
|
14
22
|
# log_level - DEBUG / INFO / WARN as usual
|
15
23
|
# monitor - true or false. False prevents any instrumentation from starting
|
16
24
|
# name - override the name reported to APM. This is the name that shows in the Web UI
|
17
|
-
#
|
25
|
+
# profile - turn on/off scoutprof (only applicable in Gem versions including scoutprof)
|
26
|
+
# proxy - an http proxy
|
18
27
|
# report_format - 'json' or 'marshal'. Marshal is legacy and will be removed.
|
19
|
-
#
|
20
|
-
#
|
28
|
+
# scm_subdirectory - if the app root lives in source management in a subdirectory. E.g. #{SCM_ROOT}/src
|
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
|
21
32
|
#
|
22
33
|
# Any of these config settings can be set with an environment variable prefixed
|
23
34
|
# by SCOUT_ and uppercasing the key: SCOUT_LOG_LEVEL for instance.
|
24
35
|
|
25
|
-
|
26
|
-
# Config - Made up of config overlay
|
27
|
-
# Default -> File -> Environment Var
|
28
|
-
# QUESTION: How to embed arrays or hashes into ENV?
|
29
|
-
|
30
36
|
module ScoutApm
|
31
37
|
class Config
|
38
|
+
KNOWN_CONFIG_OPTIONS = [
|
39
|
+
'application_root',
|
40
|
+
'async_recording',
|
41
|
+
'compress_payload',
|
42
|
+
'config_file',
|
43
|
+
'data_file',
|
44
|
+
'detailed_middleware',
|
45
|
+
'dev_trace',
|
46
|
+
'direct_host',
|
47
|
+
'disabled_instruments',
|
48
|
+
'enable_background_jobs',
|
49
|
+
'host',
|
50
|
+
'hostname',
|
51
|
+
'ignore',
|
52
|
+
'key',
|
53
|
+
'log_file_path',
|
54
|
+
'log_level',
|
55
|
+
'monitor',
|
56
|
+
'name',
|
57
|
+
'profile',
|
58
|
+
'proxy',
|
59
|
+
'remote_agent_host',
|
60
|
+
'remote_agent_port',
|
61
|
+
'report_format',
|
62
|
+
'scm_subdirectory',
|
63
|
+
'uri_reporting',
|
64
|
+
]
|
32
65
|
|
33
66
|
################################################################################
|
34
67
|
# Coersions
|
@@ -92,6 +125,12 @@ module ScoutApm
|
|
92
125
|
end
|
93
126
|
end
|
94
127
|
|
128
|
+
class IntegerCoercion
|
129
|
+
def coerce(val)
|
130
|
+
val.to_i
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
95
134
|
# Simply returns the passed in value, without change
|
96
135
|
class NullCoercion
|
97
136
|
def coerce(val)
|
@@ -101,10 +140,14 @@ module ScoutApm
|
|
101
140
|
|
102
141
|
|
103
142
|
SETTING_COERCIONS = {
|
104
|
-
"
|
105
|
-
"
|
143
|
+
"async_recording" => BooleanCoercion.new,
|
144
|
+
"detailed_middleware" => BooleanCoercion.new,
|
106
145
|
"dev_trace" => BooleanCoercion.new,
|
146
|
+
"enable_background_jobs" => BooleanCoercion.new,
|
107
147
|
"ignore" => JsonCoercion.new,
|
148
|
+
"monitor" => BooleanCoercion.new,
|
149
|
+
'database_metric_limit' => IntegerCoercion.new,
|
150
|
+
'database_metric_report_limit' => IntegerCoercion.new,
|
108
151
|
}
|
109
152
|
|
110
153
|
|
@@ -129,7 +172,7 @@ module ScoutApm
|
|
129
172
|
def self.with_file(file_path=nil, config={})
|
130
173
|
overlays = [
|
131
174
|
ConfigEnvironment.new,
|
132
|
-
ConfigFile.new(file_path, config
|
175
|
+
ConfigFile.new(file_path, config),
|
133
176
|
ConfigDefaults.new,
|
134
177
|
ConfigNull.new,
|
135
178
|
]
|
@@ -140,8 +183,17 @@ module ScoutApm
|
|
140
183
|
@overlays = Array(overlays)
|
141
184
|
end
|
142
185
|
|
186
|
+
# For a given key, what is the first overlay says that it can handle it?
|
187
|
+
def overlay_for_key(key)
|
188
|
+
@overlays.detect{ |overlay| overlay.has_key?(key) }
|
189
|
+
end
|
190
|
+
|
143
191
|
def value(key)
|
144
|
-
|
192
|
+
if ! KNOWN_CONFIG_OPTIONS.include?(key)
|
193
|
+
ScoutApm::Agent.instance.logger.debug("Requested looking up a unknown configuration key: #{key} (not a problem. Evaluate and add to config.rb)")
|
194
|
+
end
|
195
|
+
|
196
|
+
o = overlay_for_key(key)
|
145
197
|
raw_value = if o
|
146
198
|
o.value(key)
|
147
199
|
else
|
@@ -149,22 +201,42 @@ module ScoutApm
|
|
149
201
|
nil
|
150
202
|
end
|
151
203
|
|
152
|
-
coercion = SETTING_COERCIONS
|
204
|
+
coercion = SETTING_COERCIONS.fetch(key, NullCoercion.new)
|
153
205
|
coercion.coerce(raw_value)
|
154
206
|
end
|
155
207
|
|
208
|
+
# Did we load anything for configuration?
|
209
|
+
def any_keys_found?
|
210
|
+
@overlays.any? { |overlay| overlay.any_keys_found? }
|
211
|
+
end
|
212
|
+
|
213
|
+
def log_settings
|
214
|
+
messages = KNOWN_CONFIG_OPTIONS.inject([]) do |memo, key|
|
215
|
+
o = overlay_for_key(key)
|
216
|
+
memo << "#{o.name} - #{key}: #{value(key).inspect}"
|
217
|
+
end
|
218
|
+
ScoutApm::Agent.instance.logger.debug("Resolved Setting Values:\n" + messages.join("\n"))
|
219
|
+
end
|
220
|
+
|
156
221
|
class ConfigDefaults
|
157
222
|
DEFAULTS = {
|
158
|
-
'
|
223
|
+
'compress_payload' => true,
|
224
|
+
'detailed_middleware' => false,
|
225
|
+
'dev_trace' => false,
|
159
226
|
'direct_host' => 'https://apm.scoutapp.com',
|
160
|
-
'log_level' => 'info',
|
161
|
-
'uri_reporting' => 'full_path',
|
162
|
-
'report_format' => 'json',
|
163
227
|
'disabled_instruments' => [],
|
164
228
|
'enable_background_jobs' => true,
|
229
|
+
'host' => 'https://checkin.scoutapp.com',
|
165
230
|
'ignore' => [],
|
166
|
-
'
|
167
|
-
'profile'
|
231
|
+
'log_level' => 'info',
|
232
|
+
'profile' => true, # for scoutprof
|
233
|
+
'report_format' => 'json',
|
234
|
+
'scm_subdirectory' => '',
|
235
|
+
'uri_reporting' => 'full_path',
|
236
|
+
'remote_agent_host' => '127.0.0.1',
|
237
|
+
'remote_agent_port' => 7721, # picked at random
|
238
|
+
'database_metric_limit' => 5000, # The hard limit on db metrics
|
239
|
+
'database_metric_report_limit' => 1000,
|
168
240
|
}.freeze
|
169
241
|
|
170
242
|
def value(key)
|
@@ -174,6 +246,15 @@ module ScoutApm
|
|
174
246
|
def has_key?(key)
|
175
247
|
DEFAULTS.has_key?(key)
|
176
248
|
end
|
249
|
+
|
250
|
+
# Defaults are here, but not counted as user specified.
|
251
|
+
def any_keys_found?
|
252
|
+
false
|
253
|
+
end
|
254
|
+
|
255
|
+
def name
|
256
|
+
"defaults"
|
257
|
+
end
|
177
258
|
end
|
178
259
|
|
179
260
|
|
@@ -188,6 +269,14 @@ module ScoutApm
|
|
188
269
|
def has_key?(*)
|
189
270
|
true
|
190
271
|
end
|
272
|
+
|
273
|
+
def any_keys_found?
|
274
|
+
false
|
275
|
+
end
|
276
|
+
|
277
|
+
def name
|
278
|
+
"no-config"
|
279
|
+
end
|
191
280
|
end
|
192
281
|
|
193
282
|
class ConfigEnvironment
|
@@ -203,6 +292,16 @@ module ScoutApm
|
|
203
292
|
def key_to_env_key(key)
|
204
293
|
'SCOUT_' + key.upcase
|
205
294
|
end
|
295
|
+
|
296
|
+
def any_keys_found?
|
297
|
+
KNOWN_CONFIG_OPTIONS.any? { |option|
|
298
|
+
ENV.has_key?(key_to_env_key(option))
|
299
|
+
}
|
300
|
+
end
|
301
|
+
|
302
|
+
def name
|
303
|
+
"environment"
|
304
|
+
end
|
206
305
|
end
|
207
306
|
|
208
307
|
# Attempts to load a configuration file, and parse it as YAML. If the file
|
@@ -228,6 +327,16 @@ module ScoutApm
|
|
228
327
|
@settings.has_key?(key)
|
229
328
|
end
|
230
329
|
|
330
|
+
def any_keys_found?
|
331
|
+
KNOWN_CONFIG_OPTIONS.any? { |option|
|
332
|
+
@settings.has_key?(option)
|
333
|
+
}
|
334
|
+
end
|
335
|
+
|
336
|
+
def name
|
337
|
+
"config-file"
|
338
|
+
end
|
339
|
+
|
231
340
|
private
|
232
341
|
|
233
342
|
def load_file(file)
|
@@ -248,17 +357,18 @@ module ScoutApm
|
|
248
357
|
raw_file = File.read(@resolved_file_path)
|
249
358
|
erb_file = ERB.new(raw_file).result(binding)
|
250
359
|
parsed_yaml = YAML.load(erb_file)
|
251
|
-
|
360
|
+
file_settings = parsed_yaml[app_environment]
|
252
361
|
|
253
|
-
if
|
254
|
-
|
255
|
-
|
362
|
+
if file_settings.is_a? Hash
|
363
|
+
logger.debug("Loaded Configuration: #{@resolved_file_path}. Using environment: #{app_environment}")
|
364
|
+
@settings = file_settings
|
365
|
+
@file_loaded = true
|
366
|
+
else
|
367
|
+
logger.info("Couldn't find configuration in #{@resolved_file_path} for environment: #{app_environment}. Configuration in ENV will still be applied.")
|
368
|
+
@file_loaded = false
|
256
369
|
end
|
257
|
-
|
258
|
-
logger.debug("Loaded Configuration: #{@resolved_file_path}. Using environment: #{app_environment}")
|
259
|
-
@file_loaded = true
|
260
370
|
rescue Exception => e # Explicit `Exception` handling to catch SyntaxError and anything else that ERB or YAML may throw
|
261
|
-
logger.
|
371
|
+
logger.info("Failed loading configuration file (#{@resolved_file_path}): #{e.message}. ScoutAPM will continue starting with configuration from ENV and defaults")
|
262
372
|
@file_loaded = false
|
263
373
|
end
|
264
374
|
end
|