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.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/CHANGELOG.markdown +147 -2
  4. data/Guardfile +43 -0
  5. data/Rakefile +2 -2
  6. data/ext/allocations/allocations.c +6 -0
  7. data/ext/allocations/extconf.rb +1 -0
  8. data/ext/rusage/README.md +26 -0
  9. data/ext/rusage/extconf.rb +5 -0
  10. data/ext/rusage/rusage.c +52 -0
  11. data/lib/scout_apm.rb +28 -15
  12. data/lib/scout_apm/agent.rb +89 -37
  13. data/lib/scout_apm/agent/logging.rb +6 -1
  14. data/lib/scout_apm/agent/reporting.rb +9 -6
  15. data/lib/scout_apm/app_server_load.rb +21 -10
  16. data/lib/scout_apm/attribute_arranger.rb +6 -3
  17. data/lib/scout_apm/background_job_integrations/delayed_job.rb +71 -1
  18. data/lib/scout_apm/background_job_integrations/resque.rb +85 -0
  19. data/lib/scout_apm/background_job_integrations/sidekiq.rb +22 -20
  20. data/lib/scout_apm/background_recorder.rb +43 -0
  21. data/lib/scout_apm/background_worker.rb +19 -15
  22. data/lib/scout_apm/config.rb +138 -28
  23. data/lib/scout_apm/db_query_metric_set.rb +80 -0
  24. data/lib/scout_apm/db_query_metric_stats.rb +102 -0
  25. data/lib/scout_apm/debug.rb +37 -0
  26. data/lib/scout_apm/environment.rb +22 -15
  27. data/lib/scout_apm/git_revision.rb +51 -0
  28. data/lib/scout_apm/histogram.rb +11 -2
  29. data/lib/scout_apm/instant/assets/xmlhttp_instrumentation.html +2 -2
  30. data/lib/scout_apm/instant/middleware.rb +196 -54
  31. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +89 -68
  32. data/lib/scout_apm/instruments/action_view.rb +49 -0
  33. data/lib/scout_apm/instruments/active_record.rb +127 -3
  34. data/lib/scout_apm/instruments/grape.rb +4 -3
  35. data/lib/scout_apm/instruments/middleware_detailed.rb +4 -6
  36. data/lib/scout_apm/instruments/mongoid.rb +24 -3
  37. data/lib/scout_apm/instruments/net_http.rb +7 -2
  38. data/lib/scout_apm/instruments/percentile_sampler.rb +36 -19
  39. data/lib/scout_apm/instruments/process/process_cpu.rb +3 -2
  40. data/lib/scout_apm/instruments/process/process_memory.rb +3 -3
  41. data/lib/scout_apm/instruments/resque.rb +40 -0
  42. data/lib/scout_apm/layaway.rb +67 -28
  43. data/lib/scout_apm/layer.rb +19 -59
  44. data/lib/scout_apm/layer_children_set.rb +77 -0
  45. data/lib/scout_apm/layer_converters/allocation_metric_converter.rb +5 -6
  46. data/lib/scout_apm/layer_converters/converter_base.rb +201 -14
  47. data/lib/scout_apm/layer_converters/database_converter.rb +55 -0
  48. data/lib/scout_apm/layer_converters/depth_first_walker.rb +22 -10
  49. data/lib/scout_apm/layer_converters/error_converter.rb +5 -7
  50. data/lib/scout_apm/layer_converters/find_layer_by_type.rb +34 -0
  51. data/lib/scout_apm/layer_converters/histograms.rb +14 -0
  52. data/lib/scout_apm/layer_converters/job_converter.rb +36 -50
  53. data/lib/scout_apm/layer_converters/metric_converter.rb +17 -19
  54. data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +10 -12
  55. data/lib/scout_apm/layer_converters/slow_job_converter.rb +41 -115
  56. data/lib/scout_apm/layer_converters/slow_request_converter.rb +33 -117
  57. data/lib/scout_apm/limited_layer.rb +126 -0
  58. data/lib/scout_apm/metric_meta.rb +0 -5
  59. data/lib/scout_apm/metric_set.rb +9 -1
  60. data/lib/scout_apm/metric_stats.rb +7 -8
  61. data/lib/scout_apm/rack.rb +26 -0
  62. data/lib/scout_apm/remote/message.rb +23 -0
  63. data/lib/scout_apm/remote/recorder.rb +57 -0
  64. data/lib/scout_apm/remote/router.rb +49 -0
  65. data/lib/scout_apm/remote/server.rb +58 -0
  66. data/lib/scout_apm/reporter.rb +51 -15
  67. data/lib/scout_apm/request_histograms.rb +4 -0
  68. data/lib/scout_apm/request_manager.rb +2 -1
  69. data/lib/scout_apm/scored_item_set.rb +7 -0
  70. data/lib/scout_apm/serializers/db_query_serializer_to_json.rb +15 -0
  71. data/lib/scout_apm/serializers/histograms_serializer_to_json.rb +21 -0
  72. data/lib/scout_apm/serializers/payload_serializer.rb +10 -3
  73. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +6 -6
  74. data/lib/scout_apm/serializers/slow_jobs_serializer_to_json.rb +2 -1
  75. data/lib/scout_apm/server_integrations/puma.rb +5 -2
  76. data/lib/scout_apm/slow_job_policy.rb +1 -10
  77. data/lib/scout_apm/slow_job_record.rb +6 -1
  78. data/lib/scout_apm/slow_request_policy.rb +1 -10
  79. data/lib/scout_apm/slow_transaction.rb +20 -2
  80. data/lib/scout_apm/store.rb +66 -12
  81. data/lib/scout_apm/synchronous_recorder.rb +26 -0
  82. data/lib/scout_apm/tracked_request.rb +136 -71
  83. data/lib/scout_apm/utils/active_record_metric_name.rb +8 -4
  84. data/lib/scout_apm/utils/backtrace_parser.rb +3 -3
  85. data/lib/scout_apm/utils/gzip_helper.rb +24 -0
  86. data/lib/scout_apm/utils/numbers.rb +14 -0
  87. data/lib/scout_apm/utils/scm.rb +14 -0
  88. data/lib/scout_apm/version.rb +1 -1
  89. data/scout_apm.gemspec +5 -4
  90. data/test/test_helper.rb +18 -0
  91. data/test/unit/config_test.rb +59 -8
  92. data/test/unit/db_query_metric_set_test.rb +56 -0
  93. data/test/unit/db_query_metric_stats_test.rb +113 -0
  94. data/test/unit/git_revision_test.rb +15 -0
  95. data/test/unit/histogram_test.rb +14 -0
  96. data/test/unit/instruments/net_http_test.rb +21 -0
  97. data/test/unit/instruments/percentile_sampler_test.rb +137 -0
  98. data/test/unit/layaway_test.rb +20 -0
  99. data/test/unit/layer_children_set_test.rb +88 -0
  100. data/test/unit/layer_converters/depth_first_walker_test.rb +66 -0
  101. data/test/unit/layer_converters/metric_converter_test.rb +22 -0
  102. data/test/unit/layer_converters/stubs.rb +33 -0
  103. data/test/unit/limited_layer_test.rb +53 -0
  104. data/test/unit/remote/test_message.rb +13 -0
  105. data/test/unit/remote/test_router.rb +33 -0
  106. data/test/unit/remote/test_server.rb +15 -0
  107. data/test/unit/serializers/payload_serializer_test.rb +3 -12
  108. data/test/unit/store_test.rb +66 -0
  109. data/test/unit/test_tracked_request.rb +87 -0
  110. data/test/unit/utils/active_record_metric_name_test.rb +8 -0
  111. data/test/unit/utils/backtrace_parser_test.rb +5 -0
  112. data/test/unit/utils/numbers_test.rb +15 -0
  113. data/test/unit/utils/scm.rb +17 -0
  114. metadata +125 -30
  115. data/ext/stacks/extconf.rb +0 -37
  116. data/ext/stacks/scout_atomics.h +0 -86
  117. data/ext/stacks/stacks.c +0 -811
  118. data/lib/scout_apm/capacity.rb +0 -57
  119. data/lib/scout_apm/deploy_integrations/capistrano_2.cap +0 -12
  120. data/lib/scout_apm/deploy_integrations/capistrano_2.rb +0 -83
  121. data/lib/scout_apm/deploy_integrations/capistrano_3.cap +0 -12
  122. data/lib/scout_apm/deploy_integrations/capistrano_3.rb +0 -88
  123. data/lib/scout_apm/instruments/delayed_job.rb +0 -57
  124. data/lib/scout_apm/serializers/deploy_serializer.rb +0 -16
  125. data/lib/scout_apm/trace_compactor.rb +0 -312
  126. data/lib/scout_apm/utils/fake_stacks.rb +0 -87
  127. 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(queue_layer)
