scout_apm_logging 0.0.13 → 1.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.
- checksums.yaml +4 -4
- data/.gitignore +0 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/NOTICE +4 -0
- data/lib/scout_apm/logging/config.rb +5 -131
- data/lib/scout_apm/logging/loggers/capture.rb +3 -2
- data/lib/scout_apm/logging/loggers/formatter.rb +21 -2
- data/lib/scout_apm/logging/loggers/opentelemetry/LICENSE +201 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/NOTICE +9 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/log_record.rb +18 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/logger.rb +64 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/logger_provider.rb +31 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/severity_number.rb +43 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs/version.rb +18 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/api/logs.rb +28 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/exporter/otlp/logs_exporter.rb +389 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/exporter/otlp/version.rb +20 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/collector/logs/v1/logs_service_pb.rb +43 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/common/v1/common_pb.rb +58 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/logs/v1/logs_pb.rb +91 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/exporter/proto/resource/v1/resource_pb.rb +33 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/opentelemetry.rb +62 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export/batch_log_record_processor.rb +225 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export/log_record_exporter.rb +64 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/export.rb +34 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record.rb +115 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record_data.rb +31 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/log_record_processor.rb +53 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/logger.rb +94 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/logger_provider.rb +158 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs/version.rb +20 -0
- data/lib/scout_apm/logging/loggers/opentelemetry/sdk/logs.rb +28 -0
- data/lib/scout_apm/logging/utils.rb +0 -69
- data/lib/scout_apm/logging/version.rb +1 -1
- data/lib/scout_apm_logging.rb +2 -11
- data/scout_apm_logging.gemspec +7 -0
- data/spec/data/config_test_1.yml +0 -1
- data/spec/data/mock_config.yml +0 -3
- data/spec/integration/rails/lifecycle_spec.rb +57 -23
- data/spec/spec_helper.rb +0 -12
- data/spec/unit/config_spec.rb +0 -12
- data/spec/unit/loggers/capture_spec.rb +0 -6
- metadata +126 -39
- data/bin/scout_apm_logging_monitor +0 -6
- data/lib/scout_apm/logging/monitor/_rails.rb +0 -22
- data/lib/scout_apm/logging/monitor/collector/checksum.rb +0 -51
- data/lib/scout_apm/logging/monitor/collector/configuration.rb +0 -150
- data/lib/scout_apm/logging/monitor/collector/downloader.rb +0 -78
- data/lib/scout_apm/logging/monitor/collector/extractor.rb +0 -37
- data/lib/scout_apm/logging/monitor/collector/manager.rb +0 -57
- data/lib/scout_apm/logging/monitor/monitor.rb +0 -216
- data/lib/scout_apm/logging/monitor_manager/manager.rb +0 -162
- data/lib/scout_apm/logging/state.rb +0 -69
- data/spec/data/empty_logs_config.yml +0 -0
- data/spec/data/logs_config.yml +0 -3
- data/spec/data/state_file.json +0 -3
- data/spec/integration/loggers/capture_spec.rb +0 -68
- data/spec/integration/monitor/collector/downloader/will_verify_checksum.rb +0 -49
- data/spec/integration/monitor/collector_healthcheck_spec.rb +0 -29
- data/spec/integration/monitor/continuous_state_collector_spec.rb +0 -31
- data/spec/integration/monitor/previous_collector_setup_spec.rb +0 -45
- data/spec/integration/monitor_manager/disable_agent_spec.rb +0 -30
- data/spec/integration/monitor_manager/monitor_pid_file_spec.rb +0 -38
- data/spec/integration/monitor_manager/single_monitor_spec.rb +0 -53
- data/spec/unit/monitor/collector/configuration_spec.rb +0 -64
- data/spec/unit/state_spec.rb +0 -20
- 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
|
data/lib/scout_apm_logging.rb
CHANGED
@@ -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 =
|
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
|
data/scout_apm_logging.gemspec
CHANGED
@@ -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
|
data/spec/data/config_test_1.yml
CHANGED
data/spec/data/mock_config.yml
CHANGED
@@ -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::
|
9
|
-
|
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
|
-
|
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
|
-
|
57
|
+
local_messages = lines.map { |item| item['msg'] }
|
45
58
|
|
46
|
-
# Verify we have all the logs
|
47
|
-
expect(
|
48
|
-
expect(
|
49
|
-
expect(
|
50
|
-
expect(
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
data/spec/unit/config_spec.rb
CHANGED
@@ -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')
|