scout_apm_logging 0.0.13 → 1.0.1

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 +6 -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
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module ScoutApm
8
+ module Logging
9
+ module Loggers
10
+ module OpenTelemetry
11
+ module SDK
12
+ module Logs
13
+ # The SDK implementation of OpenTelemetry::Logs::Logger
14
+ class Logger < OpenTelemetry::Logs::Logger
15
+ # @api private
16
+ #
17
+ # Returns a new {OpenTelemetry::SDK::Logs::Logger} instance. This should
18
+ # not be called directly. New loggers should be created using
19
+ # {LoggerProvider#logger}.
20
+ #
21
+ # @param [String] name Instrumentation package name
22
+ # @param [String] version Instrumentation package version
23
+ # @param [LoggerProvider] logger_provider The {LoggerProvider} that
24
+ # initialized the logger
25
+ #
26
+ # @return [OpenTelemetry::SDK::Logs::Logger]
27
+ def initialize(name, version, logger_provider)
28
+ @instrumentation_scope = ::OpenTelemetry::SDK::InstrumentationScope.new(name, version)
29
+ @logger_provider = logger_provider
30
+ end
31
+
32
+ # Emit a {LogRecord} to the processing pipeline.
33
+ #
34
+ # @param [optional Time] timestamp Time when the event occurred.
35
+ # @param [optional Time] observed_timestamp Time when the event was
36
+ # observed by the collection system.
37
+ # @param [optional OpenTelemetry::Trace::SpanContext] span_context The
38
+ # OpenTelemetry::Trace::SpanContext to associate with the
39
+ # {LogRecord}.
40
+ # @param severity_number [optional Integer] Numerical value of the
41
+ # severity. Smaller numerical values correspond to less severe events
42
+ # (such as debug events), larger numerical values correspond to more
43
+ # severe events (such as errors and critical events).
44
+ # @param [optional String, Numeric, Boolean, Array<String, Numeric,
45
+ # Boolean>, Hash{String => String, Numeric, Boolean, Array<String,
46
+ # Numeric, Boolean>}] body A value containing the body of the log record.
47
+ # @param [optional Hash{String => String, Numeric, Boolean,
48
+ # Array<String, Numeric, Boolean>}] attributes Additional information
49
+ # about the event.
50
+ # @param [optional String (16-byte binary)] trace_id Request trace id as
51
+ # defined in {https://www.w3.org/TR/trace-context/#trace-id W3C Trace Context}.
52
+ # Can be set for logs that are part of request processing and have an
53
+ # assigned trace id.
54
+ # @param [optional String (8-byte binary)] span_id Span id. Can be set
55
+ # for logs that are part of a particular processing span. If span_id
56
+ # is present trace_id should also be present.
57
+ # @param [optional Integer (8-bit byte of bit flags)] trace_flags Trace
58
+ # flag as defined in {https://www.w3.org/TR/trace-context/#trace-flags W3C Trace Context}
59
+ # specification. At the time of writing the specification defines one
60
+ # flag - the SAMPLED flag.
61
+ # @param [optional OpenTelemetry::Context] context The OpenTelemetry::Context
62
+ # to associate with the {LogRecord}.
63
+ #
64
+ # @api public
65
+ def on_emit(timestamp: nil,
66
+ observed_timestamp: Time.now,
67
+ severity_text: nil,
68
+ severity_number: nil,
69
+ body: nil,
70
+ attributes: nil,
71
+ trace_id: nil,
72
+ span_id: nil,
73
+ trace_flags: nil,
74
+ context: ::OpenTelemetry::Context.current)
75
+
76
+ @logger_provider.on_emit(timestamp: timestamp,
77
+ observed_timestamp: observed_timestamp,
78
+ severity_text: severity_text,
79
+ severity_number: severity_number,
80
+ body: body,
81
+ attributes: attributes,
82
+ trace_id: nil,
83
+ span_id: nil,
84
+ trace_flags: nil,
85
+ instrumentation_scope: @instrumentation_scope,
86
+ context: context)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module ScoutApm
8
+ module Logging
9
+ module Loggers
10
+ module OpenTelemetry
11
+ module SDK
12
+ module Logs
13
+ # The SDK implementation of OpenTelemetry::Logs::LoggerProvider.
14
+ class LoggerProvider < OpenTelemetry::Logs::LoggerProvider
15
+ UNEXPECTED_ERROR_MESSAGE = 'unexpected error in ' \
16
+ 'OpenTelemetry::SDK::Logs::LoggerProvider#%s'
17
+
18
+ private_constant :UNEXPECTED_ERROR_MESSAGE
19
+
20
+ # Returns a new LoggerProvider instance.
21
+ #
22
+ # @param [optional Resource] resource The resource to associate with
23
+ # new LogRecords created by {Logger}s created by this LoggerProvider.
24
+ #
25
+ # @return [OpenTelemetry::SDK::Logs::LoggerProvider]
26
+ def initialize(resource: OpenTelemetry::SDK::Resources::Resource.create)
27
+ @log_record_processors = []
28
+ @mutex = Mutex.new
29
+ @resource = resource
30
+ @stopped = false
31
+ end
32
+
33
+ # Returns an {OpenTelemetry::SDK::Logs::Logger} instance.
34
+ #
35
+ # @param [String] name Instrumentation package name
36
+ # @param [optional String] version Instrumentation package version
37
+ #
38
+ # @return [OpenTelemetry::SDK::Logs::Logger]
39
+ def logger(name:, version: nil)
40
+ version ||= ''
41
+
42
+ if !name.is_a?(String) || name.empty?
43
+ OpenTelemetry.logger.warn('LoggerProvider#logger called with an ' \
44
+ "invalid name. Name provided: #{name.inspect}")
45
+ end
46
+
47
+ Logger.new(name, version, self)
48
+ end
49
+
50
+ # Adds a new log record processor to this LoggerProvider's
51
+ # log_record_processors.
52
+ #
53
+ # @param [LogRecordProcessor] log_record_processor The
54
+ # {LogRecordProcessor} to add to this LoggerProvider.
55
+ def add_log_record_processor(log_record_processor)
56
+ @mutex.synchronize do
57
+ if @stopped
58
+ OpenTelemetry.logger.warn('calling LoggerProvider#' \
59
+ 'add_log_record_processor after shutdown.')
60
+ return
61
+ end
62
+ @log_record_processors = @log_record_processors.dup.push(log_record_processor)
63
+ end
64
+ end
65
+
66
+ # Attempts to stop all the activity for this LoggerProvider. Calls
67
+ # {LogRecordProcessor#shutdown} for all registered {LogRecordProcessor}s.
68
+ #
69
+ # This operation may block until all log records are processed. Must
70
+ # be called before turning off the main application to ensure all data
71
+ # are processed and exported.
72
+ #
73
+ # After this is called all newly created {LogRecord}s will be no-op.
74
+ #
75
+ # @param [optional Numeric] timeout An optional timeout in seconds.
76
+ # @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
77
+ # a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
78
+ def shutdown(timeout: nil)
79
+ @mutex.synchronize do
80
+ if @stopped
81
+ OpenTelemetry.logger.warn('LoggerProvider#shutdown called multiple times.')
82
+ return Export::FAILURE
83
+ end
84
+
85
+ start_time = OpenTelemetry::Common::Utilities.timeout_timestamp
86
+ results = @log_record_processors.map do |processor|
87
+ remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)
88
+ break [Export::TIMEOUT] if remaining_timeout&.zero?
89
+
90
+ processor.shutdown(timeout: remaining_timeout)
91
+ end
92
+
93
+ @stopped = true
94
+ results.max || Export::SUCCESS
95
+ end
96
+ end
97
+
98
+ # Immediately export all {LogRecord}s that have not yet been exported
99
+ # for all the registered {LogRecordProcessor}s.
100
+ #
101
+ # This method should only be called in cases where it is absolutely
102
+ # necessary, such as when using some FaaS providers that may suspend
103
+ # the process after an invocation, but before the {LogRecordProcessor}
104
+ # exports the completed {LogRecord}s.
105
+ #
106
+ # @param [optional Numeric] timeout An optional timeout in seconds.
107
+ # @return [Integer] Export::SUCCESS if no error occurred, Export::FAILURE if
108
+ # a non-specific failure occurred, Export::TIMEOUT if a timeout occurred.
109
+ def force_flush(timeout: nil)
110
+ @mutex.synchronize do
111
+ return Export::SUCCESS if @stopped
112
+
113
+ start_time = OpenTelemetry::Common::Utilities.timeout_timestamp
114
+ results = @log_record_processors.map do |processor|
115
+ remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)
116
+ return Export::TIMEOUT if remaining_timeout&.zero?
117
+
118
+ processor.force_flush(timeout: remaining_timeout)
119
+ end
120
+
121
+ results.max || Export::SUCCESS
122
+ end
123
+ end
124
+
125
+ # @api private
126
+ def on_emit(timestamp: nil,
127
+ observed_timestamp: nil,
128
+ severity_text: nil,
129
+ severity_number: nil,
130
+ body: nil,
131
+ attributes: nil,
132
+ trace_id: nil,
133
+ span_id: nil,
134
+ trace_flags: nil,
135
+ instrumentation_scope: nil,
136
+ context: nil)
137
+
138
+ log_record = LogRecord.new(timestamp: timestamp,
139
+ observed_timestamp: observed_timestamp,
140
+ severity_text: severity_text,
141
+ severity_number: severity_number,
142
+ body: body,
143
+ attributes: attributes,
144
+ trace_id: trace_id,
145
+ span_id: span_id,
146
+ trace_flags: trace_flags,
147
+ resource: @resource,
148
+ instrumentation_scope: instrumentation_scope)
149
+
150
+ @log_record_processors.each { |processor| processor.on_emit(log_record, context) }
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module ScoutApm
8
+ module Logging
9
+ module Loggers
10
+ module OpenTelemetry
11
+ module SDK
12
+ module Logs
13
+ # Current OpenTelemetry logs sdk version
14
+ VERSION = '0.1.0'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ require_relative 'logs/version'
8
+ require_relative 'logs/logger'
9
+ require_relative 'logs/logger_provider'
10
+ require_relative 'logs/log_record'
11
+ require_relative 'logs/log_record_data'
12
+ require_relative 'logs/log_record_processor'
13
+ require_relative 'logs/export'
14
+
15
+ module ScoutApm
16
+ module Logging
17
+ module Loggers
18
+ module OpenTelemetry
19
+ module SDK
20
+ # The Logs module contains the OpenTelemetry logs reference
21
+ # implementation.
22
+ module Logs
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -12,75 +12,6 @@ module ScoutApm
12
12
 
