console 1.10.1 → 1.13.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: d9156ec2b29460187235a07fa3d66c53a008c78987873413415a8f66d0d3ccb6
4
- data.tar.gz: e27d6622b77fc3b5b2c616523956f67ee33e1cc0243d4d8313242e88cb8fe007
3
+ metadata.gz: cdbb00255589a49fe7d7d8e94361816a0316ae40f88a96b29b3dae6b0b243a05
4
+ data.tar.gz: 935c2a4dba52698495892e0dd858c2a6654e154d1edeb07d7b9dbacb52ddaea9
5
5
  SHA512:
6
- metadata.gz: ae3e4e8c9aba59786238e9ff0ba6e5568d891aae95f7dbe2276875a62ef895def36fc31d33aced9ed287897374d96d9678a1c7c0f0d045eca2b2e6f4f56f8fbb
7
- data.tar.gz: ec3bba369b4bad6139a1509fe51665b7d75df5af2a4176db6f6abe73e64753a86eca97aa11852cc4073db0645fbe9d3b32981b8507a46409f6c4a4cb43071910
6
+ metadata.gz: 208d13a845fef65da3942b11326f9cfb4c0f21b82a97a839dc010261f662ae7483931a510f131141c28b25e8540ff99d7e920118585c167897c4765b80b0e92f
7
+ data.tar.gz: 388d12e41c387d37fe4191c49017ca77784d8c397f526a708038d67ef3b35cab87fd0a9aff837c2a9c34227d1a672e7a61027bdc08b2b5fd833c13a179d81326
data/lib/console.rb CHANGED
@@ -20,6 +20,7 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  # THE SOFTWARE.
22
22
 
23
+ require_relative 'console/version'
23
24
  require_relative 'console/logger'
24
25
 
25
26
  module Console
@@ -33,6 +33,10 @@ module Console
33
33
  @buffer.last
34
34
  end
35
35
 
36
+ def include?(pattern)
37
+ JSON.dump(@buffer).include?(pattern)
38
+ end
39
+
36
40
  def clear
37
41
  @buffer.clear
38
42
  end
@@ -0,0 +1,50 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module Console
22
+ module Clock
23
+ def self.formatted_duration(duration)
24
+ if duration < 60.0
25
+ return "#{duration.round(2)}s"
26
+ end
27
+
28
+ duration /= 60.0
29
+
30
+ if duration < 60.0
31
+ return "#{duration.round}m"
32
+ end
33
+
34
+ duration /= 60.0
35
+
36
+ if duration < 60.0
37
+ return "#{duration.round(1)}h"
38
+ end
39
+
40
+ duration /= 24.0
41
+
42
+ return "#{duration.round(1)}d"
43
+ end
44
+
45
+ # Get the current elapsed monotonic time.
46
+ def self.now
47
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
48
+ end
49
+ end
50
+ end
@@ -47,6 +47,8 @@ module Console
47
47
  terminal[:exception_title] ||= terminal.style(:red, nil, :bold)
48
48
  terminal[:exception_detail] ||= terminal.style(:yellow)
49
49
  terminal[:exception_backtrace] ||= terminal.style(:red)
50
+ terminal[:exception_backtrace_other] ||= terminal.style(:red, nil, :faint)
51
+ terminal[:exception_message] ||= terminal.style(:default)
50
52
  end
51
53
 
52
54
  def to_h
@@ -66,13 +68,18 @@ module Console
66
68
  output.puts " #{terminal[:exception_detail]}#{line}#{terminal.reset}"
67
69
  end
68
70
 
71
+ root_pattern = /^#{@root}\// if @root
72
+
69
73
  exception.backtrace&.each_with_index do |line, index|
70
74
  path, offset, message = line.split(":")
75
+ style = :exception_backtrace
71
76
 
72
77
  # Make the path a bit more readable
