scout_apm_logging 0.0.0.1

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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +37 -0
  3. data/.gitignore +13 -0
  4. data/.rubocop.yml +65 -0
  5. data/Dockerfile +18 -0
  6. data/Gemfile +5 -0
  7. data/README.md +58 -0
  8. data/Rakefile +35 -0
  9. data/bin/scout_apm_logging_monitor +5 -0
  10. data/gems/rails.gemfile +3 -0
  11. data/lib/scout_apm/logging/config.rb +265 -0
  12. data/lib/scout_apm/logging/context.rb +58 -0
  13. data/lib/scout_apm/logging/logger.rb +26 -0
  14. data/lib/scout_apm/logging/loggers/capture.rb +46 -0
  15. data/lib/scout_apm/logging/loggers/formatter.rb +86 -0
  16. data/lib/scout_apm/logging/loggers/logger.rb +82 -0
  17. data/lib/scout_apm/logging/loggers/proxy.rb +39 -0
  18. data/lib/scout_apm/logging/loggers/swap.rb +82 -0
  19. data/lib/scout_apm/logging/monitor/collector/checksum.rb +51 -0
  20. data/lib/scout_apm/logging/monitor/collector/configuration.rb +148 -0
  21. data/lib/scout_apm/logging/monitor/collector/downloader.rb +78 -0
  22. data/lib/scout_apm/logging/monitor/collector/extractor.rb +37 -0
  23. data/lib/scout_apm/logging/monitor/collector/manager.rb +57 -0
  24. data/lib/scout_apm/logging/monitor/monitor.rb +214 -0
  25. data/lib/scout_apm/logging/monitor_manager/manager.rb +150 -0
  26. data/lib/scout_apm/logging/state.rb +70 -0
  27. data/lib/scout_apm/logging/utils.rb +86 -0
  28. data/lib/scout_apm/logging/version.rb +7 -0
  29. data/lib/scout_apm_logging.rb +35 -0
  30. data/scout_apm_logging.gemspec +27 -0
  31. data/spec/data/config_test_1.yml +27 -0
  32. data/spec/data/empty_logs_config.yml +0 -0
  33. data/spec/data/logs_config.yml +3 -0
  34. data/spec/data/mock_config.yml +29 -0
  35. data/spec/data/state_file.json +3 -0
  36. data/spec/integration/loggers/capture_spec.rb +78 -0
  37. data/spec/integration/monitor/collector/downloader/will_verify_checksum.rb +47 -0
  38. data/spec/integration/monitor/collector_healthcheck_spec.rb +27 -0
  39. data/spec/integration/monitor/continuous_state_collector_spec.rb +29 -0
  40. data/spec/integration/monitor/previous_collector_setup_spec.rb +42 -0
  41. data/spec/integration/monitor_manager/disable_agent_spec.rb +28 -0
  42. data/spec/integration/monitor_manager/monitor_pid_file_spec.rb +36 -0
  43. data/spec/integration/monitor_manager/single_monitor_spec.rb +53 -0
  44. data/spec/integration/rails/lifecycle_spec.rb +29 -0
  45. data/spec/spec_helper.rb +65 -0
  46. data/spec/unit/config_spec.rb +25 -0
  47. data/spec/unit/loggers/capture_spec.rb +64 -0
  48. data/spec/unit/monitor/collector/configuration_spec.rb +64 -0
  49. data/spec/unit/state_spec.rb +20 -0
  50. data/tooling/checksums.rb +106 -0
  51. metadata +167 -0
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # Launched as a daemon process by the monitor manager at Rails startup.
5
+ ##
6
+ require 'json'
7
+
8
+ require 'scout_apm'
9
+
10
+ require_relative '../logger'
11
+ require_relative '../context'
12
+ require_relative '../config'
13
+ require_relative '../utils'
14
+ require_relative '../state'
15
+ require_relative './collector/manager'
16
+
17
+ module ScoutApm
18
+ module Logging
19
+ # Entry point for the monitor daemon process.
20
+ class Monitor
21
+ attr_reader :context
22
+ attr_accessor :latest_state_sha
23
+
24
+ @@instance = nil
25
+
26
+ def self.instance
27
+ @@instance ||= new
28
+ end
29
+
30
+ def initialize
31
+ @context = Context.new
32
+
33
+ context.application_root = $stdin.gets&.chomp
34
+
35
+ # Load in the dynamic and state based config settings.
36
+ context.config = Config.with_file(context, determine_scout_config_filepath)
37
+
38
+ daemonize_process!
39
+ end
40
+
41
+ def setup!
42
+ context.config.logger.info('Monitor daemon process started')
43
+
44
+ add_exit_handler!
45
+
46
+ unless has_logs_to_monitor?
47
+ context.config.logger.warn('No logs are set to be monitored. Please set the `logs_monitored` config setting. Exiting.')
48
+ return
49
+ end
50
+
51
+ initiate_collector_setup! unless has_previous_collector_setup?
52
+
53
+ @latest_state_sha = get_state_file_sha
54
+
55
+ run!
56
+ end
57
+
58
+ def run!
59
+ # Prevent the monitor from checking the collector health before it's fully started.
60
+ # Having this be configurable is useful for testing.
61
+ sleep context.config.value('monitor_interval_delay')
62
+
63
+ loop do
64
+ sleep context.config.value('monitor_interval')
65
+
66
+ check_collector_health
67
+
68
+ check_state_change
69
+ end
70
+ end
71
+
72
+ # Only useful for testing.
73
+ def config=(config)
74
+ context.config = config
75
+ end
76
+
77
+ private
78
+
79
+ def daemonize_process!
80
+ # Similar to that of Process.daemon, but we want to keep the dir, STDOUT and STDERR.
81
+ exit if fork
82
+ Process.setsid
83
+ exit if fork
84
+ $stdin.reopen '/dev/null'
85
+
86
+ File.write(context.config.value('monitor_pid_file'), Process.pid)
87
+ end
88
+
89
+ def has_logs_to_monitor?
90
+ context.config.value('logs_monitored').any?
91
+ end
92
+
93
+ def has_previous_collector_setup?
94
+ return false unless context.config.value('health_check_port') != 0
95
+
96
+ healthy_response = request_health_check_port("http://localhost:#{context.config.value('health_check_port')}/")
97
+
98
+ if healthy_response
99
+ context.logger.info("Collector already setup on port #{context.config.value('health_check_port')}")
100
+ else
101
+ context.logger.info('Setting up new collector')
102
+ end
103
+
104
+ healthy_response
105
+ end
106
+
107
+ def initiate_collector_setup!
108
+ set_health_check_port!
109
+
110
+ Collector::Manager.new(context).setup!
111
+ end
112
+
113
+ def is_port_available?(port)
114
+ socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
115
+ remote_address = Socket.sockaddr_in(port, '127.0.0.1')
116
+
117
+ begin
118
+ socket.connect_nonblock(remote_address)
119
+ rescue Errno::EINPROGRESS
120
+ IO.select(nil, [socket])
121
+ retry
122
+ rescue Errno::EISCONN, Errno::ECONNRESET
123
+ false
124
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
125
+ true
126
+ ensure
127
+ socket.close if socket && !socket.closed?
128
+ end
129
+ end
130
+
131
+ def set_health_check_port!
132
+ health_check_port = 13_133
133
+ until is_port_available?(health_check_port)
134
+ sleep 0.1
135
+ health_check_port += 1
136
+ end
137
+
138
+ Config::ConfigDynamic.set_value('health_check_port', health_check_port)
139
+ context.config.state.flush_state!
140
+ end
141
+
142
+ def request_health_check_port(endpoint)
143
+ uri = URI(endpoint)
144
+
145
+ begin
146
+ response = Net::HTTP.get_response(uri)
147
+
148
+ unless response.is_a?(Net::HTTPSuccess)
149
+ context.logger.error("Error occurred while checking collector health: #{response.message}")
150
+ return false
151
+ end
152
+ rescue StandardError => e
153
+ context.logger.error("Error occurred while checking collector health: #{e.message}")
154
+ return false
155
+ end
156
+
157
+ true
158
+ end
159
+
160
+ def check_collector_health
161
+ context.logger.debug('Checking collector health')
162
+ collector_health_endpoint = "http://localhost:#{context.config.value('health_check_port')}/"
163
+
164
+ healthy_response = request_health_check_port(collector_health_endpoint)
165
+
166
+ initiate_collector_setup! unless healthy_response
167
+ end
168
+
169
+ def remove_collector_process # rubocop:disable Metrics/AbcSize
170
+ return unless File.exist? context.config.value('collector_pid_file')
171
+
172
+ process_id = File.read(context.config.value('collector_pid_file'))
173
+ return if process_id.empty?
174
+
175
+ begin
176
+ Process.kill('TERM', process_id.to_i)
177
+ rescue Errno::ENOENT, Errno::ESRCH => e
178
+ context.logger.error("Error occurred while removing collector process from monitor: #{e.message}")
179
+ ensure
180
+ File.delete(context.config.value('collector_pid_file'))
181
+ end
182
+ end
183
+
184
+ def check_state_change
185
+ current_sha = get_state_file_sha
186
+
187
+ return if current_sha == latest_state_sha
188
+
189
+ remove_collector_process
190
+ initiate_collector_setup!
191
+
192
+ # File SHA can change due to port mappings on collector setup.
193
+ @latest_state_sha = get_state_file_sha
194
+ end
195
+
196
+ def add_exit_handler!
197
+ at_exit do
198
+ # There may not be a file to delete, as the monitor manager ensures cleaning it up when monitoring is disabled.
199
+ File.delete(context.config.value('monitor_pid_file')) if File.exist?(context.config.value('monitor_pid_file'))
200
+ end
201
+ end
202
+
203
+ def get_state_file_sha
204
+ return nil unless File.exist?(context.config.value('monitor_state_file'))
205
+
206
+ `sha256sum #{context.config.value('monitor_state_file')}`.split(' ').first
207
+ end
208
+
209
+ def determine_scout_config_filepath
210
+ "#{context.application_root}/config/scout_apm.yml"
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ScoutApm
4
+ module Logging
5
+ # Manages the creation of the daemon monitor process.
6
+ class MonitorManager
7
+ attr_reader :context
8
+
9
+ @@instance = nil
10
+
11
+ def self.instance
12
+ @@instance ||= new
13
+ end
14
+
15
+ def initialize
16
+ @context = Context.new
17
+ context.config = Config.with_file(context, context.config.value('config_file'))
18
+ end
19
+
20
+ def setup!
21
+ context.config.log_settings(context.logger)
22
+ context.logger.info('Setting up monitor daemon process')
23
+
24
+ add_exit_handler!
25
+
26
+ determine_configuration_state
27
+
28
+ # Continue to hold the lock until we have written the PID file.
29
+ ensure_monitor_pid_file_exists
30
+ end
31
+
32
+ def determine_configuration_state
33
+ monitoring_enabled = context.config.value('logs_monitor')
34
+
35
+ if monitoring_enabled
36
+ context.logger.info('Log monitoring enabled')
37
+ create_process
38
+ else
39
+ context.logger.info('Log monitoring disabled')
40
+ remove_processes
41
+ end
42
+ end
43
+
44
+ # With the use of fileoffsets in the collector, and the persistent queue of already collected logs,
45
+ # we can safely restart the collector. Due to the way fingerprinting of the files works, if the
46
+ # file path switches, but the beginning contents of the file remain the same, the file will be
47
+ # treated as the same file as before.
48
+ # If logs get rotated, the fingerprint changes, and the collector automatically detects this.
49
+ def add_exit_handler!
50
+ at_exit do
51
+ # Only remove/restart the monitor and collector if we are exiting from an app_server process.
52
+ # We need to wait on this check, as the process command line changes at some point.
53
+ remove_processes if Utils.current_process_is_app_server?
54
+ end
55
+ end
56
+
57
+ def create_process
58
+ return if process_exists?
59
+
60
+ Utils.ensure_directory_exists(context.config.value('monitor_pid_file'))
61
+
62
+ reader, writer = IO.pipe
63
+
64
+ gem_directory = File.expand_path('../../../..', __dir__)
65
+
66
+ # As we daemonize the process, we will write to the pid file within the process.
67
+ Process.spawn("ruby #{gem_directory}/bin/scout_apm_logging_monitor", in: reader)
68
+
69
+ reader.close
70
+ # TODO: Add support for Sinatra.
71
+ writer.puts Rails.root if defined?(Rails)
72
+ writer.close
73
+ end
74
+
75
+ private
76
+
77
+ def ensure_monitor_pid_file_exists
78
+ start_time = Time.now
79
+ # We don't want to hold up the initial Rails boot time for very long.
80
+ timeout_seconds = 0.1
81
+
82
+ # Naive benchmarks show this taking ~0.01 seconds.
83
+ loop do
84
+ break if File.exist?(context.config.value('monitor_pid_file'))
85
+
86
+ if Time.now - start_time > timeout_seconds
87
+ context.logger.warn('Unable to verify monitor PID file write. Releasing lock.')
88
+ break
89
+ end
90
+
91
+ sleep 0.01
92
+ end
93
+ end
94
+
95
+ def process_exists?
96
+ return false unless File.exist? context.config.value('monitor_pid_file')
97
+
98
+ process_id = File.read(context.config.value('monitor_pid_file'))
99
+ return false if process_id.empty?
100
+
101
+ process_exists = Utils.check_process_liveliness(process_id.to_i, 'scout_apm_logging_monitor')
102
+ File.delete(context.config.value('monitor_pid_file')) unless process_exists
103
+
104
+ process_exists
105
+ end
106
+
107
+ def remove_monitor_process # rubocop:disable Metrics/AbcSize
108
+ return unless File.exist? context.config.value('monitor_pid_file')
109
+
110
+ process_id = File.read(context.config.value('monitor_pid_file'))
111
+ return if process_id.empty?
112
+
113
+ begin
114
+ Process.kill('TERM', process_id.to_i)
115
+ rescue Errno::ENOENT, Errno::ESRCH => e
116
+ context.logger.error("Error occurred while removing monitor process: #{e.message}")
117
+ File.delete(context.config.value('monitor_pid_file'))
118
+ end
119
+ end
120
+
121
+ def remove_collector_process # rubocop:disable Metrics/AbcSize
122
+ return unless File.exist? context.config.value('collector_pid_file')
123
+
124
+ process_id = File.read(context.config.value('collector_pid_file'))
125
+ return if process_id.empty?
126
+
127
+ begin
128
+ Process.kill('TERM', process_id.to_i)
129
+ rescue Errno::ENOENT, Errno::ESRCH => e
130
+ context.logger.error("Error occurred while removing collector process from manager: #{e.message}")
131
+ ensure
132
+ File.delete(context.config.value('collector_pid_file'))
133
+ end
134
+ end
135
+
136
+ def remove_data_file
137
+ return unless File.exist? context.config.value('monitor_state_file')
138
+
139
+ File.delete(context.config.value('monitor_state_file'))
140
+ end
141
+
142
+ # Remove both the monitor and collector processes that we have spawned.
143
+ def remove_processes
144
+ remove_monitor_process
145
+ remove_collector_process
146
+ remove_data_file
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ScoutApm
4
+ module Logging
5
+ class Config
6
+ # Responsibling for ensuring safe interprocess persitance around configuration state.
7
+ class State
8
+ attr_reader :context
9
+
10
+ def initialize(context)
11
+ @context = context
12
+ end
13
+
14
+ def load_state_from_file
15
+ return unless File.exist?(context.config.value('monitor_state_file'))
16
+
17
+ file_contents = File.read(context.config.value('monitor_state_file'))
18
+ JSON.parse(file_contents)
19
+ end
20
+
21
+ def flush_to_file!(updated_log_locations = []) # rubocop:disable Metrics/AbcSize
22
+ Utils.ensure_directory_exists(context.config.value('monitor_state_file'))
23
+
24
+ File.open(context.config.value('monitor_state_file'), (File::RDWR | File::CREAT), 0o644) do |file|
25
+ file.flock(File::LOCK_EX)
26
+
27
+ data = Config::ConfigState.get_values_to_set.each_with_object({}) do |key, memo|
28
+ memo[key] = context.config.value(key)
29
+ end
30
+
31
+ unless updated_log_locations.empty?
32
+ contents = file.read
33
+
34
+ olds_log_files = if contents.empty?
35
+ []
36
+ else
37
+ current_data = JSON.parse(contents)
38
+ current_data['logs_monitored']
39
+ end
40
+
41
+ data['logs_monitored'] = merge_and_dedup_log_locations(updated_log_locations, olds_log_files)
42
+ end
43
+
44
+ file.rewind # Move cursor to beginning of the file
45
+ file.truncate(0) # Truncate existing content
46
+ file.write(JSON.pretty_generate(data))
47
+ rescue StandardError => e
48
+ context.logger.error("Error occurred while flushing state to file: #{e.message}. Unlocking.")
49
+ ensure
50
+ file.flock(File::LOCK_UN)
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ # Should we add better detection for similar basenames but different paths?
57
+ # May be a bit tricky with tools like capistrano and releases paths differentiated by time.
58
+ def merge_and_dedup_log_locations(new_logs, old_logs)
59
+ # Take the new logs if duplication, as we could be in a newer release.
60
+ merged = (new_logs + old_logs).each_with_object({}) do |log_path, hash|
61
+ base_name = File.basename(log_path)
62
+ hash[base_name] ||= log_path
63
+ end
64
+
65
+ merged.values
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module ScoutApm
6
+ module Logging
7
+ # Miscellaneous utilities for the logging module.
8
+ module Utils
9
+ # Takes a complete file path, and ensures that the directory structure exists.
10
+ def self.ensure_directory_exists(file_path)
11
+ file_path = File.dirname(file_path) unless file_path[-1] == '/'
12
+
13
+ FileUtils.mkdir_p(file_path) unless File.directory?(file_path)
14
+ end
15
+
16
+ # TODO: Add support for other platforms
17
+ def self.get_architecture
18
+ if /arm/ =~ RbConfig::CONFIG['arch']
19
+ 'arm64'
20
+ else
21
+ 'amd64'
22
+ end
23
+ end
24
+
25
+ def self.get_host_os
26
+ if /darwin|mac os/ =~ RbConfig::CONFIG['host_os']
27
+ 'darwin'
28
+ else
29
+ 'linux'
30
+ end
31
+ end
32
+
33
+ def self.check_process_liveliness(pid, name)
34
+ # Pipe to cat to prevent truncation of the output
35
+ process_information = `ps -p #{pid} -o pid=,stat=,command= | cat`
36
+ return false if process_information.empty?
37
+
38
+ process_information_parts = process_information.split(' ')
39
+ process_information_status = process_information_parts[1]
40
+
41
+ return false if process_information_status == 'Z'
42
+ return false unless process_information.include?(name)
43
+
44
+ true
45
+ end
46
+
47
+ def self.current_process_is_app_server?
48
+ # TODO: Add more app servers.
49
+ process_command = `ps -p #{Process.pid} -o command= | cat`.downcase
50
+ [
51
+ process_command.include?('puma'),
52
+ process_command.include?('unicorn'),
53
+ process_command.include?('passenger')
54
+ ].any?
55
+ end
56
+
57
+ def self.skip_setup?
58
+ [
59
+ ARGV.include?('assets:precompile'),
60
+ ARGV.include?('assets:clean'),
61
+ (defined?(::Rails::Console) && $stdout.isatty && $stdin.isatty)
62
+ ].any?
63
+ end
64
+
65
+ def self.attempt_exclusive_lock(context)
66
+ lock_file = context.config.value('manager_lock_file')
67
+ ensure_directory_exists(lock_file)
68
+
69
+ begin
70
+ file = File.open(lock_file, File::RDWR | File::CREAT | File::EXCL)
71
+ rescue Errno::EEXIST
72
+ context.logger.info('Manager lock file held, continuing.')
73
+ return
74
+ end
75
+
76
+ # Ensure the lock file is deleted when the block completes
77
+ begin
78
+ yield
79
+ ensure
80
+ file.close
81
+ File.delete(lock_file) if File.exist?(lock_file)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ScoutApm
4
+ module Logging
5
+ VERSION = '0.0.0.1'
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'scout_apm'
4
+
5
+ require 'scout_apm/logging/config'
6
+ require 'scout_apm/logging/logger'
7
+ require 'scout_apm/logging/context'
8
+ require 'scout_apm/logging/utils'
9
+ require 'scout_apm/logging/state'
10
+
11
+ require 'scout_apm/logging/loggers/capture'
12
+
13
+ require 'scout_apm/logging/monitor_manager/manager'
14
+
15
+ module ScoutApm
16
+ ## This module is responsible for setting up monitoring of the application's logs.
17
+ module Logging
18
+ if defined?(Rails) && defined?(Rails::Railtie)
19
+ # If we are in a Rails environment, setup the monitor daemon manager.
20
+ class RailTie < ::Rails::Railtie
21
+ initializer 'scout_apm_logging.monitor' do
22
+ context = ScoutApm::Logging::MonitorManager.instance.context
23
+
24
+ Loggers::Capture.new(context).capture_log_locations!
25
+
26
+ unless Utils.skip_setup?
27
+ Utils.attempt_exclusive_lock(context) do
28
+ ScoutApm::Logging::MonitorManager.instance.setup!
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ $LOAD_PATH.push File.expand_path('lib', __dir__)
4
+ require 'scout_apm/logging/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'scout_apm_logging'
8
+ s.version = ScoutApm::Logging::VERSION
9
+ s.authors = 'Scout APM'
10
+ s.email = ['support@scoutapp.com']
11
+ s.homepage = 'https://github.com/scoutapp/scout_apm_ruby_logging'
12
+ s.summary = 'Ruby Logging Support'
13
+ s.description = 'Sets up log monitoring for Scout APM Ruby clients.'
14
+ s.license = 'MIT'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.require_paths = ['lib']
19
+
20
+ s.required_ruby_version = '>= 2.6'
21
+
22
+ s.add_dependency 'scout_apm'
23
+
24
+ s.add_development_dependency 'rspec'
25
+ s.add_development_dependency 'rubocop', '1.50.2'
26
+ s.add_development_dependency 'rubocop-ast', '1.30.0'
27
+ end
@@ -0,0 +1,27 @@
1
+ common: &defaults
2
+ name: Scout APM
3
+ key: 000011110000
4
+ log_level: debug
5
+ monitor: true
6
+
7
+ ###
8
+ # Logging
9
+ ###
10
+ logs_monitor: true
11
+ logs_ingest_key: "00001000010000abc"
12
+ logs_monitored: ["/tmp/fake_log_file.log"]
13
+
14
+ production:
15
+ <<: *defaults
16
+ name: APM Test Conf (Production)
17
+
18
+ development:
19
+ <<: *defaults
20
+ name: APM Test Conf (Development)
21
+ host: http://localhost:3000
22
+ monitor: true
23
+
24
+ test:
25
+ <<: *defaults
26
+ name: APM Test Conf (Test)
27
+ monitor: false
File without changes
@@ -0,0 +1,3 @@
1
+ extensions:
2
+ file_storage/otc:
3
+ directory: /dev/null
@@ -0,0 +1,29 @@
1
+ common: &defaults
2
+ name: Scout APM
3
+ key: 000011110000
4
+ log_level: debug
5
+ monitor: true
6
+
7
+ ###
8
+ # Logging
9
+ ###
10
+ logs_monitor: true
11
+ logs_ingest_key: "00001000010000abc"
12
+ logs_monitored: ["/tmp/fake_log_file.log"]
13
+ # Need to give a high enough number for the original health check to pass
14
+ monitor_interval: 10
15
+
16
+ production:
17
+ <<: *defaults
18
+ name: APM Test Conf (Production)
19
+
20
+ development:
21
+ <<: *defaults
22
+ name: APM Test Conf (Development)
23
+ host: http://localhost:3000
24
+ monitor: true
25
+
26
+ test:
27
+ <<: *defaults
28
+ name: APM Test Conf (Test)
29
+ monitor: false
@@ -0,0 +1,3 @@
1
+ {
2
+ "health_check_port": 1234
3
+ }