cem_acpt 0.2.4 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|