73
- path.gsub!(/^#{@root}\//, "./") if @root
78
+ if root_pattern and path.sub!(root_pattern, "").nil?
79
+ style = :exception_backtrace_other
80
+ end
74
81
 
75
- output.puts " #{index == 0 ? "→" : " "} #{terminal[:exception_backtrace]}#{path}:#{offset}#{terminal.reset} #{message}"
82
+ output.puts " #{index == 0 ? "→" : " "} #{terminal[style]}#{path}:#{offset}#{terminal[:exception_message]} #{message}#{terminal.reset}"
76
83
  end
77
84
 
78
85
  if exception.cause
@@ -0,0 +1,42 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'metric'
22
+ require_relative '../clock'
23
+
24
+ module Console
25
+ module Event
26
+ class Enter < Generic
27
+ def initialize(name)
28
+ @name = name
29
+ end
30
+
31
+ def format(output, terminal, verbose)
32
+ output.puts "→ #{@name}"
33
+ end
34
+ end
35
+
36
+ class Exit < Metric
37
+ def value_string
38
+ "← #{@name} took #{Clock.formatted_duration(@value)}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -19,6 +19,7 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require_relative 'generic'
22
+ require_relative '../clock'
22
23
 
23
24
  module Console
24
25
  module Event
@@ -41,8 +42,16 @@ module Console
41
42
  {name: @name, value: @value, tags: @tags}
42
43
  end
43
44
 
45
+ def value_string
46
+ "#{@name}: #{@value}"
47
+ end
48
+
44
49
  def format(output, terminal, verbose)
45
- output.puts "#{@name}=#{@value} #{@tags.inspect}"
50
+ if @tags&.any?
51
+ output.puts "#{value_string} #{@tags.inspect}"
52
+ else
53
+ output.puts value_string
54
+ end
46
55
  end
47
56
  end
48
57
  end
@@ -26,11 +26,12 @@ module Console
26
26
  class Filter
27
27
  def self.[] **levels
28
28
  klass = Class.new(self)
29
+ min_level, max_level = levels.values.minmax
29
30
 
30
31
  klass.instance_exec do
31
32
  const_set(:LEVELS, levels)
32
- const_set(:MINIMUM_LEVEL, levels.values.min)
33
- const_set(:MAXIMUM_LEVEL, levels.values.max)
33
+ const_set(:MINIMUM_LEVEL, min_level)
34
+ const_set(:MAXIMUM_LEVEL, max_level)
34
35
 
35
36
  levels.each do |name, level|
36
37
  const_set(name.to_s.upcase, level)
@@ -102,7 +103,7 @@ module Console
102
103
  end
103
104
 
104
105
  def all!
105
- @level = -1
106
+ @level = self.class::MINIMUM_LEVEL - 1
106
107
  end
107
108
 
108
109
  # You can enable and disable logging for classes. This function checks if logging for a given subject is enabled.
@@ -18,11 +18,14 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require_relative 'output'
21
22
  require_relative 'filter'
23
+ require_relative 'measure'
22
24
  require_relative 'progress'
23
25
 
24
26
  require_relative 'resolver'
25
27
  require_relative 'terminal/logger'
28
+ require_relative 'serialized/logger'
26
29
 
27
30
  require 'fiber/local'
28
31
 
@@ -34,7 +37,7 @@ module Console
34
37
  # You can also specify CONSOLE_LEVEL=debug or CONSOLE_LEVEL=info in environment.
35
38
  # https://mislav.net/2011/06/ruby-verbose-mode/ has more details about how it all fits together.
36
39
  def self.default_log_level(env = ENV)
37
- if level = (env['CONSOLE_LEVEL'] || env['CONSOLE_LOG_LEVEL'])
40
+ if level = env['CONSOLE_LEVEL']
38
41
  LEVELS[level.to_sym] || level.to_i
39
42
  elsif $DEBUG
40
43
  DEBUG
@@ -50,17 +53,25 @@ module Console
50
53
  !$VERBOSE.nil? || env['CONSOLE_VERBOSE']
51
54
  end
52
55
 
53
- def self.default_logger(output, verbose: self.verbose?, level: self.default_log_level)
54
- terminal = Terminal::Logger.new(output, verbose: verbose)
56
+ def self.default_logger(output = $stderr, env = ENV, **options)
57
+ if options[:verbose].nil?
58
+ options[:verbose] = self.verbose?(env)
59
+ end
60
+
61
+ if options[:level].nil?
62
+ options[:level] = self.default_log_level(env)
63
+ end
64
+
65
+ output = Output.new(output, env, **options)
66
+ logger = self.new(output, **options)
55
67
 
56
- logger = self.new(terminal, verbose: verbose, level: level)
57
- resolver = Resolver.default_resolver(logger)
68
+ Resolver.default_resolver(logger)
58
69
 
59
70
  return logger
60
71
  end
61
72
 
62
73
  def self.local
63
- self.default_logger($stderr)
74
+ self.default_logger
64
75
  end
65
76
 
66
77
  DEFAULT_LEVEL = 1
@@ -73,8 +84,15 @@ module Console
73
84
  Progress.new(self, subject, total, **options)
74
85
  end
75
86
 
76
- # @deprecated Please use {progress}.
77
- alias measure progress
87
+ def measure(subject, name = "block", **tags, &block)
88
+ measure = Measure.new(self, subject, **tags)
89
+
90
+ if block_given?
91
+ return measure.duration(name, &block)
92
+ else
93
+ return measure
94
+ end
95
+ end
78
96
 
79
97
  def failure(subject, exception, *arguments, &block)
80
98
  fatal(subject, *arguments, Event::Failure.new(exception), &block)
@@ -0,0 +1,49 @@
1
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'event/measure'
22
+ require_relative 'clock'
23
+
24
+ module Console
25
+ class Measure
26
+ def initialize(output, subject, **tags)
27
+ @output = output
28
+ @subject = subject
29
+ @tags = tags
30
+ end
31
+
32
+ attr :tags
33
+
34
+ # Measure the execution of a block of code.
35
+ def duration(name, &block)
36
+ @output.info(@subject) {Event::Enter.new(name)}
37
+
38
+ start_time = Clock.now
39
+
40
+ result = yield(self)
41
+
42
+ duration = Clock.now - start_time
43
+
44
+ @output.info(@subject) {Event::Exit.new(name, duration, **@tags)}
45
+
46
+ return result
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,40 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'output/default'
22
+ require_relative 'output/json'
23
+ require_relative 'output/text'
24
+ require_relative 'output/xterm'
25
+
26
+ module Console
27
+ module Output
28
+ def self.new(output = nil, env = ENV, **options)
29
+ if names = env['CONSOLE_OUTPUT']
30
+ names = names.split(',').map(&:to_sym).reverse
31
+
32
+ names.inject(output) do |output, name|
33
+ Output.const_get(name).new(output, **options)
34
+ end
35
+ else
36
+ return Output::Default.new(output, **options)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'xterm'
22
+ require_relative 'json'
23
+
24
+ module Console
25
+ module Output
26
+ module Default
27
+ def self.new(output, **options)
28
+ output ||= $stderr
29
+
30
+ if output.tty?
31
+ XTerm.new(output, **options)
32
+ else
33
+ JSON.new(output, **options)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../serialized/logger'
22
+
23
+ module Console
24
+ module Output
25
+ module JSON
26
+ def self.new(output, **options)
27
+ Serialized::Logger.new(output, format: ::JSON, **options)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,118 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../serialized/logger'
22
+
23
+ module Console
24
+ module Output
25
+ class Sensitive
26
+ def initialize(output, **options)
27
+ @output = output
28
+ end
29
+
30
+ REDACT = /
31
+ phone
32
+ | email
33
+ | full_?name
34
+ | first_?name
35
+ | last_?name
36
+
37
+ | device_name
38
+ | user_agent
39
+
40
+ | zip
41
+ | address
42
+ | location
43
+ | latitude
44
+ | longitude
45
+
46
+ | ip
47
+ | gps
48
+
49
+ | sex
50
+ | gender
51
+
52
+ | token
53
+ | password
54
+ /xi
55
+
56
+ def redact?(text)
57
+ text.match?(REDACT)
58
+ end
59
+
60
+ def redact_hash(arguments, filter)
61
+ arguments.transform_values do |value|
62
+ redact(value, filter)
63
+ end
64
+ end
65
+
66
+ def redact_array(array, filter)
67
+ array.map do |value|
68
+ redact(value, filter)
69
+ end
70
+ end
71
+
72
+ def redact(argument, filter)
73
+ case argument
74
+ when String
75
+ if filter
76
+ filter.call(argument)
77
+ elsif redact?(argument)
78
+ "[REDACTED]"
79
+ else
80
+ argument
81
+ end
82
+ when Array
83
+ redact_array(argument, filter)
84
+ when Hash
85
+ redact_hash(argument, filter)
86
+ else
87
+ redact(argument.to_s, filter)
88
+ end
89
+ end
90
+
91
+ class Filter
92
+ def initialize(substitutions)
93
+ @substitutions = substitutions
94
+ @pattern = Regexp.union(substitutions.keys)
95
+ end
96
+
97
+ def call(text)
98
+ text.gsub(@pattern, @substitutions)
99
+ end
100
+ end
101
+
102
+ def call(subject = nil, *arguments, sensitive: true, **options, &block)
103
+ if sensitive
104
+ if sensitive.respond_to?(:call)
105
+ filter = sensitive
106
+ elsif sensitive.is_a?(Hash)
107
+ filter = Filter.new(sensitive)
108
+ end
109
+
110
+ subject = redact(subject, filter)
111
+ arguments = redact_array(arguments, filter)
112
+ end
113
+
114
+ @output.call(subject, *arguments, **options)
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../terminal/logger'
22
+
23
+ module Console
24
+ module Output
25
+ module Text
26
+ def self.new(output, **options)
27
+ Terminal::Logger.new(output, format: Terminal::Text, **options)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../terminal/logger'
22
+
23
+ module Console
24
+ module Output
25
+ module XTerm
26
+ def self.new(output, **options)
27
+ Terminal::Logger.new(output, format: Terminal::XTerm, **options)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -19,6 +19,7 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require_relative 'event/progress'
22
+ require_relative 'clock'
22
23
 
23
24
  module Console
24
25
  class Progress
@@ -93,7 +94,7 @@ module Console
93
94
 
94
95
  def to_s
95
96
  if estimated_remaining_time = self.estimated_remaining_time
96
- "#{@current}/#{@total} completed in #{formatted_duration(self.duration)}, #{formatted_duration(estimated_remaining_time)} remaining."
97
+ "#{@current}/#{@total} completed in #{Clock.formatted_duration(self.duration)}, #{Clock.formatted_duration(estimated_remaining_time)} remaining."
97
98
  else
98
99
  "#{@current}/#{@total} completed, waiting for estimate..."
99
100
  end
@@ -108,33 +109,13 @@ module Console
108
109
  end
109
110
 
110
111
  def output?
111
- if duration = duration_since_last_output
112
+ if remaining.zero?
113
+ return true
114
+ elsif duration = duration_since_last_output
112
115
  return duration > @minimum_output_duration
113
116
  else
114
117
  return true
115
118
  end
116
119
  end
117
-
118
- def formatted_duration(duration)
119
- if duration < 60.0
120
- return "#{duration.round(2)}s"
121
- end
122
-
123
- duration /= 60.0
124
-
125
- if duration < 60.0
126
- return "#{duration.round}m"
127
- end
128
-
129
- duration /= 60.0
130
-
131
- if duration < 60.0
132
- return "#{duration.round(1)}h"
133
- end
134
-
135
- duration /= 24.0
136
-
137
- return "#{duration.round(1)}d"
138
- end
139
120
  end
140
121
  end
@@ -26,6 +26,8 @@ module Console
26
26
  #
27
27
  # e.g. `CONSOLE_WARN=Acorn,Banana CONSOLE_DEBUG=Cat` will set the log level for the classes Acorn and Banana to `warn` and Cat to `debug`. This overrides the default log level.
28
28
  #
29
+ # You can enable all log levels for a given class by using `CONSOLE_ON=MyClass`. Similarly you can disable all logging using `CONSOLE_OFF=MyClass`.
30
+ #
29
31
  # @parameter logger [Logger] A logger instance to set the logging levels on.
30
32
  # @parameter env [Hash] The environment to read levels from.
31
33
  #
@@ -33,23 +35,42 @@ module Console
33
35
  # @returns [Resolver] If there were custom logging levels, then the created resolver is returned.
34
36
  def self.default_resolver(logger, env = ENV)
35
37
  # Find all CONSOLE_<LEVEL> variables from environment:
36
- levels = Logger::LEVELS
38
+ levels = logger.class::LEVELS
37
39
  .map{|label, level| [level, env["CONSOLE_#{label.upcase}"]&.split(',')]}
38
40
  .to_h
39
41
  .compact
40
42
 
43
+ off_klasses = env['CONSOLE_OFF']&.split(',')
44
+ on_klasses = env['CONSOLE_ON']&.split(',')
45
+
46
+ resolver = nil
47
+
41
48
  # If we have any levels, then create a class resolver, and each time a class is resolved, set the log level for that class to the specified level:
42
- if levels.any?
43
- resolver = Resolver.new
49
+ if on_klasses&.any?
50
+ resolver ||= Resolver.new
44
51
 
45
- levels.each do |level, names|
46
- resolver.bind(names) do |klass|
47
- logger.enable(klass, level)
48
- end
52
+ resolver.bind(on_klasses) do |klass|
53
+ logger.enable(klass, logger.class::MINIMUM_LEVEL - 1)
49
54
  end
55
+ end
56
+
57
+ if off_klasses&.any?
58
+ resolver ||= Resolver.new
50
59
 
51
- return resolver
60
+ resolver.bind(off_klasses) do |klass|
61
+ logger.disable(klass)
62
+ end
52
63
  end
64
+
65
+ levels.each do |level, names|
66
+ resolver ||= Resolver.new
67
+
68
+ resolver.bind(names) do |klass|
69
+ logger.enable(klass, level)
70
+ end
71
+ end
72
+
73
+ return resolver
53
74
  end
54
75
 
55
76
  def initialize
@@ -27,10 +27,11 @@ require 'json'
27
27
  module Console
28
28
  module Serialized
29
29
  class Logger
30
- def initialize(io = $stderr, format: JSON)
30
+ def initialize(io = $stderr, format: JSON, verbose: false, **options)
31
31
  @io = io
32
32
  @start = Time.now
33
33
  @format = format
34
+ @verbose = verbose
34
35
  end
35
36
 
36
37
  attr :io
@@ -38,6 +39,11 @@ module Console
38
39
  attr :format
39
40
 
40
41
  def verbose!(value = true)
42
+ @verbose = true
43
+ end
44
+
45
+ def dump(record)
46
+ @format.dump(record)
41
47
  end
42
48
 
43
49
  def call(subject = nil, *arguments, severity: UNKNOWN, **options, &block)
@@ -71,7 +77,7 @@ module Console
71
77
  end
72
78
  end
73
79
 
74
- @io.puts(@format.dump(record))
80
+ @io.puts(self.dump(record))
75
81
  end
76
82
  end
77
83
  end
@@ -25,6 +25,7 @@ require_relative 'text'
25
25
  require_relative 'xterm'
26
26
 
27
27
  require 'json'
28
+ require 'fiber'
28
29
 
29
30
  module Console
30
31
  module Terminal
@@ -54,11 +55,11 @@ module Console
54
55
  end
55
56
 
56
57
  class Logger
57
- def initialize(io = $stderr, verbose: nil, start_at: Terminal.start_at!, **options)
58
+ def initialize(io = $stderr, verbose: nil, start_at: Terminal.start_at!, format: nil, **options)
58
59
  @io = io
59
60
  @start_at = start_at
60
61
 
61
- @terminal = Terminal.for(io)
62
+ @terminal = format.nil? ? Terminal.for(io) : format.new(io)
62
63
 
63
64
  if verbose.nil?
64
65
  @verbose = !@terminal.colors?
@@ -66,7 +67,6 @@ module Console
66
67
  @verbose = verbose
67
68
  end
68
69
 
69
- @terminal[:logger_prefix] ||= @terminal.style(nil, nil, nil)
70
70
  @terminal[:logger_suffix] ||= @terminal.style(:white, nil, :faint)
71
71
  @terminal[:subject] ||= @terminal.style(nil, nil, :bold)
72
72
  @terminal[:debug] = @terminal.style(:cyan)
@@ -136,7 +136,7 @@ module Console
136
136
  def format_argument(argument, output)
137
137
  case argument
138
138
  when Exception
139
- Event::Failure.new(argument).format(output, @terminal, @verbose)
139
+ Event::Failure.for(argument).format(output, @terminal, @verbose)
140
140
  when Event::Generic
141
141
  argument.format(output, @terminal, @verbose)
142
142
  else
@@ -154,11 +154,21 @@ module Console
154
154
  end
155
155
  end
156
156
 
157
+ def default_suffix(object = nil)
158
+ buffer = +" #{@terminal[:logger_suffix]}"
159
+
160
+ if object
161
+ buffer << "[oid=0x#{object.object_id.to_s(16)}] "
162
+ end
163
+
164
+ buffer << "[ec=0x#{Fiber.current.object_id.to_s(16)}] [pid=#{Process.pid}] [#{::Time.now}]#{@terminal.reset}"
165
+ end
166
+
157
167
  def format_object_subject(severity, prefix, subject, output)
158
168
  prefix_style = @terminal[severity]
159
169
 
160
170
  if @verbose
161
- suffix = " #{@terminal[:logger_suffix]}[oid=0x#{subject.object_id.to_s(16)}] [pid=#{Process.pid}] [#{Time.now}]#{@terminal.reset}"
171
+ suffix = default_suffix(subject)
162
172
  end
163
173
 
164
174
  prefix = "#{prefix_style}#{prefix}:#{@terminal.reset} "
@@ -170,7 +180,7 @@ module Console
170
180
  prefix_style = @terminal[severity]
171
181
 
172
182
  if @verbose
173
- suffix = " #{@terminal[:logger_suffix]}[pid=#{Process.pid}] [#{Time.now}]#{@terminal.reset}"
183
+ suffix = default_suffix
174
184
  end
175
185
 
176
186
  prefix = "#{prefix_style}#{prefix}:#{@terminal.reset} "
@@ -187,15 +197,7 @@ module Console
187
197
  end
188
198
 
189
199
  def time_offset_prefix
190
- offset = Time.now - @start_at
191
- minutes = (offset/60).floor
192
- seconds = (offset - (minutes*60))
193
-
194
- if minutes > 0
195
- "#{minutes}m#{seconds.floor}s"
196
- else
197
- "#{seconds.round(2)}s"
198
- end.rjust(6)
200
+ Clock.formatted_duration(Time.now - @start_at).rjust(6)
199
201
  end
200
202
 
201
203
  def build_prefix(name)
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Console
22
- VERSION = "1.10.1"
22
+ VERSION = "1.13.0"
23
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: console
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.1
4
+ version: 1.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-01 00:00:00.000000000 Z
11
+ date: 2021-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fiber-local
@@ -104,14 +104,23 @@ files:
104
104
  - lib/console.rb
105
105
  - lib/console/buffer.rb
106
106
  - lib/console/capture.rb
107
+ - lib/console/clock.rb
107
108
  - lib/console/event.rb
108
109
  - lib/console/event/failure.rb
109
110
  - lib/console/event/generic.rb
111
+ - lib/console/event/measure.rb
110
112
  - lib/console/event/metric.rb
111
113
  - lib/console/event/progress.rb
112
114
  - lib/console/event/spawn.rb
113
115
  - lib/console/filter.rb
114
116
  - lib/console/logger.rb
117
+ - lib/console/measure.rb
118
+ - lib/console/output.rb
119
+ - lib/console/output/default.rb
120
+ - lib/console/output/json.rb
121
+ - lib/console/output/sensitive.rb
122
+ - lib/console/output/text.rb
123
+ - lib/console/output/xterm.rb
115
124
  - lib/console/progress.rb
116
125
  - lib/console/resolver.rb
117
126
  - lib/console/serialized/logger.rb