13
13
  FileUtils.mkdir_p(file_path) unless File.directory?(file_path)
14
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
15
  end
85
16
  end
86
17
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ScoutApm
4
4
  module Logging
5
- VERSION = '0.0.13'
5
+ VERSION = '1.0.1'
6
6
  end
7
7
  end
@@ -6,12 +6,8 @@ require 'scout_apm/logging/config'
6
6
  require 'scout_apm/logging/logger'
7
7
  require 'scout_apm/logging/context'
8
8
  require 'scout_apm/logging/utils'
9
- require 'scout_apm/logging/state'
10
-
11
9
  require 'scout_apm/logging/loggers/capture'
12
10
 
13
- require 'scout_apm/logging/monitor_manager/manager'
14
-
15
11
  module ScoutApm
16
12
  ## This module is responsible for setting up monitoring of the application's logs.
17
13
  module Logging
@@ -19,15 +15,10 @@ module ScoutApm
19
15
  # If we are in a Rails environment, setup the monitor daemon manager.
20
16
  class RailTie < ::Rails::Railtie
21
17
  initializer 'scout_apm_logging.monitor', after: :initialize_logger, before: :initialize_cache do
22
- context = ScoutApm::Logging::MonitorManager.instance.context
18
+ context = Context.new
19
+ context.config = Config.with_file(context, context.config.value('config_file'))
23
20
 
