scout_apm_logging 0.0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/test.yml +37 -0
- data/.gitignore +13 -0
- data/.rubocop.yml +65 -0
- data/Dockerfile +18 -0
- data/Gemfile +5 -0
- data/README.md +58 -0
- data/Rakefile +35 -0
- data/bin/scout_apm_logging_monitor +5 -0
- data/gems/rails.gemfile +3 -0
- data/lib/scout_apm/logging/config.rb +265 -0
- data/lib/scout_apm/logging/context.rb +58 -0
- data/lib/scout_apm/logging/logger.rb +26 -0
- data/lib/scout_apm/logging/loggers/capture.rb +46 -0
- data/lib/scout_apm/logging/loggers/formatter.rb +86 -0
- data/lib/scout_apm/logging/loggers/logger.rb +82 -0
- data/lib/scout_apm/logging/loggers/proxy.rb +39 -0
- data/lib/scout_apm/logging/loggers/swap.rb +82 -0
- data/lib/scout_apm/logging/monitor/collector/checksum.rb +51 -0
- data/lib/scout_apm/logging/monitor/collector/configuration.rb +148 -0
- data/lib/scout_apm/logging/monitor/collector/downloader.rb +78 -0
- data/lib/scout_apm/logging/monitor/collector/extractor.rb +37 -0
- data/lib/scout_apm/logging/monitor/collector/manager.rb +57 -0
- data/lib/scout_apm/logging/monitor/monitor.rb +214 -0
- data/lib/scout_apm/logging/monitor_manager/manager.rb +150 -0
- data/lib/scout_apm/logging/state.rb +70 -0
- data/lib/scout_apm/logging/utils.rb +86 -0
- data/lib/scout_apm/logging/version.rb +7 -0
- data/lib/scout_apm_logging.rb +35 -0
- data/scout_apm_logging.gemspec +27 -0
- data/spec/data/config_test_1.yml +27 -0
- data/spec/data/empty_logs_config.yml +0 -0
- data/spec/data/logs_config.yml +3 -0
- data/spec/data/mock_config.yml +29 -0
- data/spec/data/state_file.json +3 -0
- data/spec/integration/loggers/capture_spec.rb +78 -0
- data/spec/integration/monitor/collector/downloader/will_verify_checksum.rb +47 -0
- data/spec/integration/monitor/collector_healthcheck_spec.rb +27 -0
- data/spec/integration/monitor/continuous_state_collector_spec.rb +29 -0
- data/spec/integration/monitor/previous_collector_setup_spec.rb +42 -0
- data/spec/integration/monitor_manager/disable_agent_spec.rb +28 -0
- data/spec/integration/monitor_manager/monitor_pid_file_spec.rb +36 -0
- data/spec/integration/monitor_manager/single_monitor_spec.rb +53 -0
- data/spec/integration/rails/lifecycle_spec.rb +29 -0
- data/spec/spec_helper.rb +65 -0
- data/spec/unit/config_spec.rb +25 -0
- data/spec/unit/loggers/capture_spec.rb +64 -0
- data/spec/unit/monitor/collector/configuration_spec.rb +64 -0
- data/spec/unit/state_spec.rb +20 -0
- data/tooling/checksums.rb +106 -0
- metadata +167 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 198bf309d17f0abfcf2c33f820a2d44e1e92ef5af9e321ee9fbaacfec256f032
|
4
|
+
data.tar.gz: 2a09b14a45e702fe1e4f4caee6ff89475e14971ba9edad41ae5cf3c72ab97850
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ebd30ceac50e2f14f3693946e8dd883112cc502513678b3a9963e253f2622d1ea488c2a66bce01f3e20726d7c6f08f61392cde6f22f1d9d0412987780f6399fa
|
7
|
+
data.tar.gz: c0cfeb3cfbed2628772550b18b1a2f7965a144fafea14846ed65936043ae283b48cef6ba0109a05dfe9df3b826080d0f24676c0957b3909311cae12098b1e2de
|
@@ -0,0 +1,37 @@
|
|
1
|
+
name: Test
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test:
|
7
|
+
name: RSpec
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
ruby: ['2.6', '2.7', '3.0', '3.1', '3.2', '3.3' ]
|
12
|
+
env:
|
13
|
+
DOCKER_RUBY_VERSION: ${{ matrix.ruby }}
|
14
|
+
BUNDLE_GEMFILE: gems/rails.gemfile
|
15
|
+
runs-on: ubuntu-20.04
|
16
|
+
steps:
|
17
|
+
- uses: actions/checkout@v2
|
18
|
+
- name: Set up Ruby
|
19
|
+
uses: ruby/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: ${{ matrix.ruby }}
|
22
|
+
bundler-cache: true
|
23
|
+
- name: Run RSpec
|
24
|
+
run: bundle exec rake test
|
25
|
+
|
26
|
+
rubocop:
|
27
|
+
name: RuboCop
|
28
|
+
runs-on: ubuntu-latest
|
29
|
+
steps:
|
30
|
+
- uses: actions/checkout@v2
|
31
|
+
- name: Set up Ruby
|
32
|
+
uses: ruby/setup-ruby@v1
|
33
|
+
with:
|
34
|
+
ruby-version: "3.2"
|
35
|
+
bundler-cache: true
|
36
|
+
- name: Run check
|
37
|
+
run: bundle exec rubocop
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- "bin/*"
|
4
|
+
- "tmp/**/*"
|
5
|
+
- "vendor/**/*"
|
6
|
+
- "spec/spec_helper.rb"
|
7
|
+
- "gems/*"
|
8
|
+
- "Rakefile"
|
9
|
+
- "tooling/*"
|
10
|
+
NewCops: disable
|
11
|
+
TargetRubyVersion: 2.6
|
12
|
+
|
13
|
+
Metrics/BlockLength:
|
14
|
+
Exclude:
|
15
|
+
- "spec/**/*"
|
16
|
+
|
17
|
+
Metrics/ClassLength:
|
18
|
+
Max: 150
|
19
|
+
|
20
|
+
Style/FrozenStringLiteralComment:
|
21
|
+
EnforcedStyle: always
|
22
|
+
Exclude:
|
23
|
+
- "Gemfile*"
|
24
|
+
- "*.gemspec"
|
25
|
+
- "spec/**/*"
|
26
|
+
|
27
|
+
Naming/FileName:
|
28
|
+
Exclude:
|
29
|
+
- "lib/scout_apm/scout_apm_logging.rb"
|
30
|
+
- "spec/scout_apm_logging_spec.rb"
|
31
|
+
|
32
|
+
Style/Documentation:
|
33
|
+
Enabled: true
|
34
|
+
|
35
|
+
Naming/PredicateName:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
Metrics/PerceivedComplexity:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
Metrics/CyclomaticComplexity:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
Style/ClassVars:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
Style/EmptyElse:
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
Naming/AccessorMethodName:
|
51
|
+
Enabled: false
|
52
|
+
|
53
|
+
Metrics/MethodLength:
|
54
|
+
Max: 25
|
55
|
+
|
56
|
+
Lint/LiteralAsCondition:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
Layout/LineLength:
|
60
|
+
Max: 130
|
61
|
+
|
62
|
+
Style/PercentLiteralDelimiters:
|
63
|
+
PreferredDelimiters:
|
64
|
+
'%w': "[]"
|
65
|
+
'%W': "[]"
|
data/Dockerfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
ARG RUBY_VERSION=3.3
|
2
|
+
FROM ruby:$RUBY_VERSION
|
3
|
+
|
4
|
+
# Set the working directory inside the container
|
5
|
+
WORKDIR /app
|
6
|
+
|
7
|
+
ARG BUNDLE_GEMFILE=./gems/rails.gemfile
|
8
|
+
|
9
|
+
# # Copy the entire project directory into the container
|
10
|
+
COPY . .
|
11
|
+
|
12
|
+
# Remove any local .lock files after copying
|
13
|
+
RUN find . -type f -name "*.lock" -exec rm -f {} \;
|
14
|
+
|
15
|
+
# Install dependencies
|
16
|
+
RUN bundle install
|
17
|
+
|
18
|
+
ENV BUNDLE_GEMFILE=./gems/rails.gemfile
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# ScoutApm Ruby Logging
|
2
|
+
|
3
|
+
[](https://github.com/scoutapp/scout_apm_ruby_logging/actions)
|
4
|
+
|
5
|
+
A Ruby gem for detailed, easy to navigate, managed log monitoring.
|
6
|
+
|
7
|
+
Sign up for an account at https://www.scoutapm.com to start monitoring your logs and application performance in minutes.
|
8
|
+
|
9
|
+
## Getting Started
|
10
|
+
Add the gem to your Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'scout_apm_logging'
|
14
|
+
```
|
15
|
+
|
16
|
+
Update your Gemfile:
|
17
|
+
```ruby
|
18
|
+
bundle install
|
19
|
+
```
|
20
|
+
|
21
|
+
Update your [RAILS_ROOT/config/scout_apm.yml](https://scoutapm.com/apps/new_ruby_application_configuration) and add the following:
|
22
|
+
|
23
|
+
```yaml
|
24
|
+
# ... Previous &defaults or environment defined configurations
|
25
|
+
|
26
|
+
# ENV equivalent: SCOUT_LOGS_MONITOR=true
|
27
|
+
# ENV equivalent: SCOUT_LOGS_INGEST_KEY=...
|
28
|
+
|
29
|
+
logs_monitor: true
|
30
|
+
logs_ingest_key: ...
|
31
|
+
```
|
32
|
+
|
33
|
+
Deploy :rocket:
|
34
|
+
|
35
|
+
## Testing
|
36
|
+
To run the entire test suite:
|
37
|
+
```ruby
|
38
|
+
bundle exec rake test
|
39
|
+
```
|
40
|
+
|
41
|
+
To run an individual test file within the suite:
|
42
|
+
```ruby
|
43
|
+
bundle exec rake test file=/path/to/spec/_spec.rb
|
44
|
+
```
|
45
|
+
|
46
|
+
To run test(s) against a specific Ruby version:
|
47
|
+
```ruby
|
48
|
+
DOCKER_RUBY_VERSION=3.3 bundle exec rake test
|
49
|
+
```
|
50
|
+
|
51
|
+
## Local
|
52
|
+
Point your Gemfile at your local checkout:
|
53
|
+
```ruby
|
54
|
+
gem 'scout_apm_logging', path: '/path/to/scout_apm_ruby_logging'
|
55
|
+
```
|
56
|
+
|
57
|
+
## Help
|
58
|
+
Email support@scoutapm.com if you need a hand.
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
task :default => :test
|
2
|
+
|
3
|
+
task :test do
|
4
|
+
ruby_version = ENV.has_key?("DOCKER_RUBY_VERSION") ? ENV["DOCKER_RUBY_VERSION"] : "3.3"
|
5
|
+
|
6
|
+
unless system("docker image inspect rspec-runner-#{ruby_version} > /dev/null 2>&1")
|
7
|
+
puts "Building RSpec runner Docker image..."
|
8
|
+
system("docker build --build-arg RUBY_VERSION=#{ruby_version} -t rspec-runner-#{ruby_version} .")
|
9
|
+
end
|
10
|
+
|
11
|
+
additional_options = ENV["debug"] ? "-it" : ""
|
12
|
+
|
13
|
+
if ENV["file"]
|
14
|
+
puts "Running RSpec test for #{ENV["file"]}..."
|
15
|
+
system("docker run -v #{Dir.pwd}/lib:/app/lib -v #{Dir.pwd}/spec:/app/spec #{additional_options} --rm rspec-runner-#{ruby_version} bundle exec rspec #{ENV["file"]} 2>&1")
|
16
|
+
else
|
17
|
+
puts "Running RSpec tests..."
|
18
|
+
Dir.glob("spec/**/*_spec.rb") do |spec_file|
|
19
|
+
puts "Running #{spec_file}..."
|
20
|
+
system("docker run -v #{Dir.pwd}/lib:/app/lib -v #{Dir.pwd}/spec:/app/spec #{additional_options} --rm rspec-runner-#{ruby_version} bundle exec rspec #{spec_file} 2>&1")
|
21
|
+
|
22
|
+
# Exit the task if a test fails
|
23
|
+
unless $?.success?
|
24
|
+
abort("Test failed: #{spec_file}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
task :access_container do
|
31
|
+
puts "Accessing a new rspec-runner-#{ruby_version} container..."
|
32
|
+
|
33
|
+
ruby_version = ENV.has_key?("DOCKER_RUBY_VERSION") ? ENV["DOCKER_RUBY_VERSION"] : "3.3"
|
34
|
+
system("docker run -it --rm -v #{Dir.pwd}/lib:/app/lib -v #{Dir.pwd}/spec:/app/spec --entrypoint /bin/bash rspec-runner-#{ruby_version}")
|
35
|
+
end
|
data/gems/rails.gemfile
ADDED
@@ -0,0 +1,265 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Valid Config Options:
|
4
|
+
#
|
5
|
+
# This list is complete, but some are for developers of
|
6
|
+
# scout_apm_logging itself. See the documentation at https://scoutapm.com/docs for
|
7
|
+
# customer-focused documentation.
|
8
|
+
#
|
9
|
+
# config_file - location of the scout_apm.yml configuration file
|
10
|
+
# log_level - log level for the internal library itself
|
11
|
+
# log_stdout - true or false. If true, log to STDOUT
|
12
|
+
# log_stderr - true or false. If true, log to STDERR
|
13
|
+
# log_file_path - either a directory or "STDOUT"
|
14
|
+
# log_class - the underlying class to use for logging. Defaults to Ruby's Logger class
|
15
|
+
# logs_monitor - true or false. If true, monitor logs
|
16
|
+
# logs_monitored - an array of log file paths to monitor. Overrides the default log destination detection
|
17
|
+
# logs_ingest_key - the ingest key to use for logs
|
18
|
+
# logs_capture_level - the minimum log level to start capturing logs for
|
19
|
+
# logs_config - a hash of configuration options for merging into the collector's config
|
20
|
+
# logs_reporting_endpoint - the endpoint to send logs to
|
21
|
+
# logs_proxy_log_dir - the directory to store logs in for monitoring
|
22
|
+
# manager_lock_file - the location for obtaining an exclusive lock for running monitor manager
|
23
|
+
# monitor_pid_file - the location of the pid file for the monitor
|
24
|
+
# monitor_state_file - the location of the state file for the monitor
|
25
|
+
# monitor_interval - the interval to check the collector healtcheck and for new state logs
|
26
|
+
# monitor_interval_delay - the delay to wait before running the first monitor interval
|
27
|
+
# collector_log_level - the log level for the collector
|
28
|
+
# collector_sending_queue_storage_dir - the directory to store queue files
|
29
|
+
# collector_offset_storage_dir - the directory to store offset files
|
30
|
+
# collector_pid_file - the location of the pid file for the collector
|
31
|
+
# collector_download_dir - the directory to store downloaded collector files
|
32
|
+
# collector_config_file - the location of the config file for the collector
|
33
|
+
# collector_version - the version of the collector to download
|
34
|
+
# health_check_port - the port to use for the collector health check. Default is dynamically derived based on port availability
|
35
|
+
#
|
36
|
+
# Any of these config settings can be set with an environment variable prefixed
|
37
|
+
# by SCOUT_ and uppercasing the key: SCOUT_LOG_LEVEL for instance.
|
38
|
+
|
39
|
+
module ScoutApm
|
40
|
+
module Logging
|
41
|
+
# Holds the configuration values for Scout APM Logging.
|
42
|
+
class Config < ScoutApm::Config
|
43
|
+
KNOWN_CONFIG_OPTIONS = %w[
|
44
|
+
config_file
|
45
|
+
log_level
|
46
|
+
log_stderr
|
47
|
+
log_stdout
|
48
|
+
log_file_path
|
49
|
+
log_class
|
50
|
+
logs_monitor
|
51
|
+
logs_monitored
|
52
|
+
logs_ingest_key
|
53
|
+
logs_capture_level
|
54
|
+
logs_config
|
55
|
+
logs_reporting_endpoint
|
56
|
+
logs_proxy_log_dir
|
57
|
+
manager_lock_file
|
58
|
+
monitor_pid_file
|
59
|
+
monitor_state_file
|
60
|
+
monitor_interval
|
61
|
+
monitor_interval_delay
|
62
|
+
collector_sending_queue_storage_dir
|
63
|
+
collector_offset_storage_dir
|
64
|
+
collector_pid_file
|
65
|
+
collector_download_dir
|
66
|
+
collector_config_file
|
67
|
+
collector_log_level
|
68
|
+
collector_version
|
69
|
+
health_check_port
|
70
|
+
].freeze
|
71
|
+
|
72
|
+
SETTING_COERCIONS = {
|
73
|
+
'logs_monitor' => BooleanCoercion.new,
|
74
|
+
'logs_monitored' => JsonCoercion.new,
|
75
|
+
'monitor_interval' => IntegerCoercion.new,
|
76
|
+
'monitor_interval_delay' => IntegerCoercion.new,
|
77
|
+
'health_check_port' => IntegerCoercion.new
|
78
|
+
}.freeze
|
79
|
+
|
80
|
+
# The bootstrapped, and initial config that we attach to the context. Will be swapped out by
|
81
|
+
# both the monitor and manager on initialization to the one with a file (which also has the dynamic
|
82
|
+
# and state configs).
|
83
|
+
def self.without_file(context)
|
84
|
+
overlays = [
|
85
|
+
ConfigEnvironment.new,
|
86
|
+
ConfigDefaults.new,
|
87
|
+
ConfigNull.new
|
88
|
+
]
|
89
|
+
new(context, overlays)
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.with_file(context, file_path = nil, config = {})
|
93
|
+
overlays = [
|
94
|
+
ConfigEnvironment.new,
|
95
|
+
ConfigFile.new(context, file_path, config),
|
96
|
+
ConfigDynamic.new,
|
97
|
+
ConfigState.new(context),
|
98
|
+
ConfigDefaults.new,
|
99
|
+
ConfigNull.new
|
100
|
+
]
|
101
|
+
new(context, overlays)
|
102
|
+
end
|
103
|
+
|
104
|
+
def state
|
105
|
+
@overlays.find { |overlay| overlay.is_a? ConfigState }
|
106
|
+
end
|
107
|
+
|
108
|
+
def value(key)
|
109
|
+
unless KNOWN_CONFIG_OPTIONS.include?(key)
|
110
|
+
logger.debug("Requested looking up a unknown configuration key: #{key} (not a problem. Evaluate and add to config.rb)")
|
111
|
+
end
|
112
|
+
|
113
|
+
o = overlay_for_key(key)
|
114
|
+
raw_value = if o
|
115
|
+
o.value(key)
|
116
|
+
else
|
117
|
+
# No overlay said it could handle this key, bail out with nil.
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
|
121
|
+
coercion = SETTING_COERCIONS.fetch(key, NullCoercion.new)
|
122
|
+
coercion.coerce(raw_value)
|
123
|
+
end
|
124
|
+
|
125
|
+
def all_settings
|
126
|
+
KNOWN_CONFIG_OPTIONS.inject([]) do |memo, key|
|
127
|
+
o = overlay_for_key(key)
|
128
|
+
memo << { key: key, value: value(key).inspect, source: o.name }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Dynamically set state based on the application.
|
133
|
+
class ConfigDynamic
|
134
|
+
@values_to_set = {
|
135
|
+
'health_check_port': nil
|
136
|
+
}
|
137
|
+
|
138
|
+
class << self
|
139
|
+
attr_reader :values_to_set
|
140
|
+
|
141
|
+
def set_value(key, value)
|
142
|
+
@values_to_set[key] = value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def value(key)
|
147
|
+
self.class.values_to_set[key]
|
148
|
+
end
|
149
|
+
|
150
|
+
def has_key?(key)
|
151
|
+
self.class.values_to_set.key?(key)
|
152
|
+
end
|
153
|
+
|
154
|
+
def name
|
155
|
+
'dynamic'
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# State that is persisted and communicated upon by multiple processes.
|
160
|
+
class ConfigState
|
161
|
+
@values_to_set = {
|
162
|
+
'logs_monitored': [],
|
163
|
+
'health_check_port': nil
|
164
|
+
}
|
165
|
+
|
166
|
+
class << self
|
167
|
+
attr_reader :values_to_set
|
168
|
+
|
169
|
+
def set_value(key, value)
|
170
|
+
@values_to_set[key] = value
|
171
|
+
end
|
172
|
+
|
173
|
+
def get_values_to_set
|
174
|
+
@values_to_set.keys.map(&:to_s)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
attr_reader :context, :state
|
179
|
+
|
180
|
+
def initialize(context)
|
181
|
+
@context = context
|
182
|
+
|
183
|
+
# Note, the config on the context we are passing in here comes from the Config.without_file. We
|
184
|
+
# won't be aware of a state file that was defined in a config file, but this would be a very
|
185
|
+
# rare thing to have happen as this is more of an internal config value.
|
186
|
+
@state = State.new(context)
|
187
|
+
|
188
|
+
set_values_from_state
|
189
|
+
end
|
190
|
+
|
191
|
+
def value(key)
|
192
|
+
self.class.values_to_set[key]
|
193
|
+
end
|
194
|
+
|
195
|
+
def has_key?(key)
|
196
|
+
self.class.values_to_set.key?(key)
|
197
|
+
end
|
198
|
+
|
199
|
+
def name
|
200
|
+
'state'
|
201
|
+
end
|
202
|
+
|
203
|
+
def flush_state!
|
204
|
+
state.flush_to_file!
|
205
|
+
end
|
206
|
+
|
207
|
+
def add_log_locations!(updated_log_locations)
|
208
|
+
state.flush_to_file!(updated_log_locations)
|
209
|
+
end
|
210
|
+
|
211
|
+
private
|
212
|
+
|
213
|
+
def set_values_from_state
|
214
|
+
data = state.load_state_from_file
|
215
|
+
|
216
|
+
return unless data
|
217
|
+
|
218
|
+
data.each do |key, value|
|
219
|
+
self.class.set_value(key, value)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Defaults in case no config file has been found.
|
225
|
+
class ConfigDefaults
|
226
|
+
DEFAULTS = {
|
227
|
+
'log_level' => 'info',
|
228
|
+
'logs_monitored' => [],
|
229
|
+
'logs_capture_level' => 'debug',
|
230
|
+
'logs_reporting_endpoint' => 'https://otlp.telemetryhub.com:4317',
|
231
|
+
'logs_proxy_log_dir' => '/tmp/scout_apm/logs/',
|
232
|
+
'manager_lock_file' => '/tmp/scout_apm/monitor_lock_file.lock',
|
233
|
+
'monitor_pid_file' => '/tmp/scout_apm/scout_apm_log_monitor.pid',
|
234
|
+
'monitor_state_file' => '/tmp/scout_apm/scout_apm_log_monitor_state.json',
|
235
|
+
'monitor_interval' => 60,
|
236
|
+
'monitor_interval_delay' => 60,
|
237
|
+
'collector_log_level' => 'error',
|
238
|
+
'collector_offset_storage_dir' => '/tmp/scout_apm/file_storage/receiver/',
|
239
|
+
'collector_sending_queue_storage_dir' => '/tmp/scout_apm/file_storage/otc/',
|
240
|
+
'collector_pid_file' => '/tmp/scout_apm/scout_apm_otel_collector.pid',
|
241
|
+
'collector_download_dir' => '/tmp/scout_apm/',
|
242
|
+
'collector_config_file' => '/tmp/scout_apm/config.yml',
|
243
|
+
'collector_version' => '0.102.1'
|
244
|
+
}.freeze
|
245
|
+
|
246
|
+
def value(key)
|
247
|
+
DEFAULTS[key]
|
248
|
+
end
|
249
|
+
|
250
|
+
def has_key?(key)
|
251
|
+
DEFAULTS.key?(key)
|
252
|
+
end
|
253
|
+
|
254
|
+
# Dyanmic/computed values are here, but not counted as user specified.
|
255
|
+
def any_keys_found?
|
256
|
+
false
|
257
|
+
end
|
258
|
+
|
259
|
+
def name
|
260
|
+
'defaults'
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ScoutApm
|
4
|
+
module Logging
|
5
|
+
# Contains context around Scout APM logging, such as environment, configuration, and the logger.
|
6
|
+
class Context
|
7
|
+
# The root of the application.
|
8
|
+
attr_accessor :application_root
|
9
|
+
|
10
|
+
# Initially start up without attempting to load a configuration file. We
|
11
|
+
# need to be able to lookup configuration options like "application_root"
|
12
|
+
# which would then in turn influence where the yaml configuration file is
|
13
|
+
# located
|
14
|
+
#
|
15
|
+
# Later in initialization, we set config= to include the file.
|
16
|
+
def initialize
|
17
|
+
@logger = LoggerFactory.build_minimal_logger
|
18
|
+
end
|
19
|
+
|
20
|
+
def config
|
21
|
+
@config ||= Config.without_file(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def environment
|
25
|
+
@environment ||= ScoutApm::Environment.instance
|
26
|
+
end
|
27
|
+
|
28
|
+
def logger
|
29
|
+
@logger ||= LoggerFactory.build(config, environment, application_root)
|
30
|
+
end
|
31
|
+
|
32
|
+
def config=(config)
|
33
|
+
@config = config
|
34
|
+
|
35
|
+
@logger = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create a logger based on the configuration settings.
|
40
|
+
class LoggerFactory
|
41
|
+
def self.build(config, environment, application_root = nil)
|
42
|
+
root = application_root || environment.root
|
43
|
+
Logger.new(root,
|
44
|
+
{
|
45
|
+
log_level: config.value('log_level'),
|
46
|
+
log_file_path: config.value('log_file_path'),
|
47
|
+
stdout: config.value('log_stdout') || environment.platform_integration.log_to_stdout?,
|
48
|
+
stderr: config.value('log_stderr'),
|
49
|
+
logger_class: config.value('log_class')
|
50
|
+
})
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.build_minimal_logger
|
54
|
+
Logger.new(nil, stdout: true)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ScoutApm
|
4
|
+
module Logging
|
5
|
+
# Custom internal logger for ScoutApm Logging.
|
6
|
+
class Logger < ScoutApm::Logger
|
7
|
+
private
|
8
|
+
|
9
|
+
def determine_log_destination
|
10
|
+
case true
|
11
|
+
when stdout?
|
12
|
+
$stdout
|
13
|
+
when stderr?
|
14
|
+
$stderr
|
15
|
+
when validate_path(@opts[:log_file])
|
16
|
+
@opts[:log_file]
|
17
|
+
when validate_path("#{log_file_path}/scout_apm_logging.log")
|
18
|
+
"#{log_file_path}/scout_apm_logging.log"
|
19
|
+
else
|
20
|
+
# Safe fallback
|
21
|
+
$stdout
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
require_relative './swap'
|
6
|
+
require_relative './proxy'
|
7
|
+
|
8
|
+
module ScoutApm
|
9
|
+
module Logging
|
10
|
+
module Loggers
|
11
|
+
# Will capture the log destinations from the application's loggers.
|
12
|
+
class Capture
|
13
|
+
attr_reader :context
|
14
|
+
|
15
|
+
def initialize(context)
|
16
|
+
@context = context
|
17
|
+
end
|
18
|
+
|
19
|
+
def capture_log_locations! # rubocop:disable Metrics/AbcSize
|
20
|
+
logger_instances << Rails.logger if defined?(Rails)
|
21
|
+
logger_instances << Sidekiq.logger if defined?(Sidekiq)
|
22
|
+
logger_instances << ObjectSpace.each_object(::ScoutTestLogger).to_a if defined?(::ScoutTestLogger)
|
23
|
+
|
24
|
+
# Swap in our logger for each logger instance, in conjunction with the original class.
|
25
|
+
updated_log_locations = logger_instances.compact.flatten.map do |logger|
|
26
|
+
swapped_in_location(logger)
|
27
|
+
end
|
28
|
+
|
29
|
+
context.config.state.add_log_locations!(updated_log_locations)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def logger_instances
|
35
|
+
@logger_instances ||= []
|
36
|
+
end
|
37
|
+
|
38
|
+
def swapped_in_location(log_instance)
|
39
|
+
swap = Swap.new(context, log_instance)
|
40
|
+
swap.update_logger!
|
41
|
+
swap.log_location
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|