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.
- checksums.yaml +4 -4
- data/.gitignore +0 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +3 -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.18'
|
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')
|