24
21
  Loggers::Capture.new(context).setup!
25
-
26
- unless Utils.skip_setup?
27
- Utils.attempt_exclusive_lock(context) do
28
- ScoutApm::Logging::MonitorManager.instance.setup!
29
- end
30
- end
31
22
  end
32
23
  end
33
24
  end
@@ -17,9 +17,16 @@ Gem::Specification.new do |s|
17
17
 
18
18
  s.required_ruby_version = '>= 2.6'
19
19
 
20
+ s.add_dependency 'googleapis-common-protos-types'
21
+ s.add_dependency 'google-protobuf', '~> 3.0'
22
+ s.add_dependency 'opentelemetry-api'
23
+ s.add_dependency 'opentelemetry-common'
24
+ s.add_dependency 'opentelemetry-instrumentation-base'
25
+ s.add_dependency 'opentelemetry-sdk', '>= 1.2'
20
26
  s.add_dependency 'scout_apm'
21
27
 
22
28
  s.add_development_dependency 'rspec'
23
29
  s.add_development_dependency 'rubocop', '1.50.2'
24
30
  s.add_development_dependency 'rubocop-ast', '1.30.0'
31
+ s.add_development_dependency 'webmock'
25
32
  end
@@ -9,7 +9,6 @@ common: &defaults
9
9
  ###
