scout_apm_logging 0.0.13 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -4
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +3 -0
  5. data/NOTICE +4 -0
  6. data/lib/scout_apm/logging/config.rb +5 -131
  7. data/lib/scout_apm/logging/loggers/capture.rb +3 -2
  8. data/lib/scout_apm/logging/loggers/formatter.rb +21 -2
  9. data/lib/scout_apm/logging/loggers/opentelemetry/LICENSE +201 -0
  10. data/lib/scout_apm/logging/loggers/opentelemetry/NOTICE +9 -0
  11. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/log_record.rb +18 -0
  12. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/logger.rb +64 -0
  13. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/logger_provider.rb +31 -0
  14. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/severity_number.rb +43 -0
  15. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/version.rb +18 -0
  16. data/lib/scout_apm/logging/loggers/opentelemetry/api/logs.rb +28 -0
  17. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/exporter/otlp/logs_exporter.rb +389 -0
  18. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/exporter/otlp/version.rb +20 -0
  19. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/collector/logs/v1/logs_service_pb.rb +43 -0
  20. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/common/v1/common_pb.rb +58 -0
  21. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/logs/v1/logs_pb.rb +91 -0
  22. data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/resource/v1/resource_pb.rb +33 -0
  23. data/lib/scout_apm/logging/loggers/opentelemetry/opentelemetry.rb +62 -0
  24. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export/batch_log_record_processor.rb +225 -0
  25. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export/log_record_exporter.rb +64 -0
  26. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export.rb +34 -0
  27. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record.rb +115 -0
  28. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record_data.rb +31 -0
  29. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record_processor.rb +53 -0
  30. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/logger.rb +94 -0
  31. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/logger_provider.rb +158 -0
  32. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/version.rb +20 -0
  33. data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs.rb +28 -0
  34. data/lib/scout_apm/logging/utils.rb +0 -69
  35. data/lib/scout_apm/logging/version.rb +1 -1
  36. data/lib/scout_apm_logging.rb +2 -11
  37. data/scout_apm_logging.gemspec +7 -0
  38. data/spec/data/config_test_1.yml +0 -1
  39. data/spec/data/mock_config.yml +0 -3
  40. data/spec/integration/rails/lifecycle_spec.rb +57 -23
  41. data/spec/spec_helper.rb +0 -12
  42. data/spec/unit/config_spec.rb +0 -12
  43. data/spec/unit/loggers/capture_spec.rb +0 -6
  44. metadata +126 -39
  45. data/bin/scout_apm_logging_monitor +0 -6
  46. data/lib/scout_apm/logging/monitor/_rails.rb +0 -22
  47. data/lib/scout_apm/logging/monitor/collector/checksum.rb +0 -51
  48. data/lib/scout_apm/logging/monitor/collector/configuration.rb +0 -150
  49. data/lib/scout_apm/logging/monitor/collector/downloader.rb +0 -78
  50. data/lib/scout_apm/logging/monitor/collector/extractor.rb +0 -37
  51. data/lib/scout_apm/logging/monitor/collector/manager.rb +0 -57
  52. data/lib/scout_apm/logging/monitor/monitor.rb +0 -216
  53. data/lib/scout_apm/logging/monitor_manager/manager.rb +0 -162
  54. data/lib/scout_apm/logging/state.rb +0 -69
  55. data/spec/data/empty_logs_config.yml +0 -0
  56. data/spec/data/logs_config.yml +0 -3
  57. data/spec/data/state_file.json +0 -3
  58. data/spec/integration/loggers/capture_spec.rb +0 -68
  59. data/spec/integration/monitor/collector/downloader/will_verify_checksum.rb +0 -49
  60. data/spec/integration/monitor/collector_healthcheck_spec.rb +0 -29
  61. data/spec/integration/monitor/continuous_state_collector_spec.rb +0 -31
  62. data/spec/integration/monitor/previous_collector_setup_spec.rb +0 -45
  63. data/spec/integration/monitor_manager/disable_agent_spec.rb +0 -30
  64. data/spec/integration/monitor_manager/monitor_pid_file_spec.rb +0 -38
  65. data/spec/integration/monitor_manager/single_monitor_spec.rb +0 -53
  66. data/spec/unit/monitor/collector/configuration_spec.rb +0 -64
  67. data/spec/unit/state_spec.rb +0 -20
  68. data/tooling/checksums.rb +0 -106