64
+ req.start_layer(ScoutApm::Layer.new('Queue', queue))
77
65
  started_queue = true
78
- req.start_layer(job_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
- job_class = msg['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
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
- begin
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
- # 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
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
@@ -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
- # host - override the default hostname detection. Default varies by environment - either system hostname, or PAAS hostname
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
- # uri_reporting - 'path' or 'full_path' default is 'full_path', which reports URL params as well as the path.
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
- # dev_trace - true or false. Enables always-on tracing in development environmen only
20
- # enable_background_jobs - true or false
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
- "monitor" => BooleanCoercion.new,
105
- "enable_background_jobs" => BooleanCoercion.new,
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[:file]),
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
- o = @overlays.detect{ |overlay| overlay.has_key?(key) }
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[key] || NullCoercion.new
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
- 'host' => 'https://checkin.scoutapp.com',
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
- 'dev_trace' => false, # false for now so code can live in main branch
167
- 'profile' => true # for scoutprof
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
- @settings = parsed_yaml[app_environment]
360
+ file_settings = parsed_yaml[app_environment]
252
361
 
253
- if !@settings.is_a? Hash
254
- raise ("Missing environment key for: #{app_environment}. This can happen if the key is missing, or with a malformed configuration file," +
255
- " check that there is a top level #{app_environment} key.")
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.debug("Failed loading configuration file: #{e.message}. ScoutAPM will continue starting with configuration from ENV and defaults")
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