10
10
  logs_monitor: true
11
11
  logs_ingest_key: "00001000010000abc"
12
- logs_monitored: ["/tmp/fake_log_file.log"]
13
12
 
14
13
  production:
15
14
  <<: *defaults
@@ -9,9 +9,6 @@ common: &defaults
9
9
  ###
10
10
  logs_monitor: true
11
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
12
 
16
13
  production:
17
14
  <<: *defaults
@@ -1,32 +1,45 @@
1
+ require 'webmock/rspec'
2
+
1
3
  require 'spec_helper'
4
+ require 'zlib'
5
+ require 'stringio'
2
6
  require_relative '../../rails/app'
3
7
 
4
8
  describe ScoutApm::Logging do
9
+ before do
10
+ @file_path = '/app/response_body.txt'
11
+ # Capture the outgoing HTTP request to inspect its values
12
+ stub_request(:post, 'https://otlp-devel.scoutotel.com:4318/v1/logs')
13
+ .to_return do |request|
14
+ # We have to write to the file, as we are applying the patch in the fork, and
15
+ # need to run the expectations in the main process.
16
+ File.open(@file_path, 'a+') do |file|
17
+ msgs = transform_body_to_msgs(request.body)
18
+ file.puts(msgs)
19
+ end
20
+ { body: '', headers: {}, status: 200 }
21
+ end
22
+ end
23
+
5
24
  it 'checks the Rails lifecycle for creating the daemon and collector processes' do
6
25
  ENV['SCOUT_LOGS_MONITOR'] = 'true'
26
+ ENV['SCOUT_LOGS_REPORTING_ENDPOINT_HTTP'] = 'https://otlp-devel.scoutotel.com:4318/v1/logs'
7
27
 
8
- context = ScoutApm::Logging::MonitorManager.instance.context
9
- pid_file = context.config.value('monitor_pid_file')
10
- expect(File.exist?(pid_file)).to be_falsey
28
+ context = ScoutApm::Logging::Context.new
29
+ context.config = ScoutApm::Logging::Config.with_file(context, context.config.value('config_file'))
11
30
 
12
31
  rails_pid = fork do
13
32
  initialize_app
14
33
  end
15
34
 
16
- wait_for_process_with_timeout!('otelcol-contrib', 20)
17
-
18
- # Check if the monitor PID file exists
19
- expect(File.exist?(pid_file)).to be_truthy
20
-
21
- # Read the PID from the PID file
22
- pid = File.read(pid_file).to_i
23
-
24
- # Check if the process with the stored PID is running
25
- expect(ScoutApm::Logging::Utils.check_process_liveliness(pid, 'scout_apm_logging_monitor')).to be_truthy
35
+ # TODO: Improve check to wait for Rails initialization.
36
+ sleep 5
26
37
 
27
38
  # Call the app to generate the logs
28
39
  `curl localhost:8080`
29
40
 
41
+ sleep 5
42
+
30
43
  proxy_dir = context.config.value('logs_proxy_log_dir')
31
44
  files = Dir.entries(proxy_dir) - ['.', '..']
32
45
  log_file = File.join(proxy_dir, files[0])
@@ -41,24 +54,45 @@ describe ScoutApm::Logging do
41
54
  end
42
55
  end
43
56
 
44
- messages = lines.map { |item| item['msg'] }
57
+ local_messages = lines.map { |item| item['msg'] }
45
58
 
46
- # Verify we have all the logs
47
- expect(messages.count('[TEST] Some log')).to eq(1)
48
- expect(messages.count('[YIELD] Yield Test')).to eq(1)
49
- expect(messages.count('Another Log')).to eq(1)
50
- expect(messages.count('Should not be captured')).to eq(0)
59
+ # Verify we have all the logs in the local log file
60
+ expect(local_messages.count('[TEST] Some log')).to eq(1)
61
+ expect(local_messages.count('[YIELD] Yield Test')).to eq(1)
62
+ expect(local_messages.count('Another Log')).to eq(1)
63
+ expect(local_messages.count('Should not be captured')).to eq(0)
51
64
 
52
65
  log_locations = lines.map { |item| item['log_location'] }.compact
53
66
 
54
67
  # Verify that log attributes aren't persisted
55
68
  expect(log_locations.size).to eq(1)
56
69
 
