cem_acpt 0.2.4 → 0.6.0
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/.github/workflows/spec.yml +30 -0
- data/Gemfile +4 -3
- data/Gemfile.lock +95 -43
- data/README.md +144 -83
- data/cem_acpt.gemspec +12 -7
- data/exe/cem_acpt +41 -7
- data/lib/cem_acpt/config.rb +340 -0
- data/lib/cem_acpt/core_extensions.rb +17 -61
- data/lib/cem_acpt/goss/api/action_response.rb +175 -0
- data/lib/cem_acpt/goss/api.rb +83 -0
- data/lib/cem_acpt/goss.rb +8 -0
- data/lib/cem_acpt/image_name_builder.rb +0 -9
- data/lib/cem_acpt/logging/formatter.rb +97 -0
- data/lib/cem_acpt/logging.rb +168 -142
- data/lib/cem_acpt/platform/base.rb +26 -37
- data/lib/cem_acpt/platform/gcp.rb +48 -62
- data/lib/cem_acpt/platform.rb +30 -28
- data/lib/cem_acpt/provision/terraform/linux.rb +47 -0
- data/lib/cem_acpt/provision/terraform/os_data.rb +72 -0
- data/lib/cem_acpt/provision/terraform/windows.rb +22 -0
- data/lib/cem_acpt/provision/terraform.rb +193 -0
- data/lib/cem_acpt/provision.rb +20 -0
- data/lib/cem_acpt/puppet_helpers.rb +0 -1
- data/lib/cem_acpt/test_data.rb +23 -13
- data/lib/cem_acpt/test_runner/log_formatter/goss_action_response.rb +104 -0
- data/lib/cem_acpt/test_runner/log_formatter.rb +10 -0
- data/lib/cem_acpt/test_runner.rb +170 -3
- data/lib/cem_acpt/utils/puppet.rb +29 -0
- data/lib/cem_acpt/utils/ssh.rb +197 -0
- data/lib/cem_acpt/utils/terminal.rb +27 -0
- data/lib/cem_acpt/utils.rb +4 -138
- data/lib/cem_acpt/version.rb +1 -1
- data/lib/cem_acpt.rb +73 -23
- data/lib/terraform/gcp/linux/goss/puppet_idempotent.yaml +10 -0
- data/lib/terraform/gcp/linux/goss/puppet_noop.yaml +12 -0
- data/lib/terraform/gcp/linux/main.tf +191 -0
- data/lib/terraform/gcp/linux/systemd/goss-acpt.service +8 -0
- data/lib/terraform/gcp/linux/systemd/goss-idempotent.service +8 -0
- data/lib/terraform/gcp/linux/systemd/goss-noop.service +8 -0
- data/lib/terraform/gcp/windows/.keep +0 -0
- data/sample_config.yaml +22 -21
- metadata +151 -51
- data/lib/cem_acpt/bootstrap/bootstrapper.rb +0 -206
- data/lib/cem_acpt/bootstrap/operating_system/rhel_family.rb +0 -129
- data/lib/cem_acpt/bootstrap/operating_system.rb +0 -17
- data/lib/cem_acpt/bootstrap.rb +0 -12
- data/lib/cem_acpt/context.rb +0 -153
- data/lib/cem_acpt/platform/base/cmd.rb +0 -71
- data/lib/cem_acpt/platform/gcp/cmd.rb +0 -353
- data/lib/cem_acpt/platform/gcp/compute.rb +0 -332
- data/lib/cem_acpt/platform/vmpooler.rb +0 -24
- data/lib/cem_acpt/rspec_utils.rb +0 -242
- data/lib/cem_acpt/shared_objects.rb +0 -537
- data/lib/cem_acpt/spec_helper_acceptance.rb +0 -184
- data/lib/cem_acpt/test_runner/run_handler.rb +0 -187
- data/lib/cem_acpt/test_runner/runner.rb +0 -210
- data/lib/cem_acpt/test_runner/runner_result.rb +0 -103
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'async'
|
4
|
+
require 'async/barrier'
|
5
|
+
require 'async/http/internet'
|
6
|
+
require 'json'
|
7
|
+
require_relative 'api/action_response'
|
8
|
+
|
9
|
+
module CemAcpt
|
10
|
+
module Goss
|
11
|
+
# Holds methods for interacting with the Goss API running on a test node.
|
12
|
+
module Api
|
13
|
+
class << self
|
14
|
+
# The actions that can be run against the Goss API. The key is the action
|
15
|
+
# name and the value is the port/endpoint of the action.
|
16
|
+
ACTIONS = {
|
17
|
+
acpt: '8080/acpt',
|
18
|
+
idempotent: '8081/idempotent',
|
19
|
+
noop: '8082/noop',
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
# Create a URI for the specified action against the specified host.
|
23
|
+
# @param host [String] The host to run the action against. This should be
|
24
|
+
# a public IP address or a DNS-resolvable name.
|
25
|
+
# @param action [Symbol] The action to run.
|
26
|
+
# @return [URI] The URI for the action.
|
27
|
+
def action_uri(host, action)
|
28
|
+
URI("http://#{host}:#{ACTIONS[action.to_sym]}")
|
29
|
+
end
|
30
|
+
|
31
|
+
# Run the specified actions against the specified hosts asynchronously.
|
32
|
+
# @param hosts [Array<String>] The hosts to run the actions against. Each
|
33
|
+
# host should be a public IP address or a DNS-resolvable name.
|
34
|
+
# @param results [Queue] The queue to push the results to.
|
35
|
+
# @param only [Array<Symbol>] The actions to run.
|
36
|
+
# @param except [Array<Symbol>] The actions to skip.
|
37
|
+
# @return [Queue] The queue of results.
|
38
|
+
def run_actions_async(hosts, results: Queue.new, only: [], except: [])
|
39
|
+
raise ArgumentError, 'hosts must be an Array' unless hosts.is_a?(Array)
|
40
|
+
raise ArgumentError, 'results must be a Queue-like object implementing #<<' unless results.respond_to?(:<<)
|
41
|
+
raise ArgumentError, 'only must be an Array' unless except.is_a?(Array)
|
42
|
+
raise ArgumentError, 'except must be an Array' unless except.is_a?(Array)
|
43
|
+
only.map!(&:to_sym)
|
44
|
+
except.map!(&:to_sym)
|
45
|
+
only_specified = !only.empty?
|
46
|
+
except_specified = !except.empty?
|
47
|
+
Async do
|
48
|
+
internet = Async::HTTP::Internet.new
|
49
|
+
barrier = Async::Barrier.new
|
50
|
+
barrier.async do
|
51
|
+
hosts.each do |host|
|
52
|
+
ACTIONS.keys.each do |action, _|
|
53
|
+
next if only_specified && !only.include?(action)
|
54
|
+
next if except_specified && except.include?(action)
|
55
|
+
|
56
|
+
results << run_action(internet, host, action)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
barrier.wait
|
61
|
+
ensure
|
62
|
+
internet&.close
|
63
|
+
results.close if results.respond_to?(:close)
|
64
|
+
end
|
65
|
+
results
|
66
|
+
end
|
67
|
+
|
68
|
+
# Run the specified action against the specified host.
|
69
|
+
# @param internet [Async::HTTP::Internet] The Async::HTTP::Internet object to use for the request.
|
70
|
+
# @param host [String] The host to run the action against. This should be
|
71
|
+
# a public IP address or a DNS-resolvable name.
|
72
|
+
# @param action [Symbol] The action to run.
|
73
|
+
# @return [ActionResponse] The response from the action.
|
74
|
+
def run_action(internet, host, action)
|
75
|
+
uri = action_uri(host, action)
|
76
|
+
response = internet.get(uri.to_s)
|
77
|
+
body = JSON.parse(response.read)
|
78
|
+
ActionResponse.new(host, action, response.status, body)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -41,13 +41,8 @@ module CemAcpt
|
|
41
41
|
# @param test_data [Hash] The test data to use to build the image name.
|
42
42
|
# @return [String] The image name.
|
43
43
|
def build(test_data)
|
44
|
-
logger.debug 'Building image name...'
|
45
|
-
logger.debug "Using config: #{@config.to_h}"
|
46
|
-
logger.debug "Test data: #{test_data}"
|
47
44
|
parts = resolve_parts(test_data)
|
48
|
-
logger.debug "Resolved parts: #{parts}"
|
49
45
|
image_name = create_image_name(parts)
|
50
|
-
logger.debug "Created image name: #{image_name}"
|
51
46
|
final_image_name = character_substitutions(image_name)
|
52
47
|
logger.debug "Final image name: #{final_image_name}"
|
53
48
|
final_image_name
|
@@ -61,10 +56,8 @@ module CemAcpt
|
|
61
56
|
# @return [Array] The parts array with variables resolved.
|
62
57
|
def resolve_parts(test_data)
|
63
58
|
@config[:parts].each_with_object([]) do |part, ary|
|
64
|
-
logger.debug "Resolving part: #{part}"
|
65
59
|
if part.start_with?('$')
|
66
60
|
var_path = part[1..-1]
|
67
|
-
logger.debug "Resolving variable path: #{var_path}"
|
68
61
|
ary << test_data.dot_dig(var_path)
|
69
62
|
else
|
70
63
|
ary << part
|
@@ -77,10 +70,8 @@ module CemAcpt
|
|
77
70
|
# @return [String] The image name.
|
78
71
|
def create_image_name(parts)
|
79
72
|
image_name = @config[:join_with] ? parts.join(@config[:join_with]) : parts.join
|
80
|
-
logger.debug("Final image name: #{image_name}")
|
81
73
|
|
82
74
|
if @config[:validation_pattern]
|
83
|
-
logger.debug "Validating image name: #{image_name}..."
|
84
75
|
return image_name if image_name.match?(@config[:validation_pattern])
|
85
76
|
|
86
77
|
raise "Image name #{image_name} does not match validation pattern #{@config[:validation_pattern]}"
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CemAcpt
|
4
|
+
module Logging
|
5
|
+
module Formatter
|
6
|
+
class << self
|
7
|
+
def valid_format?(log_format)
|
8
|
+
all.any? { |f| f.log_format == log_format.downcase.to_sym }
|
9
|
+
end
|
10
|
+
|
11
|
+
def for(log_format)
|
12
|
+
formatter = all.find { |f| f.log_format == log_format.downcase.to_sym }
|
13
|
+
raise "Unknown log format: #{log_format}" unless formatter
|
14
|
+
|
15
|
+
formatter.get
|
16
|
+
end
|
17
|
+
|
18
|
+
def all
|
19
|
+
@all ||= [FileFormatter.new, JSONFormatter.new, TextFormatter.new, GithubActionFormatter.new]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class FileFormatter
|
24
|
+
attr_reader :log_format
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@log_format = :file
|
28
|
+
end
|
29
|
+
|
30
|
+
def get
|
31
|
+
proc do |severity, datetime, progname, msg|
|
32
|
+
format(severity, datetime, progname, msg)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def format(severity, datetime, progname, msg)
|
39
|
+
"[#{datetime}] #{severity}: #{progname}: #{msg}\n"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class JSONFormatter < FileFormatter
|
44
|
+
def initialize
|
45
|
+
super
|
46
|
+
@log_format = :json
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def format(severity, datetime, progname, msg)
|
52
|
+
require 'json'
|
53
|
+
{
|
54
|
+
timestamp: datetime,
|
55
|
+
progname: progname,
|
56
|
+
severity: severity,
|
57
|
+
message: msg,
|
58
|
+
}.to_json + "\n"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class TextFormatter < FileFormatter
|
63
|
+
def initialize
|
64
|
+
super
|
65
|
+
@log_format = :text
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def format(severity, _datetime, progname, msg)
|
71
|
+
"#{severity}: #{progname}: #{msg}\n"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class GithubActionFormatter < FileFormatter
|
76
|
+
SEV_MAP = {
|
77
|
+
'DEBUG' => '::debug::',
|
78
|
+
'INFO' => '::notice::',
|
79
|
+
'WARN' => '::warning::',
|
80
|
+
'ERROR' => '::error::',
|
81
|
+
'FATAL' => '::error::',
|
82
|
+
}.freeze
|
83
|
+
|
84
|
+
def initialize
|
85
|
+
super
|
86
|
+
@log_format = :github_action
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def format(severity, _datetime, progname, msg)
|
92
|
+
"#{SEV_MAP[severity]}[#{progname}] #{msg}\n"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/cem_acpt/logging.rb
CHANGED
@@ -1,27 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'logger'
|
4
|
+
require 'cem_acpt/logging/formatter'
|
4
5
|
|
5
6
|
module CemAcpt
|
6
7
|
# Logging for CemAcpt
|
7
8
|
module Logging
|
8
9
|
LEVEL_MAP = {
|
9
|
-
'debug' => Logger::DEBUG,
|
10
|
-
'info' => Logger::INFO,
|
11
|
-
'warn' => Logger::WARN,
|
12
|
-
'error' => Logger::ERROR,
|
13
|
-
'fatal' => Logger::FATAL,
|
14
|
-
'unknown' => Logger::UNKNOWN,
|
10
|
+
'debug' => ::Logger::DEBUG,
|
11
|
+
'info' => ::Logger::INFO,
|
12
|
+
'warn' => ::Logger::WARN,
|
13
|
+
'error' => ::Logger::ERROR,
|
14
|
+
'fatal' => ::Logger::FATAL,
|
15
|
+
'unknown' => ::Logger::UNKNOWN,
|
15
16
|
}
|
16
17
|
|
17
18
|
# Delegator class for when you want to log to multiple devices
|
18
19
|
# at the same time, such as STDOUT and a file.
|
19
|
-
# @param loggers [
|
20
|
+
# @param loggers [Logger] one or more instances of CemAcpt::Logging::Logger
|
20
21
|
class MultiLogger
|
22
|
+
# @param configs [Array<Hash>] an array of hashes containing the
|
23
|
+
# configuration for each logger. Each hash must contain the keys
|
24
|
+
# :logdev, :shift_age, and :shift_size. The values for these keys
|
25
|
+
# are passed to the Logger.new method. Any other keys and values
|
26
|
+
# are passed to the Logger.new method as keyword arguments.
|
27
|
+
# @return [MultiLogger] a new instance of MultiLogger
|
28
|
+
def self.from_logger_configs(configs)
|
29
|
+
return if configs.nil? || configs.empty?
|
30
|
+
|
31
|
+
loggers = configs.map do |config|
|
32
|
+
CemAcpt::Logging::Logger.new(config[:logdev],
|
33
|
+
config[:shift_age],
|
34
|
+
config[:shift_size],
|
35
|
+
**config.except(:logdev, :shift_age, :shift_size))
|
36
|
+
end
|
37
|
+
new(*loggers)
|
38
|
+
end
|
39
|
+
|
21
40
|
def initialize(*loggers)
|
22
41
|
@loggers = loggers
|
23
42
|
end
|
24
43
|
|
44
|
+
def logger_configs
|
45
|
+
@loggers.map(&:config_hash)
|
46
|
+
end
|
47
|
+
|
25
48
|
def method_missing(m, *args, &block)
|
26
49
|
if @loggers.all? { |l| l.respond_to?(m) }
|
27
50
|
@loggers.map { |l| l.send(m, *args, &block) }
|
@@ -35,6 +58,137 @@ module CemAcpt
|
|
35
58
|
end
|
36
59
|
end
|
37
60
|
|
61
|
+
# Delagator class for the standard Ruby Logger class.
|
62
|
+
# This class is used to ensure the Github Actions formatter
|
63
|
+
# correctly formats the log output when using the standard
|
64
|
+
# Ruby Logger class methods such as #info, #debug, etc.
|
65
|
+
class Logger < ::Logger
|
66
|
+
attr_reader :config_hash
|
67
|
+
attr_accessor :trap_context
|
68
|
+
|
69
|
+
def initialize(logdev, shift_age = 0, shift_size = 1_048_576, **kwargs)
|
70
|
+
@config_hash = { logdev: logdev, shift_age: shift_age, shift_size: shift_size, **kwargs }
|
71
|
+
@ci_mode = (kwargs[:formatter].downcase.to_sym == :github_action || !ENV['GITHUB_ACTIONS'].nil? || !ENV['CI'].nil?)
|
72
|
+
kwargs[:formatter] = CemAcpt::Logging::Formatter.for(kwargs[:formatter]) if kwargs[:formatter]
|
73
|
+
super(logdev, shift_age, shift_size, **kwargs)
|
74
|
+
@raw_logdev = logdev # Used for logging from trap context
|
75
|
+
@trap_context = false # If true, will not use the standard Logger methods and will write directly to the logdev
|
76
|
+
@verbose = false
|
77
|
+
end
|
78
|
+
|
79
|
+
def set_verbose(value)
|
80
|
+
raise ArgumentError, 'value must be a boolean' unless [true, false].include?(value)
|
81
|
+
|
82
|
+
@verbose = value
|
83
|
+
end
|
84
|
+
|
85
|
+
def verbose(progname = nil, &block)
|
86
|
+
return unless @verbose
|
87
|
+
|
88
|
+
debug(progname, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
def debug(progname = nil, &block)
|
92
|
+
return if log_trap_context('debug', progname, &block)
|
93
|
+
return if log_ci_mode('debug', progname, &block)
|
94
|
+
|
95
|
+
super
|
96
|
+
end
|
97
|
+
|
98
|
+
def info(progname = nil, &block)
|
99
|
+
return if log_trap_context('info', progname, &block)
|
100
|
+
return if log_ci_mode('info', progname, &block)
|
101
|
+
|
102
|
+
super
|
103
|
+
end
|
104
|
+
|
105
|
+
def warn(progname = nil, &block)
|
106
|
+
return if log_trap_context('warn', progname, &block)
|
107
|
+
return if log_ci_mode('warn', progname, &block)
|
108
|
+
|
109
|
+
super
|
110
|
+
end
|
111
|
+
|
112
|
+
def error(progname = nil, &block)
|
113
|
+
return if log_trap_context('error', progname, &block)
|
114
|
+
return if log_ci_mode('error', progname, &block)
|
115
|
+
|
116
|
+
super
|
117
|
+
end
|
118
|
+
|
119
|
+
def fatal(progname = nil, &block)
|
120
|
+
return if log_trap_context('fatal', progname, &block)
|
121
|
+
return if log_ci_mode('fatal', progname, &block)
|
122
|
+
|
123
|
+
super
|
124
|
+
end
|
125
|
+
|
126
|
+
# Wraps the given block in a Github Actions group if in CI mode
|
127
|
+
# @param name [String] the name of the group
|
128
|
+
def with_ci_group(name)
|
129
|
+
return yield unless @ci_mode
|
130
|
+
|
131
|
+
self.<< "::group::#{name}\n"
|
132
|
+
yield
|
133
|
+
ensure
|
134
|
+
self.<< "::endgroup::\n" if @ci_mode
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
# Can't use the standard Logger methods when in trap context
|
140
|
+
# because they don't work. So we have to write directly to the
|
141
|
+
# raw logdev. Unlike the standard Logger methods, there is no
|
142
|
+
# Mutex lock, so messages are not guaranteed to be in order.
|
143
|
+
# This is necessary in the trap context because using a Mutex
|
144
|
+
# lock in a trap context will cause a deadlock.
|
145
|
+
def log_trap_context(severity, progname = nil, &block)
|
146
|
+
return false unless @trap_context && level <= LEVEL_MAP[severity]
|
147
|
+
|
148
|
+
if @raw_logdev.respond_to?(:puts)
|
149
|
+
@raw_logdev.puts(ci_format(severity, false, progname, &block))
|
150
|
+
elsif @raw_logdev.respond_to?(:write)
|
151
|
+
@raw_logdev.write(ci_format(severity, true, progname, &block))
|
152
|
+
else
|
153
|
+
# Default to logging to STDERR
|
154
|
+
$stderr.puts(ci_format(severity, false, progname, &block))
|
155
|
+
end
|
156
|
+
true
|
157
|
+
end
|
158
|
+
|
159
|
+
# CI mode uses << instead of the standard Logger methods
|
160
|
+
# because Github Actions wants messages to be echoed. This
|
161
|
+
# is a way to ensure that, in the event of using a multi-logger
|
162
|
+
# with a standard Logger, the CI mode messages are still
|
163
|
+
# formatted correctly.
|
164
|
+
def log_ci_mode(severity, progname = nil, &block)
|
165
|
+
return false unless @ci_mode && level <= LEVEL_MAP[severity]
|
166
|
+
|
167
|
+
self.<<(ci_format(severity, true, progname, &block))
|
168
|
+
true
|
169
|
+
end
|
170
|
+
|
171
|
+
# Formats the log message for CI mode, translating the severity
|
172
|
+
# level to the correct format for Github Actions and adding
|
173
|
+
# the colon separators.
|
174
|
+
def ci_format(severity, newline = true, progname = nil, &block)
|
175
|
+
sev = if severity == 'fatal'
|
176
|
+
'error'
|
177
|
+
elsif severity == 'warn'
|
178
|
+
'warning'
|
179
|
+
elsif severity == 'info'
|
180
|
+
'notice'
|
181
|
+
else
|
182
|
+
severity
|
183
|
+
end
|
184
|
+
block_message = block_given? ? block.call : nil
|
185
|
+
base = [progname, block_message].compact.join(': ')
|
186
|
+
return if base.empty?
|
187
|
+
|
188
|
+
newline ? "::#{sev}::#{base}\n" : "::#{sev}::#{base}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
38
192
|
class << self
|
39
193
|
def new_logger(*logdevs, **configs)
|
40
194
|
new_configs = current_log_config.merge(configs.reject { |_, v| v.nil? })
|
@@ -57,14 +211,9 @@ module CemAcpt
|
|
57
211
|
|
58
212
|
# Exposes a logger instance. Will either use the currently set logger or
|
59
213
|
# create a new one.
|
60
|
-
# @return [
|
214
|
+
# @return [MultiLogger]
|
61
215
|
def logger
|
62
|
-
@logger ||=
|
63
|
-
current_log_config[:logdev],
|
64
|
-
current_log_config[:shift_age],
|
65
|
-
current_log_config[:shift_size],
|
66
|
-
**current_log_config.reject { |k, _| %i[logdev shift_age shift_size].include?(k) }
|
67
|
-
)
|
216
|
+
@logger ||= new_logger($stdout)
|
68
217
|
end
|
69
218
|
|
70
219
|
# Shortcut method for logger.level
|
@@ -92,41 +241,7 @@ module CemAcpt
|
|
92
241
|
# @param f [Symbol] the log format style to set
|
93
242
|
# @return [Proc] the proc to be passed to Logger#formatter=
|
94
243
|
def new_log_formatter(f)
|
95
|
-
|
96
|
-
when :json
|
97
|
-
require 'json'
|
98
|
-
@current_log_format = :json
|
99
|
-
proc do |severity, datetime, progname, msg|
|
100
|
-
{
|
101
|
-
timestamp: datetime,
|
102
|
-
progname: progname,
|
103
|
-
severity: severity,
|
104
|
-
message: msg,
|
105
|
-
}.to_json + "\n"
|
106
|
-
end
|
107
|
-
when :text
|
108
|
-
@current_log_format = :text
|
109
|
-
proc do |severity, _datetime, _progname, msg|
|
110
|
-
"#{severity} - #{msg}\n"
|
111
|
-
end
|
112
|
-
when :github_action
|
113
|
-
@current_log_format = :github_action
|
114
|
-
sev_map = {
|
115
|
-
'DEBUG' => '::debug::',
|
116
|
-
'INFO' => '::notice::',
|
117
|
-
'WARN' => '::warning::',
|
118
|
-
'ERROR' => '::error::',
|
119
|
-
'FATAL' => '::error::',
|
120
|
-
}
|
121
|
-
proc do |severity, _datetime, _progname, msg|
|
122
|
-
"#{sev_map[severity]}{#{msg}}\n"
|
123
|
-
end
|
124
|
-
else
|
125
|
-
@current_log_format = :file
|
126
|
-
proc do |severity, datetime, _progname, msg|
|
127
|
-
"[#{datetime}] #{severity}: #{msg}\n"
|
128
|
-
end
|
129
|
-
end
|
244
|
+
CemAcpt::Logging::Formatter.for(f)
|
130
245
|
end
|
131
246
|
|
132
247
|
# Returns the current log config if set, or the default if not.
|
@@ -134,15 +249,14 @@ module CemAcpt
|
|
134
249
|
def current_log_config
|
135
250
|
return @log_config if @log_config
|
136
251
|
|
137
|
-
formatter = new_log_formatter(current_log_format)
|
138
252
|
@log_config = {
|
139
253
|
logdev: $stdout,
|
140
254
|
shift_age: 'o',
|
141
255
|
shift_size: 1_048_576,
|
142
|
-
level: Logger::INFO,
|
256
|
+
level: ::Logger::INFO,
|
143
257
|
progname: 'CemAcpt',
|
144
258
|
datetime_format: '%Y%m%dT%H%M%S%z',
|
145
|
-
formatter:
|
259
|
+
formatter: current_log_format,
|
146
260
|
}
|
147
261
|
@log_config
|
148
262
|
end
|
@@ -177,12 +291,12 @@ module CemAcpt
|
|
177
291
|
@log_config[:logdev] = $stdout
|
178
292
|
logger.warn("Unknown log target: #{logdev.inspect}, using STDOUT")
|
179
293
|
end
|
180
|
-
@logger = Logger.new(
|
294
|
+
@logger = MultiLogger.new(Logger.new(
|
181
295
|
@log_config[:logdev],
|
182
296
|
@log_config[:shift_age],
|
183
297
|
@log_config[:shift_size],
|
184
298
|
**@log_config.reject { |k, _| %i[logdev shift_age shift_size].include?(k) },
|
185
|
-
)
|
299
|
+
))
|
186
300
|
end
|
187
301
|
end
|
188
302
|
|
@@ -260,92 +374,4 @@ module CemAcpt
|
|
260
374
|
CemAcpt::Logging.new_log_config(logdev: logdev, shift_age: shift_age, shift_size: shift_size, level: level, formatter: formatter, datetime_format: datetime_format)
|
261
375
|
end
|
262
376
|
end
|
263
|
-
|
264
|
-
module LoggingAsync
|
265
|
-
require 'concurrent-ruby'
|
266
|
-
|
267
|
-
class << self
|
268
|
-
def log_write_thread
|
269
|
-
@log_write_thread ||= Concurrent::SingleThreadExecutor.new
|
270
|
-
end
|
271
|
-
|
272
|
-
def async_debug(message, prefix = nil)
|
273
|
-
msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
|
274
|
-
CemAcpt::Logging.logger.debug(msg)
|
275
|
-
end
|
276
|
-
|
277
|
-
def async_info(message, prefix = nil)
|
278
|
-
msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
|
279
|
-
CemAcpt::Logging.logger.info(msg)
|
280
|
-
end
|
281
|
-
|
282
|
-
def async_warn(message, prefix = nil)
|
283
|
-
msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
|
284
|
-
CemAcpt::Logging.logger.warn(msg)
|
285
|
-
end
|
286
|
-
|
287
|
-
def async_error(message, prefix = nil)
|
288
|
-
msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
|
289
|
-
CemAcpt::Logging.logger.error(msg)
|
290
|
-
end
|
291
|
-
|
292
|
-
def async_fatal(message, prefix = nil)
|
293
|
-
msg = prefix.nil? || prefix.empty? ? message : "#{prefix} #{message}"
|
294
|
-
CemAcpt::Logging.logger.fatal(msg)
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
# Provides class method wrappers for logging methods
|
299
|
-
def self.included(base)
|
300
|
-
class << base
|
301
|
-
def log_write_thread
|
302
|
-
CemAcpt::LoggingAsync.log_write_thread
|
303
|
-
end
|
304
|
-
|
305
|
-
def async_debug(message, prefix = nil)
|
306
|
-
CemAcpt::LoggingAsync.async_debug(message, prefix)
|
307
|
-
end
|
308
|
-
|
309
|
-
def async_info(message, prefix = nil)
|
310
|
-
CemAcpt::LoggingAsync.async_info(message, prefix)
|
311
|
-
end
|
312
|
-
|
313
|
-
def async_warn(message, prefix = nil)
|
314
|
-
CemAcpt::LoggingAsync.async_warn(message, prefix)
|
315
|
-
end
|
316
|
-
|
317
|
-
def async_error(message, prefix = nil)
|
318
|
-
CemAcpt::LoggingAsync.async_error(message, prefix)
|
319
|
-
end
|
320
|
-
|
321
|
-
def async_fatal(message, prefix = nil)
|
322
|
-
CemAcpt::LoggingAsync.async_fatal(message, prefix)
|
323
|
-
end
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
def log_write_thread
|
328
|
-
CemAcpt::LoggingAsync.log_write_thread
|
329
|
-
end
|
330
|
-
|
331
|
-
def async_debug(message, prefix = nil)
|
332
|
-
CemAcpt::LoggingAsync.async_debug(message, prefix)
|
333
|
-
end
|
334
|
-
|
335
|
-
def async_info(message, prefix = nil)
|
336
|
-
CemAcpt::LoggingAsync.async_info(message, prefix)
|
337
|
-
end
|
338
|
-
|
339
|
-
def async_warn(message, prefix = nil)
|
340
|
-
CemAcpt::LoggingAsync.async_warn(message, prefix)
|
341
|
-
end
|
342
|
-
|
343
|
-
def async_error(message, prefix = nil)
|
344
|
-
CemAcpt::LoggingAsync.async_error(message, prefix)
|
345
|
-
end
|
346
|
-
|
347
|
-
def async_fatal(message, prefix = nil)
|
348
|
-
CemAcpt::LoggingAsync.async_fatal(message, prefix)
|
349
|
-
end
|
350
|
-
end
|
351
377
|
end
|