scout_apm 2.1.32 → 2.2.0.pre0
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 +2 -161
- data/Rakefile +2 -2
- data/ext/allocations/allocations.c +0 -6
- data/ext/allocations/extconf.rb +0 -1
- data/ext/stacks/extconf.rb +33 -0
- data/ext/stacks/scout_atomics.h +86 -0
- data/ext/stacks/stacks.c +744 -0
- data/lib/scout_apm.rb +16 -24
- data/lib/scout_apm/agent.rb +38 -93
- data/lib/scout_apm/agent/logging.rb +1 -6
- data/lib/scout_apm/agent/reporting.rb +6 -8
- data/lib/scout_apm/app_server_load.rb +10 -21
- data/lib/scout_apm/attribute_arranger.rb +2 -0
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +1 -71
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +27 -66
- data/lib/scout_apm/background_worker.rb +15 -19
- data/lib/scout_apm/capacity.rb +57 -0
- data/lib/scout_apm/config.rb +29 -135
- data/lib/scout_apm/context.rb +5 -9
- data/lib/scout_apm/deploy_integrations/capistrano_2.cap +12 -0
- data/lib/scout_apm/deploy_integrations/capistrano_2.rb +83 -0
- data/lib/scout_apm/deploy_integrations/capistrano_3.cap +12 -0
- data/lib/scout_apm/deploy_integrations/capistrano_3.rb +88 -0
- data/lib/scout_apm/environment.rb +15 -22
- data/lib/scout_apm/histogram.rb +2 -11
- data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
- data/lib/scout_apm/instant/middleware.rb +57 -198
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +2 -1
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +59 -90
- data/lib/scout_apm/instruments/active_record.rb +5 -7
- data/lib/scout_apm/instruments/delayed_job.rb +57 -0
- data/lib/scout_apm/instruments/grape.rb +3 -4
- data/lib/scout_apm/instruments/middleware_detailed.rb +6 -4
- data/lib/scout_apm/instruments/middleware_summary.rb +1 -39
- data/lib/scout_apm/instruments/mongoid.rb +3 -24
- data/lib/scout_apm/instruments/net_http.rb +2 -7
- data/lib/scout_apm/instruments/percentile_sampler.rb +19 -36
- data/lib/scout_apm/instruments/process/process_cpu.rb +2 -3
- data/lib/scout_apm/instruments/process/process_memory.rb +3 -3
- data/lib/scout_apm/layaway.rb +33 -76
- data/lib/scout_apm/layer.rb +59 -16
- data/lib/scout_apm/layer_converters/converter_base.rb +0 -199
- data/lib/scout_apm/layer_converters/job_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/metric_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +90 -15
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +101 -13
- data/lib/scout_apm/metric_set.rb +1 -9
- data/lib/scout_apm/metric_stats.rb +8 -8
- data/lib/scout_apm/reporter.rb +15 -51
- data/lib/scout_apm/request_histograms.rb +0 -4
- data/lib/scout_apm/request_manager.rb +1 -2
- data/lib/scout_apm/scored_item_set.rb +0 -7
- data/lib/scout_apm/serializers/deploy_serializer.rb +16 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +3 -9
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +5 -2
- data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +1 -2
- data/lib/scout_apm/server_integrations/puma.rb +2 -5
- data/lib/scout_apm/slow_item_set.rb +80 -0
- data/lib/scout_apm/slow_job_record.rb +1 -6
- data/lib/scout_apm/slow_transaction.rb +2 -20
- data/lib/scout_apm/store.rb +12 -50
- data/lib/scout_apm/trace_compactor.rb +311 -0
- data/lib/scout_apm/tracked_request.rb +37 -128
- data/lib/scout_apm/utils/backtrace_parser.rb +5 -7
- data/lib/scout_apm/utils/fake_stacks.rb +83 -0
- data/lib/scout_apm/version.rb +1 -1
- data/scout_apm.gemspec +4 -6
- data/test/test_helper.rb +0 -56
- data/test/unit/config_test.rb +9 -60
- data/test/unit/histogram_test.rb +0 -14
- data/test/unit/layaway_test.rb +16 -31
- data/test/unit/serializers/payload_serializer_test.rb +105 -3
- data/test/unit/slow_item_set_test.rb +94 -0
- data/test/unit/slow_job_policy_test.rb +49 -0
- data/test/unit/slow_request_policy_test.rb +5 -4
- data/test/unit/utils/backtrace_parser_test.rb +0 -19
- data/tester.rb +53 -0
- metadata +29 -124
- data/.rubocop.yml +0 -8
- data/Guardfile +0 -42
- data/ext/rusage/README.md +0 -26
- data/ext/rusage/extconf.rb +0 -5
- data/ext/rusage/rusage.c +0 -52
- data/lib/scout_apm/background_job_integrations/resque.rb +0 -85
- data/lib/scout_apm/background_recorder.rb +0 -43
- data/lib/scout_apm/debug.rb +0 -37
- data/lib/scout_apm/git_revision.rb +0 -51
- data/lib/scout_apm/instruments/action_view.rb +0 -49
- data/lib/scout_apm/instruments/resque.rb +0 -40
- data/lib/scout_apm/layer_children_set.rb +0 -77
- data/lib/scout_apm/limited_layer.rb +0 -122
- data/lib/scout_apm/rack.rb +0 -26
- data/lib/scout_apm/remote/message.rb +0 -23
- data/lib/scout_apm/remote/recorder.rb +0 -57
- data/lib/scout_apm/remote/router.rb +0 -49
- data/lib/scout_apm/remote/server.rb +0 -58
- data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +0 -21
- data/lib/scout_apm/synchronous_recorder.rb +0 -26
- data/lib/scout_apm/utils/gzip_helper.rb +0 -24
- data/lib/scout_apm/utils/numbers.rb +0 -14
- data/lib/scout_apm/utils/scm.rb +0 -14
- data/test/unit/background_job_integrations/sidekiq_test.rb +0 -104
- data/test/unit/context_test.rb +0 -30
- data/test/unit/git_revision_test.rb +0 -15
- data/test/unit/instruments/net_http_test.rb +0 -21
- data/test/unit/instruments/percentile_sampler_test.rb +0 -137
- data/test/unit/layer_children_set_test.rb +0 -88
- data/test/unit/limited_layer_test.rb +0 -53
- data/test/unit/remote/test_message.rb +0 -13
- data/test/unit/remote/test_router.rb +0 -33
- data/test/unit/remote/test_server.rb +0 -15
- data/test/unit/store_test.rb +0 -89
- data/test/unit/test_tracked_request.rb +0 -87
- data/test/unit/utils/numbers_test.rb +0 -15
- data/test/unit/utils/scm.rb +0 -17
@@ -5,6 +5,8 @@ module ScoutApm
|
|
5
5
|
def self.call(subject, attributes_list)
|
6
6
|
attributes_list.inject({}) do |attribute_hash, attribute|
|
7
7
|
case attribute
|
8
|
+
when :traces
|
9
|
+
attribute_hash[attribute] = subject.traces.to_a
|
8
10
|
when Array
|
9
11
|
attribute_hash[attribute[0]] = subject.send(attribute[1])
|
10
12
|
when :bucket
|
@@ -1,9 +1,6 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
module BackgroundJobIntegrations
|
3
3
|
class DelayedJob
|
4
|
-
ACTIVE_JOB_KLASS = 'ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper'.freeze
|
5
|
-
DJ_PERFORMABLE_METHOD = 'Delayed::PerformableMethod'.freeze
|
6
|
-
|
7
4
|
attr_reader :logger
|
8
5
|
|
9
6
|
def name
|
@@ -11,79 +8,12 @@ module ScoutApm
|
|
11
8
|
end
|
12
9
|
|
13
10
|
def present?
|
14
|
-
defined?(::Delayed::Job)
|
11
|
+
defined?(::Delayed::Job) && (File.basename($0) =~ /\Adelayed_job/)
|
15
12
|
end
|
16
13
|
|
17
14
|
def forking?
|
18
15
|
false
|
19
16
|
end
|
20
|
-
|
21
|
-
def install
|
22
|
-
plugin = Class.new(Delayed::Plugin) do
|
23
|
-
require 'delayed_job'
|
24
|
-
|
25
|
-
callbacks do |lifecycle|
|
26
|
-
lifecycle.around(:invoke_job) do |job, *args, &block|
|
27
|
-
ScoutApm::Agent.instance.start_background_worker unless ScoutApm::Agent.instance.background_worker_running?
|
28
|
-
|
29
|
-
name = begin
|
30
|
-
case job.payload_object.class.to_s
|
31
|
-
|
32
|
-
# ActiveJob's class wraps the actual job class
|
33
|
-
when ACTIVE_JOB_KLASS
|
34
|
-
job.payload_object.job_data["job_class"]
|
35
|
-
|
36
|
-
# An adhoc job, called like `@user.delay.fib(10)`.
|
37
|
-
# returns a string like "User#fib"
|
38
|
-
when DJ_PERFORMABLE_METHOD
|
39
|
-
job.name
|
40
|
-
|
41
|
-
# A "real" job called like `Delayed::Job.enqueue(MyJob.new)`
|
42
|
-
# returns "MyJob"
|
43
|
-
else
|
44
|
-
job.payload_object.class.to_s
|
45
|
-
end
|
46
|
-
rescue
|
47
|
-
# Fall back to whatever DJ thinks the name is.
|
48
|
-
job.name
|
49
|
-
end
|
50
|
-
|
51
|
-
queue = job.queue || "default"
|
52
|
-
|
53
|
-
req = ScoutApm::RequestManager.lookup
|
54
|
-
req.job!
|
55
|
-
|
56
|
-
begin
|
57
|
-
latency = Time.now - job.created_at
|
58
|
-
req.annotate_request(:queue_latency => latency)
|
59
|
-
rescue
|
60
|
-
end
|
61
|
-
|
62
|
-
queue_layer = ScoutApm::Layer.new('Queue', queue)
|
63
|
-
job_layer = ScoutApm::Layer.new('Job', name)
|
64
|
-
|
65
|
-
begin
|
66
|
-
req.start_layer(queue_layer)
|
67
|
-
started_queue = true
|
68
|
-
req.start_layer(job_layer)
|
69
|
-
started_job = true
|
70
|
-
|
71
|
-
# Call the job itself.
|
72
|
-
block.call(job, *args)
|
73
|
-
rescue
|
74
|
-
req.error!
|
75
|
-
raise
|
76
|
-
ensure
|
77
|
-
req.stop_layer if started_job
|
78
|
-
req.stop_layer if started_queue
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
Delayed::Worker.plugins << plugin # ScoutApm::BackgroundJobIntegrations::DelayedJobPlugin
|
85
|
-
end
|
86
17
|
end
|
87
18
|
end
|
88
19
|
end
|
89
|
-
|
@@ -8,7 +8,7 @@ module ScoutApm
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def present?
|
11
|
-
defined?(::Sidekiq) && File.basename($
|
11
|
+
defined?(::Sidekiq) && (File.basename($0) =~ /\Asidekiq/)
|
12
12
|
end
|
13
13
|
|
14
14
|
def forking?
|
@@ -16,33 +16,22 @@ 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
|
25
19
|
# ScoutApm::Tracer is not available when this class is defined
|
26
20
|
SidekiqMiddleware.class_eval do
|
27
21
|
include ScoutApm::Tracer
|
28
22
|
end
|
29
|
-
end
|
30
23
|
|
31
|
-
def add_middleware
|
32
24
|
::Sidekiq.configure_server do |config|
|
33
25
|
config.server_middleware do |chain|
|
34
26
|
chain.add SidekiqMiddleware
|
35
27
|
end
|
36
28
|
end
|
37
|
-
end
|
38
29
|
|
39
|
-
def install_processor
|
40
30
|
require 'sidekiq/processor' # sidekiq v4 has not loaded this file by this point
|
41
31
|
|
42
32
|
::Sidekiq::Processor.class_eval do
|
43
33
|
def initialize_with_scout(boss)
|
44
|
-
|
45
|
-
agent.start_background_worker
|
34
|
+
::ScoutApm::Agent.instance.start_background_worker unless ::ScoutApm::Agent.instance.background_worker_running?
|
46
35
|
initialize_without_scout(boss)
|
47
36
|
end
|
48
37
|
|
@@ -52,69 +41,41 @@ module ScoutApm
|
|
52
41
|
end
|
53
42
|
end
|
54
43
|
|
55
|
-
# We insert this middleware into the Sidekiq stack, to capture each job,
|
56
|
-
# and time them.
|
57
44
|
class SidekiqMiddleware
|
58
|
-
def call(
|
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']))
|
52
|
+
|
59
53
|
req = ScoutApm::RequestManager.lookup
|
60
54
|
req.job!
|
61
|
-
req.annotate_request(:queue_latency => latency
|
55
|
+
req.annotate_request(:queue_latency => latency)
|
62
56
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
57
|
+
queue_layer = ScoutApm::Layer.new("Queue", queue)
|
58
|
+
job_layer = ScoutApm::Layer.new("Job", job_class)
|
59
|
+
|
60
|
+
#if ScoutApm::Agent.instance.config.value('profile')
|
61
|
+
# Capture ScoutProf if we can
|
62
|
+
#req.enable_profiled_thread!
|
63
|
+
#job_layer.set_root_class(job_class)
|
64
|
+
#job_layer.traced!
|
65
|
+
#end
|
68
66
|
|
67
|
+
req.start_layer(queue_layer)
|
68
|
+
req.start_layer(job_layer)
|
69
|
+
|
70
|
+
begin
|
69
71
|
yield
|
70
72
|
rescue
|
71
73
|
req.error!
|
72
74
|
raise
|
73
|
-
ensure
|
74
|
-
req.stop_layer if started_job
|
75
|
-
req.stop_layer if started_queue
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
UNKNOWN_CLASS_PLACEHOLDER = 'UnknownJob'.freeze
|
80
|
-
ACTIVE_JOB_KLASS = 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper'.freeze
|
81
|
-
DELAYED_WRAPPER_KLASS = 'Sidekiq::Extensions::DelayedClass'.freeze
|
82
|
-
|
83
|
-
|
84
|
-
def job_class(msg)
|
85
|
-
job_class = msg.fetch('class', UNKNOWN_CLASS_PLACEHOLDER)
|
86
|
-
|
87
|
-
if job_class == ACTIVE_JOB_KLASS && msg.key?('wrapped')
|
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
|
102
|
-
end
|
103
|
-
|
104
|
-
job_class
|
105
|
-
rescue
|
106
|
-
UNKNOWN_CLASS_PLACEHOLDER
|
107
|
-
end
|
108
|
-
|
109
|
-
def latency(msg, time = Time.now.to_f)
|
110
|
-
created_at = msg['enqueued_at'] || msg['created_at']
|
111
|
-
if created_at
|
112
|
-
(time - created_at)
|
113
|
-
else
|
114
|
-
0
|
115
75
|
end
|
116
|
-
|
117
|
-
|
76
|
+
ensure
|
77
|
+
req.stop_layer # Job
|
78
|
+
req.stop_layer # Queue
|
118
79
|
end
|
119
80
|
end
|
120
81
|
end
|
@@ -11,10 +11,6 @@ module ScoutApm
|
|
11
11
|
@keep_running = true
|
12
12
|
end
|
13
13
|
|
14
|
-
def running?
|
15
|
-
@keep_running
|
16
|
-
end
|
17
|
-
|
18
14
|
def stop
|
19
15
|
ScoutApm::Agent.instance.logger.debug "Background Worker: stop requested"
|
20
16
|
@keep_running = false
|
@@ -29,13 +25,19 @@ module ScoutApm
|
|
29
25
|
def start(&block)
|
30
26
|
@task = block
|
31
27
|
|
32
|
-
|
28
|
+
begin
|
29
|
+
ScoutApm::Agent.instance.logger.debug "Background Worker: Starting Background Worker, running every #{period} seconds"
|
33
30
|
|
34
|
-
|
35
|
-
|
31
|
+
# The first run should be 1 period of time from now
|
32
|
+
next_time = Time.now + period
|
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
|
36
40
|
|
37
|
-
loop do
|
38
|
-
begin
|
39
41
|
now = Time.now
|
40
42
|
|
41
43
|
# Sleep the correct amount of time to reach next_time
|
@@ -45,23 +47,17 @@ module ScoutApm
|
|
45
47
|
now = Time.now
|
46
48
|
end
|
47
49
|
|
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
|
-
|
54
50
|
@task.call
|
55
51
|
|
56
52
|
# Adjust the next time to run forward by @periods until it is in the future
|
57
53
|
while next_time <= now
|
58
54
|
next_time += period
|
59
55
|
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
|
64
56
|
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
|
65
61
|
end
|
66
62
|
end
|
67
63
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Encapsulates logic for determining capacity utilization of the Ruby processes.
|
2
|
+
module ScoutApm
|
3
|
+
class Capacity
|
4
|
+
attr_reader :processing_start_time, :accumulated_time, :transaction_entry_time
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@processing_start_time = Time.now
|
8
|
+
@lock ||= Mutex.new # the transaction_entry_time could be modified while processing a request or when #process is called.
|
9
|
+
@accumulated_time = 0.0
|
10
|
+
end
|
11
|
+
|
12
|
+
# Called when a transaction is traced.
|
13
|
+
def start_transaction!
|
14
|
+
@lock.synchronize do
|
15
|
+
@transaction_entry_time = Time.now
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Called when a transaction completes to record its time used.
|
20
|
+
def finish_transaction!
|
21
|
+
@lock.synchronize do
|
22
|
+
if transaction_entry_time
|
23
|
+
@accumulated_time += (Time.now - transaction_entry_time).to_f
|
24
|
+
else
|
25
|
+
ScoutApm::Agent.instance.logger.warn "No transaction entry time. Not recording capacity metrics for transaction."
|
26
|
+
end
|
27
|
+
@transaction_entry_time = nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Ran when sending metrics to server. Reports capacity usage metrics.
|
32
|
+
def process
|
33
|
+
process_time = Time.now
|
34
|
+
ScoutApm::Agent.instance.logger.debug "Processing capacity usage for [#{@processing_start_time}] to [#{process_time}]. Time Spent: #{@accumulated_time}."
|
35
|
+
@lock.synchronize do
|
36
|
+
time_spent = @accumulated_time
|
37
|
+
@accumulated_time = 0.0
|
38
|
+
# If a transaction is still running, capture its running time up to now and
|
39
|
+
# reset the +transaction_entry_time+ to now.
|
40
|
+
if @transaction_entry_time
|
41
|
+
time_spent += (process_time - @transaction_entry_time).to_f
|
42
|
+
ScoutApm::Agent.instance.logger.debug "A transaction is running while calculating capacity. Start time: [#{transaction_entry_time}]. Will update the entry time to [#{process_time}]."
|
43
|
+
@transaction_entry_time = process_time # prevent from over-counting capacity usage. update the transaction start time to now.
|
44
|
+
end
|
45
|
+
time_spent = 0.0 if time_spent < 0.0
|
46
|
+
|
47
|
+
window = (process_time - processing_start_time).to_f # time period we are evaulating capacity usage.
|
48
|
+
window = 1.0 if window <= 0.0 # prevent divide-by-zero if clock adjusted.
|
49
|
+
capacity = time_spent / window
|
50
|
+
ScoutApm::Agent.instance.logger.debug "Instance/Capacity: #{capacity}"
|
51
|
+
ScoutApm::Agent.instance.store.track_one!("Instance", "Capacity", capacity)
|
52
|
+
|
53
|
+
@processing_start_time = process_time
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/scout_apm/config.rb
CHANGED
@@ -5,63 +5,30 @@ 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
|
-
#
|
12
8
|
# application_root - override the detected directory of the application
|
13
|
-
# compress_payload - true/false to enable gzipping of payload
|
14
9
|
# data_file - override the default temporary storage location. Must be a location in a writable directory
|
15
|
-
#
|
10
|
+
# host - override the default hostname detection. Default varies by environment - either system hostname, or PAAS hostname
|
16
11
|
# 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
|
20
12
|
# key - the account key with Scout APM. Found in Settings in the Web UI
|
21
13
|
# log_file_path - either a directory or "STDOUT".
|
22
14
|
# log_level - DEBUG / INFO / WARN as usual
|
23
15
|
# monitor - true or false. False prevents any instrumentation from starting
|
24
16
|
# name - override the name reported to APM. This is the name that shows in the Web UI
|
25
|
-
# profile - turn on/off scoutprof (only applicable in Gem versions including scoutprof)
|
26
|
-
# proxy - an http proxy
|
27
|
-
# report_format - 'json' or 'marshal'. Marshal is legacy and will be removed.
|
28
|
-
# scm_subdirectory - if the app root lives in source management in a subdirectory. E.g. #{SCM_ROOT}/src
|
29
17
|
# uri_reporting - 'path' or 'full_path' default is 'full_path', which reports URL params as well as the path.
|
30
|
-
#
|
31
|
-
#
|
18
|
+
# report_format - 'json' or 'marshal'. Marshal is legacy and will be removed.
|
19
|
+
# dev_trace - true or false. Enables always-on tracing in development environmen only
|
20
|
+
# enable_background_jobs - true or false
|
32
21
|
#
|
33
22
|
# Any of these config settings can be set with an environment variable prefixed
|
34
23
|
# by SCOUT_ and uppercasing the key: SCOUT_LOG_LEVEL for instance.
|
35
24
|
|
25
|
+
|
26
|
+
# Config - Made up of config overlay
|
27
|
+
# Default -> File -> Environment Var
|
28
|
+
# QUESTION: How to embed arrays or hashes into ENV?
|
29
|
+
|
36
30
|
module ScoutApm
|
37
31
|
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
|
-
]
|
65
32
|
|
66
33
|
################################################################################
|
67
34
|
# Coersions
|
@@ -134,12 +101,10 @@ module ScoutApm
|
|
134
101
|
|
135
102
|
|
136
103
|
SETTING_COERCIONS = {
|
137
|
-
"
|
138
|
-
"detailed_middleware" => BooleanCoercion.new,
|
139
|
-
"dev_trace" => BooleanCoercion.new,
|
104
|
+
"monitor" => BooleanCoercion.new,
|
140
105
|
"enable_background_jobs" => BooleanCoercion.new,
|
106
|
+
"dev_trace" => BooleanCoercion.new,
|
141
107
|
"ignore" => JsonCoercion.new,
|
142
|
-
"monitor" => BooleanCoercion.new,
|
143
108
|
}
|
144
109
|
|
145
110
|
|
@@ -164,7 +129,7 @@ module ScoutApm
|
|
164
129
|
def self.with_file(file_path=nil, config={})
|
165
130
|
overlays = [
|
166
131
|
ConfigEnvironment.new,
|
167
|
-
ConfigFile.new(file_path, config),
|
132
|
+
ConfigFile.new(file_path, config[:file]),
|
168
133
|
ConfigDefaults.new,
|
169
134
|
ConfigNull.new,
|
170
135
|
]
|
@@ -172,61 +137,28 @@ module ScoutApm
|
|
172
137
|
end
|
173
138
|
|
174
139
|
def initialize(overlays)
|
175
|
-
@overlays =
|
176
|
-
end
|
177
|
-
|
178
|
-
# For a given key, what is the first overlay says that it can handle it?
|
179
|
-
def overlay_for_key(key)
|
180
|
-
@overlays.detect{ |overlay| overlay.has_key?(key) }
|
140
|
+
@overlays = overlays
|
181
141
|
end
|
182
142
|
|
183
143
|
def value(key)
|
184
|
-
|
185
|
-
ScoutApm::Agent.instance.logger.debug("Requested looking up a unknown configuration key: #{key} (not a problem. Evaluate and add to config.rb)")
|
186
|
-
end
|
187
|
-
|
188
|
-
o = overlay_for_key(key)
|
189
|
-
raw_value = if o
|
190
|
-
o.value(key)
|
191
|
-
else
|
192
|
-
# No overlay said it could handle this key, bail out with nil.
|
193
|
-
nil
|
194
|
-
end
|
144
|
+
raw_value = @overlays.detect{ |overlay| overlay.has_key?(key) }.value(key)
|
195
145
|
|
196
|
-
coercion = SETTING_COERCIONS
|
146
|
+
coercion = SETTING_COERCIONS[key] || NullCoercion.new
|
197
147
|
coercion.coerce(raw_value)
|
198
148
|
end
|
199
149
|
|
200
|
-
# Did we load anything for configuration?
|
201
|
-
def any_keys_found?
|
202
|
-
@overlays.any? { |overlay| overlay.any_keys_found? }
|
203
|
-
end
|
204
|
-
|
205
|
-
def log_settings
|
206
|
-
messages = KNOWN_CONFIG_OPTIONS.inject([]) do |memo, key|
|
207
|
-
o = overlay_for_key(key)
|
208
|
-
memo << "#{o.name} - #{key}: #{value(key).inspect}"
|
209
|
-
end
|
210
|
-
ScoutApm::Agent.instance.logger.debug("Resolved Setting Values:\n" + messages.join("\n"))
|
211
|
-
end
|
212
|
-
|
213
150
|
class ConfigDefaults
|
214
151
|
DEFAULTS = {
|
215
|
-
'
|
216
|
-
'detailed_middleware' => false,
|
217
|
-
'dev_trace' => false,
|
152
|
+
'host' => 'https://checkin.scoutapp.com',
|
218
153
|
'direct_host' => 'https://apm.scoutapp.com',
|
154
|
+
'log_level' => 'info',
|
155
|
+
'uri_reporting' => 'full_path',
|
156
|
+
'report_format' => 'json',
|
219
157
|
'disabled_instruments' => [],
|
220
158
|
'enable_background_jobs' => true,
|
221
|
-
'host' => 'https://checkin.scoutapp.com',
|
222
159
|
'ignore' => [],
|
223
|
-
'
|
224
|
-
'profile'
|
225
|
-
'report_format' => 'json',
|
226
|
-
'scm_subdirectory' => '',
|
227
|
-
'uri_reporting' => 'full_path',
|
228
|
-
'remote_agent_host' => '127.0.0.1',
|
229
|
-
'remote_agent_port' => 7721, # picked at random
|
160
|
+
'dev_trace' => false, # false for now so code can live in main branch
|
161
|
+
'profile' => true # for scoutprof
|
230
162
|
}.freeze
|
231
163
|
|
232
164
|
def value(key)
|
@@ -236,15 +168,6 @@ module ScoutApm
|
|
236
168
|
def has_key?(key)
|
237
169
|
DEFAULTS.has_key?(key)
|
238
170
|
end
|
239
|
-
|
240
|
-
# Defaults are here, but not counted as user specified.
|
241
|
-
def any_keys_found?
|
242
|
-
false
|
243
|
-
end
|
244
|
-
|
245
|
-
def name
|
246
|
-
"defaults"
|
247
|
-
end
|
248
171
|
end
|
249
172
|
|
250
173
|
|
@@ -259,14 +182,6 @@ module ScoutApm
|
|
259
182
|
def has_key?(*)
|
260
183
|
true
|
261
184
|
end
|
262
|
-
|
263
|
-
def any_keys_found?
|
264
|
-
false
|
265
|
-
end
|
266
|
-
|
267
|
-
def name
|
268
|
-
"no-config"
|
269
|
-
end
|
270
185
|
end
|
271
186
|
|
272
187
|
class ConfigEnvironment
|
@@ -282,16 +197,6 @@ module ScoutApm
|
|
282
197
|
def key_to_env_key(key)
|
283
198
|
'SCOUT_' + key.upcase
|
284
199
|
end
|
285
|
-
|
286
|
-
def any_keys_found?
|
287
|
-
KNOWN_CONFIG_OPTIONS.any? { |option|
|
288
|
-
ENV.has_key?(key_to_env_key(option))
|
289
|
-
}
|
290
|
-
end
|
291
|
-
|
292
|
-
def name
|
293
|
-
"environment"
|
294
|
-
end
|
295
200
|
end
|
296
201
|
|
297
202
|
# Attempts to load a configuration file, and parse it as YAML. If the file
|
@@ -317,16 +222,6 @@ module ScoutApm
|
|
317
222
|
@settings.has_key?(key)
|
318
223
|
end
|
319
224
|
|
320
|
-
def any_keys_found?
|
321
|
-
KNOWN_CONFIG_OPTIONS.any? { |option|
|
322
|
-
@settings.has_key?(option)
|
323
|
-
}
|
324
|
-
end
|
325
|
-
|
326
|
-
def name
|
327
|
-
"config-file"
|
328
|
-
end
|
329
|
-
|
330
225
|
private
|
331
226
|
|
332
227
|
def load_file(file)
|
@@ -347,18 +242,17 @@ module ScoutApm
|
|
347
242
|
raw_file = File.read(@resolved_file_path)
|
348
243
|
erb_file = ERB.new(raw_file).result(binding)
|
349
244
|
parsed_yaml = YAML.load(erb_file)
|
350
|
-
|
245
|
+
@settings = parsed_yaml[app_environment]
|
351
246
|
|
352
|
-
if
|
353
|
-
|
354
|
-
|
355
|
-
@file_loaded = true
|
356
|
-
else
|
357
|
-
logger.info("Couldn't find configuration in #{@resolved_file_path} for environment: #{app_environment}. Configuration in ENV will still be applied.")
|
358
|
-
@file_loaded = false
|
247
|
+
if !@settings.is_a? Hash
|
248
|
+
raise ("Missing environment key for: #{app_environment}. This can happen if the key is missing, or with a malformed configuration file," +
|
249
|
+
" check that there is a top level #{app_environment} key.")
|
359
250
|
end
|
251
|
+
|
252
|
+
logger.debug("Loaded Configuration: #{@resolved_file_path}. Using environment: #{app_environment}")
|
253
|
+
@file_loaded = true
|
360
254
|
rescue Exception => e # Explicit `Exception` handling to catch SyntaxError and anything else that ERB or YAML may throw
|
361
|
-
logger.
|
255
|
+
logger.debug("Failed loading configuration file: #{e.message}. ScoutAPM will continue starting with configuration from ENV and defaults")
|
362
256
|
@file_loaded = false
|
363
257
|
end
|
364
258
|
end
|