70
+ # Verify the logs are sent to the receiver
71
+ receiver_contents = File.readlines(@file_path, chomp: true)
72
+ expect(receiver_contents.count('[TEST] Some log')).to eq(1)
73
+ expect(receiver_contents.count('[YIELD] Yield Test')).to eq(1)
74
+ expect(receiver_contents.count('Another Log')).to eq(1)
75
+ expect(receiver_contents.count('Should not be captured')).to eq(0)
76
+
57
77
  # Kill the rails process. We use kill as using any other signal throws a long log line.
58
78
  Process.kill('KILL', rails_pid)
59
- # Kill the process and ensure PID file clean up
60
- Process.kill('TERM', pid)
61
- sleep 1 # Give the process time to exit
62
- expect(File.exist?(pid_file)).to be_falsey
79
+ end
80
+
81
+ private
82
+
83
+ def transform_body_to_msgs(body)
84
+ gz = Zlib::GzipReader.new(StringIO.new(body))
85
+ uncompressed = gz.read
86
+
87
+ value = ScoutApm::Logging::Loggers::Opentelemetry::Proto::Collector::Logs::V1::ExportLogsServiceRequest.decode(uncompressed)
88
+ value_hash = value.to_h
89
+
90
+ value_hash[:resource_logs].map do |item|
91
+ item[:scope_logs].map do |sl|
92
+ sl[:log_records].map do |log|
93
+ log[:body][:string_value]
94
+ end
95
+ end
96
+ end.flatten
63
97
  end
64
98
  end
data/spec/spec_helper.rb CHANGED
@@ -23,20 +23,8 @@ end
23
23
  RSpec.configure do |config|
24
24
  ENV["SCOUT_LOG_FILE_PATH"] = "STDOUT"
25
25
  ENV["SCOUT_LOG_LEVEL"] = "debug"
26
- ENV["SCOUT_COLLECTOR_LOG_LEVEL"] = "info"
27
26
 
28
27
  config.after(:each) do
29
28
  RSpec::Mocks.space.reset_all
30
29
  end
31
30
  end
32
-
33
- def wait_for_process_with_timeout!(name, timeout_time)
34
- Timeout::timeout(timeout_time) do
35
- loop do
36
- break if `pgrep #{name} --runstates D,R,S`.strip != ""
37
- sleep 0.1
38
- end
39
- end
40
-
41
- sleep 1
42
- end
@@ -8,18 +8,6 @@ describe ScoutApm::Logging::Config do
8
8
 
9
9
  expect(conf.value('log_level')).to eq('debug')
10
10
  expect(conf.value('logs_monitor')).to eq(true)
11
- expect(conf.value('monitor_pid_file')).to eq('/tmp/scout_apm/scout_apm_log_monitor.pid')
12
11
  expect(conf.value('logs_ingest_key')).to eq('00001000010000abc')
13
- expect(conf.value('logs_monitored')).to eq(['/tmp/fake_log_file.log'])
14
- end
15
-
16
- it 'loads the state file into the config' do
17
- ENV['SCOUT_MONITOR_STATE_FILE'] = File.expand_path('../data/state_file.json', __dir__)
18
-
19
- context = ScoutApm::Logging::Context.new
20
- conf_file = File.expand_path('../data/config_test_1.yml', __dir__)
21
- conf = ScoutApm::Logging::Config.with_file(context, conf_file)
22
-
23
- expect(conf.value('health_check_port')).to eq(1234)
24
12
  end
25
13
  end
@@ -16,8 +16,6 @@ end
16
16
 
17
17
  describe ScoutApm::Logging::Loggers::Capture do
18
18
  it 'should swap the STDOUT logger and create a proxy logger' do
19
- ENV['SCOUT_MONITOR_INTERVAL'] = '10'
20
- ENV['SCOUT_MONITOR_INTERVAL_DELAY'] = '10'
21
19
  ENV['SCOUT_LOGS_MONITOR'] = 'true'
22
20
  ENV['SCOUT_LOGS_CAPTURE_LEVEL'] = 'debug'
23
21
 
@@ -47,10 +45,6 @@ describe ScoutApm::Logging::Loggers::Capture do
47
45
  # Shouldn't capture. While the log_capture_level was set to debug,
48
46
  # the original logger instance had a higher log level of info.
49
47
  expect(content).not_to include('SHOULD NOT CAPTURE')
50
-
51
- state_file = File.read(context.config.value('monitor_state_file'))
52
- state_data = JSON.parse(state_file)
53
- expect(state_data['logs_monitored']).to eq([log_path])
54
48
  end
55
49
 
56
50
  expect(output_from_log).to include('TEST')