@@ -1,216 +0,0 @@
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
- context.logger.debug('Monitor instance created')
33
-
34
- context.application_root = $stdin.gets&.chomp
35
-
36
- # Load in the dynamic and state based config settings.
37
- context.config = Config.with_file(context, determine_scout_config_filepath)
38
-
39
- daemonize_process!
40
- end
41
-
42
- def setup!
43
- context.config.logger.info('Monitor daemon process started')
44
-
45
- add_exit_handler!
46
-
47
- unless has_logs_to_monitor?
48
- context.config.logger.warn('No logs are set to be monitored. Please set the `logs_monitored` config setting. Exiting.')
49
- return
50
- end
51
-
52
- initiate_collector_setup! unless has_previous_collector_setup?
53
-
54
- @latest_state_sha = get_state_file_sha
55
-
56
- run!
57
- end
58
-
59
- def run!
60
- # Prevent the monitor from checking the collector health before it's fully started.
61
- # Having this be configurable is useful for testing.
62
- sleep context.config.value('monitor_interval_delay')
63
-
64
- loop do
65
- sleep context.config.value('monitor_interval')
66
-
67
- check_collector_health
68
-
69
- check_state_change
70
- end
71
- end
72
-
73
- # Only useful for testing.
74
- def config=(config)
75
- context.config = config
76
- end
77
-
78
- private
79
-
80
- def daemonize_process!
81
- # Similar to that of Process.daemon, but we want to keep the dir, STDOUT and STDERR.
82
- exit if fork
83
- Process.setsid
84
- exit if fork
85
- $stdin.reopen '/dev/null'
86
-
87
- context.logger.debug("Monitor process daemonized, PID: #{Process.pid}")
88
- File.write(context.config.value('monitor_pid_file'), Process.pid)
89
- end
90
-
91
- def has_logs_to_monitor?
92
- context.config.value('logs_monitored').any?
93
- end
94
-
95
- def has_previous_collector_setup?
96
- return false unless context.config.value('health_check_port') != 0
97
-
98
- healthy_response = request_health_check_port("http://localhost:#{context.config.value('health_check_port')}/")
99
-
100
- if healthy_response
101
- context.logger.info("Collector already setup on port #{context.config.value('health_check_port')}")
102
- else
103
- context.logger.info('Setting up new collector')
104
- end
105
-
106
- healthy_response
107
- end
108
-
109
- def initiate_collector_setup!
110
- set_health_check_port!
111
-
112
- Collector::Manager.new(context).setup!
113
- end
114
-
115
- def is_port_available?(port)
116
- socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
117
- remote_address = Socket.sockaddr_in(port, '127.0.0.1')
118
-
119
- begin
120
- socket.connect_nonblock(remote_address)
121
- rescue Errno::EINPROGRESS
122
- IO.select(nil, [socket])
123
- retry
124
- rescue Errno::EISCONN, Errno::ECONNRESET
125
- false
126
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
127
- true
128
- ensure
129
- socket.close if socket && !socket.closed?
130
- end
131
- end
132
-
133
- def set_health_check_port!
134
- health_check_port = 13_133
135
- until is_port_available?(health_check_port)
136
- sleep 0.1
137
- health_check_port += 1
138
- end
139
-
140
- Config::ConfigDynamic.set_value('health_check_port', health_check_port)
141
- context.config.state.flush_state!
142
- end
143
-
144
- def request_health_check_port(endpoint)
145
- uri = URI(endpoint)
146
-
147
- begin
148
- response = Net::HTTP.get_response(uri)
149
-
150
- unless response.is_a?(Net::HTTPSuccess)
151
- context.logger.error("Error occurred while checking collector health: #{response.message}")
152
- return false
153
- end
154
- rescue StandardError => e
155
- context.logger.error("Error occurred while checking collector health: #{e.message}")
156
- return false
157
- end
158
-
159
- true
160
- end
161
-
162
- def check_collector_health
163
- context.logger.debug('Checking collector health')
164
- collector_health_endpoint = "http://localhost:#{context.config.value('health_check_port')}/"
165
-
166
- healthy_response = request_health_check_port(collector_health_endpoint)
167
-
168
- initiate_collector_setup! unless healthy_response
169
- end
170
-
171
- def remove_collector_process # rubocop:disable Metrics/AbcSize
172
- return unless File.exist? context.config.value('collector_pid_file')
173
-
174
- process_id = File.read(context.config.value('collector_pid_file'))
175
- return if process_id.empty?
176
-
177
- begin
178
- Process.kill('TERM', process_id.to_i)
179
- rescue Errno::ENOENT, Errno::ESRCH => e
180
- context.logger.error("Error occurred while removing collector process from monitor: #{e.message}")
181
- ensure
182
- File.delete(context.config.value('collector_pid_file'))
183
- end
184
- end
185
-
186
- def check_state_change
187
- current_sha = get_state_file_sha
188
-
189
- return if current_sha == latest_state_sha
190
-
191
- remove_collector_process
192
- initiate_collector_setup!
193
-
194
- # File SHA can change due to port mappings on collector setup.
195
- @latest_state_sha = get_state_file_sha
196
- end
197
-
198
- def add_exit_handler!
199
- at_exit do
200
- # There may not be a file to delete, as the monitor manager ensures cleaning it up when monitoring is disabled.
201
- File.delete(context.config.value('monitor_pid_file')) if File.exist?(context.config.value('monitor_pid_file'))
202
- end
203
- end
204
-
205
- def get_state_file_sha
206
- return nil unless File.exist?(context.config.value('monitor_state_file'))
207
-
208
- `sha256sum #{context.config.value('monitor_state_file')}`.split(' ').first
209
- end
210
-
211
- def determine_scout_config_filepath
212
- "#{context.application_root}/config/scout_apm.yml"
213
- end
214
- end
215
- end
216
- end
@@ -1,162 +0,0 @@
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
- end
28
-
29
- def determine_configuration_state
30
- monitoring_enabled = context.config.value('logs_monitor')
31
-
32
- if monitoring_enabled
33
- context.logger.info('Log monitoring enabled')
34
- create_process
35
-
36
- # Continue to hold the lock until we have written the PID file.
37
- ensure_monitor_pid_file_exists
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
- # With the use of unicorn and puma worker killer, we want to ensure we only restart (exit and
51
- # eventually start) the monitor and collector when the main process exits, and not the workers.
52
- initialized_process_id = Process.pid
53
- at_exit do
54
- # Only remove/restart the monitor and collector if we are exiting from an app_server process.
55
- # We need to wait on this check, as the process command line changes at some point.
56
- if Utils.current_process_is_app_server? && Process.pid == initialized_process_id
57
- context.logger.debug('Exiting from app server process. Removing monitor and collector processes.')
58
- remove_processes
59
- end
60
- end
61
- end
62
-
63
- def create_process
64
- return if process_exists?
65
-
66
- Utils.ensure_directory_exists(context.config.value('monitor_pid_file'))
67
-
68
- reader, writer = IO.pipe
69
-
70
- gem_directory = File.expand_path('../../../..', __dir__)
71
-
72
- # As we daemonize the process, we will write to the pid file within the process.
73
- pid = Process.spawn("ruby #{gem_directory}/bin/scout_apm_logging_monitor", in: reader)
74
-
75
- reader.close
76
- # TODO: Add support for Sinatra.
77
- writer.puts Rails.root if defined?(Rails)
78
- writer.close
79
- # Block until we have spawned the process and forked. This is to ensure
80
- # we keep the exclusive lock until the process has written the PID file.
81
- Process.wait(pid)
82
- end
83
-
84
- private
85
-
86
- def ensure_monitor_pid_file_exists
87
- start_time = Time.now
88
- # We don't want to hold up the initial Rails boot time for very long.
89
- timeout_seconds = 0.1
90
-
91
- # Naive benchmarks show this taking ~0.01 seconds.
92
- loop do
93
- if File.exist?(context.config.value('monitor_pid_file'))
94
- context.logger.debug('Monitor PID file exists. Releasing lock.')
95
- break
96
- end
97
-
98
- if Time.now - start_time > timeout_seconds
99
- context.logger.warn('Unable to verify monitor PID file write. Releasing lock.')
100
- break
101
- end
102
-
103
- sleep 0.01
104
- end
105
- end
106
-
107
- def process_exists?
108
- return false unless File.exist? context.config.value('monitor_pid_file')
109
-
110
- process_id = File.read(context.config.value('monitor_pid_file'))
111
- return false if process_id.empty?
112
-
113
- process_exists = Utils.check_process_liveliness(process_id.to_i, 'scout_apm_logging_monitor')
114
- File.delete(context.config.value('monitor_pid_file')) unless process_exists
115
-
116
- process_exists
117
- end
118
-
119
- def remove_monitor_process # rubocop:disable Metrics/AbcSize
120
- return unless File.exist? context.config.value('monitor_pid_file')
121
-
122
- process_id = File.read(context.config.value('monitor_pid_file'))
123
- return if process_id.empty?
124
-
125
- begin
126
- Process.kill('TERM', process_id.to_i)
127
- rescue Errno::ENOENT, Errno::ESRCH => e
128
- context.logger.error("Error occurred while removing monitor process: #{e.message}")
129
- File.delete(context.config.value('monitor_pid_file'))
130
- end
131
- end
132
-
133
- def remove_collector_process # rubocop:disable Metrics/AbcSize
134
- return unless File.exist? context.config.value('collector_pid_file')
135
-
136
- process_id = File.read(context.config.value('collector_pid_file'))
137
- return if process_id.empty?
138
-
139
- begin
140
- Process.kill('TERM', process_id.to_i)
141
- rescue Errno::ENOENT, Errno::ESRCH => e
142
- context.logger.error("Error occurred while removing collector process from manager: #{e.message}")
143
- ensure
144
- File.delete(context.config.value('collector_pid_file'))
145
- end
146
- end
147
-
148
- def remove_data_file
149
- return unless File.exist? context.config.value('monitor_state_file')
150
-
151
- File.delete(context.config.value('monitor_state_file'))
152
- end
153
-
154
- # Remove both the monitor and collector processes that we have spawned.
155
- def remove_processes
156
- remove_monitor_process
157
- remove_collector_process
158
- remove_data_file
159
- end
160
- end
161
- end
162
- end
@@ -1,69 +0,0 @@
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
- contents = file.read
32
- old_log_state_files = if contents.empty?
33
- []
34
- else
35
- current_data = JSON.parse(contents)
36
- current_data['logs_monitored']
37
- end
38
-
39
- data['logs_monitored'] =
40
- merge_and_dedup_log_locations(updated_log_locations, old_log_state_files, data['logs_monitored'])
41
-
42
- file.rewind # Move cursor to beginning of the file
43
- file.truncate(0) # Truncate existing content
44
- file.write(JSON.pretty_generate(data))
45
- rescue StandardError => e
46
- context.logger.error("Error occurred while flushing state to file: #{e.message}. Unlocking.")
47
- ensure
48
- file.flock(File::LOCK_UN)
49
- end
50
- end
51
-
52
- private
53
-
54
- # Should we add better detection for similar basenames but different paths?
55
- # May be a bit tricky with tools like capistrano and releases paths differentiated by time.
56
- def merge_and_dedup_log_locations(*log_locations)
57
- # Take the new logs if duplication (those first passed in the args) as we could be in a newer release.
58
- logs = log_locations.reduce([], :concat)
59
- merged = logs.each_with_object({}) do |log_path, hash|
60
- base_name = File.basename(log_path)
61
- hash[base_name] ||= log_path
62
- end
63
-
64
- merged.values
65
- end
66
- end
67
- end
68
- end
69
- end
File without changes
@@ -1,3 +0,0 @@
1
- extensions:
2
- file_storage/otc:
3
- directory: /dev/null
@@ -1,3 +0,0 @@
1
- {
2
- "health_check_port": 1234
3
- }
@@ -1,68 +0,0 @@
1
- require 'logger'
2
-
3
- require 'spec_helper'
4
-
5
- require_relative '../../../lib/scout_apm/logging/loggers/capture'
6
-
7
- describe ScoutApm::Logging::Loggers::Capture do
8
- it 'should find the logger, capture the log destination, and rotate collector configs' do
9
- ENV['SCOUT_MONITOR_INTERVAL'] = '10'
10
- ENV['SCOUT_MONITOR_INTERVAL_DELAY'] = '10'
11
- ENV['SCOUT_LOGS_MONITOR'] = 'true'
12
-
13
- context = ScoutApm::Logging::MonitorManager.instance.context
14
-
15
- state_file_location = context.config.value('monitor_state_file')
16
- collector_pid_location = context.config.value('collector_pid_file')
17
- ScoutApm::Logging::Utils.ensure_directory_exists(state_file_location)
18
-
19
- first_logger = ScoutTestLogger.new('/tmp/first_file.log')
20
- first_logger_basename = File.basename(first_logger.instance_variable_get(:@logdev).filename.to_s)
21
- first_logger_updated_path = File.join(context.config.value('logs_proxy_log_dir'), first_logger_basename)
22
- TestLoggerWrapper.logger = first_logger
23
-
24
- similuate_railtie
25
-
26
- # Give the process time to initialize, download the collector, and start it
27
- wait_for_process_with_timeout!('otelcol-contrib', 20)
28
-
29
- expect(`pgrep otelcol-contrib --runstates D,R,S`).not_to be_empty
30
- collector_pid = File.read(collector_pid_location)
31
-
32
- content = File.read(state_file_location)
33
- data = JSON.parse(content)
34
- expect(data['logs_monitored']).to eq([first_logger_updated_path])
35
-
36
- second_logger = ScoutTestLogger.new('/tmp/second_file.log')
37
- second_logger_basename = File.basename(second_logger.instance_variable_get(:@logdev).filename.to_s)
38
- second_logger_updated_path = File.join(context.config.value('logs_proxy_log_dir'), second_logger_basename)
39
- TestLoggerWrapper.logger = second_logger
40
-
41
- similuate_railtie
42
-
43
- content = File.read(state_file_location)
44
- data = JSON.parse(content)
45
-
46
- expect(data['logs_monitored'].sort).to eq([first_logger_updated_path, second_logger_updated_path])
47
-
48
- # Need to wait for the delay first health check, next monitor interval to restart the collector, and then for
49
- # the collector to restart
50
- sleep 25
51
- wait_for_process_with_timeout!('otelcol-contrib', 20)
52
-
53
- expect(`pgrep otelcol-contrib --runstates D,R,S`).not_to be_empty
54
- new_collector_pid = File.read(collector_pid_location)
55
-
56
- # Should have restarted the collector based on the change
57
- expect(new_collector_pid).not_to eq(collector_pid)
58
- end
59
-
60
- private
61
-
62
- def similuate_railtie
63
- context = ScoutApm::Logging::MonitorManager.instance.context
64
-
65
- ScoutApm::Logging::Loggers::Capture.new(context).setup!
66
- ScoutApm::Logging::MonitorManager.new.setup!
67
- end
68
- end
@@ -1,49 +0,0 @@
1
- require 'spec_helper'
2
-
3
- require_relative '../../../../../lib/scout_apm/logging/monitor/collector/downloader'
4
-
5
- describe ScoutApm::Logging::Collector::Downloader do
6
- it 'should validate checksum, and correct download if neccessary' do
7
- ENV['SCOUT_LOGS_MONITOR'] = 'true'
8
- ENV['SCOUT_LOGS_MONITORED'] = '["/tmp/test.log"]'
9
-
10
- otelcol_contrib_path = '/tmp/scout_apm/otelcol-contrib'
11
- ScoutApm::Logging::Utils.ensure_directory_exists(otelcol_contrib_path)
12
-
13
- File.write(otelcol_contrib_path, 'fake content')
14
-
15
- ScoutApm::Logging::MonitorManager.instance.context.logger.info "Time start: #{Time.now}"
16
- ScoutApm::Logging::MonitorManager.instance.setup!
17
- ScoutApm::Logging::MonitorManager.instance.context.logger.info "Time after setup: #{Time.now}"
18
-
19
- # Give the process time to initialize, download the collector, and start it
20
- wait_for_process_with_timeout!('otelcol-contrib', 20)
21
-
22
- download_time = File.mtime(otelcol_contrib_path)
23
-
24
- expect(`pgrep otelcol-contrib --runstates D,R,S`).not_to be_empty
25
-
26
- ENV['SCOUT_LOGS_MONITOR'] = 'false'
27
-
28
- ScoutApm::Logging::MonitorManager.new.setup!
29
-
30
- sleep 5 # Give the process time to exit
31
-
32
- expect(File.exist?(ScoutApm::Logging::MonitorManager.instance.context.config.value('monitor_pid_file'))).to be_falsey
33
- expect(`pgrep otelcol-contrib --runstates D,R,S`).to be_empty
34
- expect(`pgrep scout_apm_log_monitor --runstates D,R,S`).to be_empty
35
-
36
- ENV['SCOUT_LOGS_MONITOR'] = 'true'
37
-
38
- ScoutApm::Logging::MonitorManager.new.setup!
39
-
40
- # Give the process time to exit, and for the healthcheck to restart it
41
- wait_for_process_with_timeout!('otelcol-contrib', 30)
42
-
43
- expect(`pgrep otelcol-contrib --runstates D,R,S`).not_to be_empty
44
-
45
- recheck_time = File.mtime(otelcol_contrib_path)
46
-
47
- expect(download_time).to eq(recheck_time)
48
- end
49
- end
@@ -1,29 +0,0 @@
1
- require 'spec_helper'
2
-
3
- require_relative '../../../lib/scout_apm/logging/monitor/monitor'
4
-
5
- describe ScoutApm::Logging::Monitor do
6
- it 'should recreate collector process on healthcheck if it has exited' do
7
- ENV['SCOUT_MONITOR_INTERVAL'] = '10'
8
- ENV['SCOUT_MONITOR_INTERVAL_DELAY'] = '10'
9
- ENV['SCOUT_LOGS_MONITOR'] = 'true'
10
- ENV['SCOUT_LOGS_MONITORED'] = '["/tmp/test.log"]'
11
-
12
- ScoutApm::Logging::Utils.ensure_directory_exists('/tmp/scout_apm/scout_apm_log_monitor.pid')
13
-
14
- ScoutApm::Logging::MonitorManager.instance.context.logger.info "Time start: #{Time.now}"
15
- ScoutApm::Logging::MonitorManager.instance.setup!
16
- ScoutApm::Logging::MonitorManager.instance.context.logger.info "Time after setup: #{Time.now}"
17
-
18
- # Give the process time to initialize, download the collector, and start it
19
- wait_for_process_with_timeout!('otelcol-contrib', 20)
20
-
21
- expect(`pgrep otelcol-contrib --runstates D,R,S`).not_to be_empty
22
-
23
- # Bypass gracefull shutdown
24
- `pkill -9 otelcol-contrib`
25
-
26
- # Give the process time to exit, and for the healthcheck to restart it
27
- wait_for_process_with_timeout!('otelcol-contrib', 30)
28
- end
29
- end
@@ -1,31 +0,0 @@
1
- require 'spec_helper'
2
-
3
- require_relative '../../../lib/scout_apm/logging/monitor/monitor'
4
-
5
- describe ScoutApm::Logging::Monitor do
6
- it "Should not restart the collector if the state hasn't changed" do
7
- ENV['SCOUT_MONITOR_INTERVAL'] = '10'
8
- ENV['SCOUT_MONITOR_INTERVAL_DELAY'] = '10'
9
- ENV['SCOUT_LOGS_MONITOR'] = 'true'
10
- ENV['SCOUT_LOGS_MONITORED'] = '["/tmp/test.log"]'
11
-
12
- context = ScoutApm::Logging::MonitorManager.instance.context
13
- collector_pid_location = context.config.value('collector_pid_file')
14
- ScoutApm::Logging::MonitorManager.instance.context.logger.info "Time start: #{Time.now}"
15
- ScoutApm::Logging::MonitorManager.instance.setup!
16
- ScoutApm::Logging::MonitorManager.instance.context.logger.info "Time after setup: #{Time.now}"
17
- # Give the process time to initialize, download the collector, and start it
18
- wait_for_process_with_timeout!('otelcol-contrib', 20)
19
-
20
- expect(`pgrep otelcol-contrib --runstates D,R,S`).not_to be_empty
21
- collector_pid = File.read(collector_pid_location)
22
-
23
- # Give time for the monitor interval to run.
24
- sleep 30
25
-
26
- expect(`pgrep otelcol-contrib --runstates D,R,S`).not_to be_empty
27
- second_read_pid = File.read(collector_pid_location)
28
-
29
- expect(second_read_pid).to eq(collector_pid)
30
- end
31
- end
@@ -1,45 +0,0 @@
1
- require 'spec_helper'
2
-
3
- require_relative '../../../lib/scout_apm/logging/monitor/monitor'
4
-
5
- describe ScoutApm::Logging::Monitor do
6
- it 'should use previous collector setup if monitor daemon exits' do
7
- ENV['SCOUT_LOGS_MONITOR'] = 'true'
8
- ENV['SCOUT_LOGS_MONITORED'] = '["/tmp/test.log"]'
9
-
10
- monitor_pid_location = ScoutApm::Logging::MonitorManager.instance.context.config.value('monitor_pid_file')
11
- collector_pid_location = ScoutApm::Logging::MonitorManager.instance.context.config.value('collector_pid_file')
12
- ScoutApm::Logging::Utils.ensure_directory_exists(monitor_pid_location)
13
-
14
- ScoutApm::Logging::MonitorManager.instance.context.logger.info "Time start: #{Time.now}"
15
- ScoutApm::Logging::MonitorManager.instance.setup!
16
- ScoutApm::Logging::MonitorManager.instance.context.logger.info "Time after setup: #{Time.now}"
17
-
18
- # Give the process time to initialize, download the collector, and start it
19
- wait_for_process_with_timeout!('otelcol-contrib', 20)
20
-
21
- monitor_pid = File.read(monitor_pid_location)
22
-
23
- otelcol_pid = `pgrep otelcol-contrib --runstates D,R,S`.strip!
24
- stored_otelcol_pid = File.read(collector_pid_location)
25
- expect(otelcol_pid).to eq(stored_otelcol_pid)
26
-
27
- `kill -9 #{monitor_pid}`
28
-
29
- # Create a separate monitor manager instance, or else we won't reload
30
- # the configuraiton state.
31
- ScoutApm::Logging::MonitorManager.new.setup!
32
-
33
- sleep 5
34
-
35
- expect(`pgrep -f /app/bin/scout_apm_logging_monitor --runstates D,R,S`).not_to be_empty
36
-
37
- new_monitor_pid = File.read(monitor_pid_location)
38
- expect(new_monitor_pid).not_to eq(monitor_pid)
39
-
40
- should_be_same_otelcol_pid = `pgrep otelcol-contrib --runstates D,R,S`.strip!
41
- should_be_same_stored_otelcol_pid = File.read(collector_pid_location)
42
- expect(should_be_same_otelcol_pid).to eq(otelcol_pid)
43
- expect(should_be_same_stored_otelcol_pid).to eq(stored_otelcol_pid)
44
- end
45
- end