console 1.23.7 → 1.25.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16dee5ad9914f3983622406c0e1b84d39a187c67c01bd29d31966e964b703e8c
4
- data.tar.gz: 0f175325d0d8e6bee203025a83917ab5309efa6a9e2995d242b0f99a3cceedf0
3
+ metadata.gz: f91958a87f7e55b773b421ba83cedd33c65d8668fc9be859b8a49af0ed93dcd4
4
+ data.tar.gz: fe5c31ba8acb886bbdde2c28b8b958f64dd0bc376d76fa89c79685a5239ab267
5
5
  SHA512:
6
- metadata.gz: 8909e58d31aa84e349f4d87aaa6f818baff0a2416ac2de6dd1961136c1d9380e570fc94d34c44cc7463c707222a56d5507b2dd0f58281c6503a5622aa7c04e30
7
- data.tar.gz: 857289e0809007816272b9270691bb07a1d657fe5cefeed73329406c6ececb63f4dec495ca70513702533bedd70666b557a5a31fa9216ec4b461c586f854cfcb
6
+ metadata.gz: bddf649708410d79d0d979342bdd98c1c4bb1687ddbe79c8b0f1da8de36aab881872bb3b086256083044e366627c111fd642146c6781d4bc669f9d26511cc4a4
7
+ data.tar.gz: 49a2cc984ba416722d712eaf8133e1ce1e8be95fd36f056e57a157539b6e1033f6a1a9074b9b7283510c2fa8214db3ba8bfa70de4971bd9fe0227e15a6dab32d
checksums.yaml.gz.sig CHANGED
Binary file
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023, by Samuel Williams.
4
+ # Copyright, 2023-2024, by Samuel Williams.
5
5
 
6
6
  module Console
7
+ # This namespace is reserved for logging adapters provided by other gems.
7
8
  module Adapter
8
9
  end
9
10
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
 
6
6
  require_relative 'filter'
7
7
 
@@ -40,7 +40,7 @@ module Console
40
40
  @verbose
41
41
  end
42
42
 
43
- def call(subject = nil, *arguments, severity: UNKNOWN, **options, &block)
43
+ def call(subject = nil, *arguments, severity: UNKNOWN, event: nil, **options, &block)
44
44
  message = {
45
45
  time: ::Time.now.iso8601,
46
46
  severity: severity,
@@ -51,6 +51,10 @@ module Console
51
51
  message[:subject] = subject
52
52
  end
53
53
 
54
+ if event
55
+ message[:event] = event.to_hash
56
+ end
57
+
54
58
  if arguments.any?
55
59
  message[:arguments] = arguments
56
60
  end
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022-2023, by Samuel Williams.
4
+ # Copyright, 2022-2024, by Samuel Williams.
5
5
 
6
6
  require 'logger'
7
7
 
8
8
  module Console
9
9
  module Compatible
10
+ # A compatible interface for {::Logger} which can be used with {Console}.
10
11
  class Logger < ::Logger
11
12
  class LogDevice
12
13
  def initialize(subject, output)
@@ -1,73 +1,74 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
  # Copyright, 2021, by Robert Schulze.
6
6
 
7
7
  require_relative 'generic'
8
8
 
9
9
  module Console
10
10
  module Event
11
+ # Represents a failure event.
12
+ #
13
+ # ```ruby
14
+ # Console::Event::Failure.for(exception).emit(self)
15
+ # ```
11
16
  class Failure < Generic
12
- def self.current_working_directory
17
+ def self.default_root
13
18
  Dir.getwd
14
19
  rescue # e.g. Errno::EMFILE
15
20
  nil
16
21
  end
17
22
 
18
23
  def self.for(exception)
19
- self.new(exception, self.current_working_directory)
24
+ self.new(exception, self.default_root)
20
25
  end
21
26
 
22
- def initialize(exception, root = nil)
27
+ def self.log(subject, exception, **options)
28
+ Console.error(subject, **self.for(exception).to_hash, **options)
29
+ end
30
+
31
+ def initialize(exception, root = Dir.getwd)
23
32
  @exception = exception
24
33
  @root = root
25
34
  end
26
35
 
27
- attr :exception
28
- attr :root
29
-
30
- def self.register(terminal)
31
- terminal[:exception_title] ||= terminal.style(:red, nil, :bold)
32
- terminal[:exception_detail] ||= terminal.style(:yellow)
33
- terminal[:exception_backtrace] ||= terminal.style(:red)
34
- terminal[:exception_backtrace_other] ||= terminal.style(:red, nil, :faint)
35
- terminal[:exception_message] ||= terminal.style(:default)
36
+ def to_hash
37
+ Hash.new.tap do |hash|
38
+ hash[:type] = :failure
39
+ hash[:root] = @root if @root
40
+ extract(@exception, hash)
41
+ end
36
42
  end
37
43
 
38
- def to_h
39
- {exception: @exception, root: @root}
44
+ def emit(*arguments, **options)
45
+ options[:severity] ||= :error
46
+
47
+ super
40
48
  end
41
49
 
42
- def format(output, terminal, verbose)
43
- format_exception(@exception, nil, output, terminal, verbose)
44
- end
50
+ private
45
51
 
46
- def format_exception(exception, prefix, output, terminal, verbose)
47
- lines = exception.message.lines.map(&:chomp)
48
-
49
- output.puts " #{prefix}#{terminal[:exception_title]}#{exception.class}#{terminal.reset}: #{lines.shift}"
52
+ def extract(exception, hash)
53
+ hash[:class] = exception.class.name
50
54
 
51
- lines.each do |line|
52
- output.puts " #{terminal[:exception_detail]}#{line}#{terminal.reset}"
53
- end
54
-
55
- root_pattern = /^#{@root}\// if @root
56
-
57
- exception.backtrace&.each_with_index do |line, index|
58
- path, offset, message = line.split(":", 3)
59
- style = :exception_backtrace
55
+ if exception.respond_to?(:detailed_message)
56
+ message = exception.detailed_message
60
57
 
61
- # Make the path a bit more readable
62
- if root_pattern and path.sub!(root_pattern, "").nil?
63
- style = :exception_backtrace_other
64
- end
58
+ # We want to remove the trailling exception class as we format it differently:
59
+ message.sub!(/\s*\(.*?\)$/, '')
65
60
 
66
- output.puts " #{index == 0 ? "→" : " "} #{terminal[style]}#{path}:#{offset}#{terminal[:exception_message]} #{message}#{terminal.reset}"
61
+ hash[:message] = message
62
+ else
63
+ hash[:message] = exception.message
67
64
  end
68
65
 
69
- if exception.cause
70
- format_exception(exception.cause, "Caused by ", output, terminal, verbose)
66
+ hash[:backtrace] = exception.backtrace
67
+
68
+ if cause = exception.cause
69
+ hash[:cause] = Hash.new.tap do |cause_hash|
70
+ extract(cause, cause_hash)
71
+ end
71
72
  end
72
73
  end
73
74
  end
@@ -1,22 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
 
6
6
  module Console
7
7
  module Event
8
8
  class Generic
9
- def self.register(terminal)
9
+ def as_json(...)
10
+ to_hash
10
11
  end
11
12
 
12
- def to_h
13
+ def to_json(...)
14
+ JSON.generate(as_json, ...)
13
15
  end
14
16
 
15
- def to_json(*arguments)
16
- JSON.generate([self.class, to_h], *arguments)
17
+ def to_s
18
+ to_json
17
19
  end
18
20
 
19
- def format(buffer, terminal)
21
+ def emit(*arguments, **options)
22
+ Console.call(*arguments, event: self, **options)
20
23
  end
21
24
  end
22
25
  end
@@ -1,12 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
 
6
6
  require_relative 'generic'
7
+ require_relative '../clock'
7
8
 
8
9
  module Console
9
10
  module Event
11
+ # Represents a spawn event.
12
+ #
13
+ # ```ruby
14
+ # Console.info(self, **Console::Event::Spawn.for("ls", "-l"))
15
+ #
16
+ # event = Console::Event::Spawn.for("ls", "-l")
17
+ # event.status = Process.wait
18
+ # ```
10
19
  class Spawn < Generic
11
20
  def self.for(*arguments, **options)
12
21
  # Extract out the command environment:
@@ -22,44 +31,38 @@ module Console
22
31
  @environment = environment
23
32
  @arguments = arguments
24
33
  @options = options
34
+
35
+ @start_time = Clock.now
36
+
37
+ @end_time = nil
38
+ @status = nil
25
39
  end
26
40
 
27
- attr :environment
28
- attr :arguments
29
- attr :options
30
-
31
- def chdir_string(options)
32
- if options and chdir = options[:chdir]
33
- " in #{chdir}"
41
+ def duration
42
+ if @end_time
43
+ @end_time - @start_time
34
44
  end
35
45
  end
36
46
 
37
- def self.register(terminal)
38
- terminal[:shell_command] ||= terminal.style(:blue, nil, :bold)
39
- end
40
-
41
- def to_h
47
+ def to_hash
42
48
  Hash.new.tap do |hash|
49
+ hash[:type] = :spawn
43
50
  hash[:environment] = @environment if @environment&.any?
44
51
  hash[:arguments] = @arguments if @arguments&.any?
45
52
  hash[:options] = @options if @options&.any?
53
+
54
+ hash[:status] = @status.to_i if @status
55
+
56
+ if duration = self.duration
57
+ hash[:duration] = duration
58
+ end
46
59
  end
47
60
  end
48
61
 
49
- def format(output, terminal, verbose)
50
- arguments = @arguments.flatten.collect(&:to_s)
51
-
52
- output.puts " #{terminal[:shell_command]}#{arguments.join(' ')}#{terminal.reset}#{chdir_string(options)}"
53
-
54
- if verbose and @environment
55
- @environment.each do |key, value|
56
- output.puts " export #{key}=#{value}"
57
- end
58
- end
62
+ def status=(status)
63
+ @end_time = Time.now
64
+ @status = status
59
65
  end
60
66
  end
61
67
  end
62
-
63
- # Deprecated.
64
- Shell = Event::Spawn
65
68
  end
data/lib/console/event.rb CHANGED
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
 
6
6
  require_relative 'event/spawn'
7
7
  require_relative 'event/failure'
8
- require_relative 'event/progress'
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
  # Copyright, 2019, by Bryan Powell.
6
6
  # Copyright, 2020, by Michael Adams.
7
7
  # Copyright, 2021, by Robert Schulze.
8
8
 
9
- require_relative 'buffer'
10
-
11
9
  module Console
12
10
  UNKNOWN = 'unknown'
13
11
 
@@ -37,7 +35,7 @@ module Console
37
35
 
38
36
  define_immutable_method(name) do |subject = nil, *arguments, **options, &block|
39
37
  if self.enabled?(subject, level)
40
- self.call(subject, *arguments, severity: name, **options, **@options, &block)
38
+ self.call(subject, *arguments, severity: name, **@options, **options, &block)
41
39
  end
42
40
  end
43
41
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023, by Samuel Williams.
4
+ # Copyright, 2023-2024, by Samuel Williams.
5
5
 
6
6
  require_relative 'format/safe'
7
7
 
@@ -10,9 +10,5 @@ module Console
10
10
  def self.default
11
11
  Safe.new(format: ::JSON)
12
12
  end
13
-
14
- def self.default_json
15
- self.default
16
- end
17
13
  end
18
14
  end
@@ -1,17 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
  # Copyright, 2021, by Bryan Powell.
6
6
  # Copyright, 2021, by Robert Schulze.
7
7
 
8
8
  require_relative 'output'
9
9
  require_relative 'filter'
10
- require_relative 'progress'
11
-
10
+ require_relative 'event'
12
11
  require_relative 'resolver'
13
- require_relative 'terminal/logger'
14
- require_relative 'serialized/logger'
12
+ require_relative 'progress'
15
13
 
16
14
  require 'fiber/local'
17
15
 
@@ -67,12 +65,24 @@ module Console
67
65
  end
68
66
 
69
67
  def progress(subject, total, **options)
70
- Progress.new(self, subject, total, **options)
68
+ options[:severity] ||= :info
69
+
70
+ Progress.new(subject, total, **options)
71
+ end
72
+
73
+ def error(subject, *arguments, **options, &block)
74
+ # This is a special case where we want to create a failure event from an exception.
75
+ # It's common to see `Console.error(self, exception)` in code.
76
+ if arguments.first.is_a?(Exception)
77
+ exception = arguments.shift
78
+ options[:event] = Event::Failure.for(exception)
79
+ end
80
+
81
+ super
71
82
  end
72
83
 
73
- # @deprecated Use `fatal` instead.
74
- def failure(subject, exception, *arguments, &block)
75
- self.fatal(subject, exception, *arguments, &block)
84
+ def failure(subject, exception, **options)
85
+ error(subject, event: Event::Failure.for(exception), **options)
76
86
  end
77
87
  end
78
88
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2022, by Samuel Williams.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
- require_relative 'xterm'
7
- require_relative 'json'
6
+ require_relative 'terminal'
7
+ require_relative 'serialized'
8
8
 
9
9
  module Console
10
10
  module Output
@@ -13,9 +13,9 @@ module Console
13
13
  output ||= $stderr
14
14
 
15
15
  if output.tty?
16
- XTerm.new(output, **options)
16
+ Terminal.new(output, **options)
17
17
  else
18
- JSON.new(output, **options)
18
+ Serialized.new(output, **options)
19
19
  end
20
20
  end
21
21
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023, by Samuel Williams.
4
+ # Copyright, 2023-2024, by Samuel Williams.
5
5
 
6
6
  module Console
7
7
  module Output
@@ -1,17 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2022, by Samuel Williams.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
- require_relative '../serialized/logger'
6
+ require_relative 'wrapper'
7
7
 
8
8
  module Console
9
9
  module Output
10
- class Sensitive
11
- def initialize(output, **options)
12
- @output = output
13
- end
14
-
10
+ class Sensitive < Wrapper
15
11
  REDACT = /
16
12
  phone
17
13
  | email
@@ -38,8 +34,14 @@ module Console
38
34
  | password
39
35
  /xi
40
36
 
37
+ def initialize(output, redact: REDACT, **options)
38
+ super(output, **options)
39
+
40
+ @redact = redact
41
+ end
42
+
41
43
  def redact?(text)
42
- text.match?(REDACT)
44
+ text.match?(@redact)
43
45
  end
44
46
 
45
47
  def redact_hash(arguments, filter)
@@ -96,7 +98,7 @@ module Console
96
98
  arguments = redact_array(arguments, filter)
97
99
  end
98
100
 
99
- @output.call(subject, *arguments, **options)
101
+ super(subject, *arguments, **options)
100
102
  end
101
103
  end
102
104
  end
@@ -1,34 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
 
6
- require_relative '../buffer'
7
- require_relative '../filter'
8
6
  require_relative '../format'
9
-
10
7
  require 'time'
11
-
12
8
  require 'fiber/annotation'
13
9
 
14
10
  module Console
15
- module Serialized
16
- class Logger
17
- def initialize(io = $stderr, format: Format.default, verbose: false, **options)
18
- @io = io
19
- @start = Time.now
11
+ module Output
12
+ class Serialized
13
+ def initialize(output, format: Format.default, **options)
14
+ @io = output
20
15
  @format = format
21
- @verbose = verbose
22
16
  end
23
17
 
24
18
  attr :io
25
- attr :start
26
19
  attr :format
27
20
 
28
- def verbose!(value = true)
29
- @verbose = true
30
- end
31
-
32
21
  def dump(record)
33
22
  @format.dump(record)
34
23
  end
@@ -72,41 +61,12 @@ module Console
72
61
  record[:message] = message
73
62
  end
74
63
 
75
- if exception = find_exception(message)
76
- record[:error] = {
77
- kind: exception.class,
78
- message: exception.message,
79
- stack: format_stack(exception)
80
- }
81
- end
82
-
83
64
  record.update(options)
84
65
 
85
66
  @io.puts(self.dump(record))
86
67
  end
87
-
88
- private
89
-
90
- def find_exception(message)
91
- message.find{|part| part.is_a?(Exception)}
92
- end
93
-
94
- def format_stack(exception)
95
- buffer = StringIO.new
96
- format_backtrace(exception, buffer)
97
- return buffer.string
98
- end
99
-
100
- def format_backtrace(exception, buffer)
101
- buffer.puts exception.backtrace
102
-
103
- if exception = exception.cause
104
- buffer.puts
105
- buffer.puts "Caused by: #{exception.class} #{exception.message}"
106
-
107
- format_backtrace(exception, buffer)
108
- end
109
- end
110
68
  end
69
+
70
+ JSON = Serialized
111
71
  end
112
72
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022-2023, by Samuel Williams.
4
+ # Copyright, 2022-2024, by Samuel Williams.
5
5
 
6
6
  module Console
7
7
  module Output
@@ -18,9 +18,9 @@ module Console
18
18
  @outputs.each{|output| output.verbose!(value)}
19
19
  end
20
20
 
21
- def call(level, subject = nil, *arguments, **options, &block)
21
+ def call(...)
22
22
  @outputs.each do |output|
23
- output.call(level, subject, *arguments, **options, &block)
23
+ output.call(...)
24
24
  end
25
25
  end
26
26
  end