console 1.10.2 → 1.13.1

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: 8a777953a8fd749c475d101a185f5f45ab11005574c3f23e413c16058e22500b
4
- data.tar.gz: 778b3da976350ffc23d5aad6d142a9398ffbd0be6c8e5931c8c5c9b4f3c49f63
3
+ metadata.gz: a8c8affd2da03cdb084d7ef8af7c666409d12606c4459da493bf01a96d6b7bf3
4
+ data.tar.gz: d0af1c352b7b89f91b57ea737a5069282177be325f1bae82db3cb8de2ee76f57
5
5
  SHA512:
6
- metadata.gz: '0280c9d59cd23431f0c2ede8da258795790af325c64dacc63dc791a0682baad85348ddbd04ef954ad17ade370958a606b6f83e42ae4c8873eaea4bc11f4a409a'
7
- data.tar.gz: eb013fb3e45477510bf1cdf40fbfbba739be5cb3fda1f8366eb97c3898d31a7eb932a3c72c8a668e6e78f0ea57cc0d813207f50567168d0e6ea723ef09898533
6
+ metadata.gz: b9c453a4bfc44ac3b87c034e8854addb31d4a1c4d2cbdc9abf698f3dbf657ff99fa636d8e46dd0262e30cd195d1c3b12ba6d64c5e8b75668ec05d8b26952f494
7
+ data.tar.gz: 9ca8d1ade8c3b7fc3c4dd307a27e2e67f236526bd60619cb30cc7c70af179846050bd22d0e67d1e55f3019702052ffeb593c5eed1d1522c577e73cebc2e56795
@@ -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,15 +68,18 @@ module Console
66
68
  output.puts " #{terminal[:exception_detail]}#{line}#{terminal.reset}"
67
69
  end
68
70
 
69
- root_expr = /^#{@root}\// if @root
70
-
71
+ root_pattern = /^#{@root}\// if @root
72
+
71
73
  exception.backtrace&.each_with_index do |line, index|
72
74
  path, offset, message = line.split(":")
75
+ style = :exception_backtrace
73
76
 
74
77
  # Make the path a bit more readable
75
- path.sub!(root_expr, "./") if root_expr
76
-
77
- output.puts " #{index == 0 ? "→" : " "} #{terminal[:exception_backtrace]}#{path}:#{offset}#{terminal.reset} #{message}"
78
+ if root_pattern and path.sub!(root_pattern, "").nil?
79
+ style = :exception_backtrace_other
80
+ end
81
+
82
+ output.puts " #{index == 0 ? "→" : " "} #{terminal[style]}#{path}:#{offset}#{terminal[:exception_message]} #{message}#{terminal.reset}"
78
83
  end
79
84
 
80
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
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(',').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
@@ -116,27 +117,5 @@ module Console
116
117
  return true
117
118
  end
118
119
  end
119
-
120
- def formatted_duration(duration)
121
- if duration < 60.0
122
- return "#{duration.round(2)}s"
123
- end
124
-
125
- duration /= 60.0
126
-
127
- if duration < 60.0
128
- return "#{duration.round}m"
129
- end
130
-
131
- duration /= 60.0
132
-
133
- if duration < 60.0
134
- return "#{duration.round(1)}h"
135
- end
136
-
137
- duration /= 24.0
138
-
139
- return "#{duration.round(1)}d"
140
- end
141
120
  end
142
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.2"
22
+ VERSION = "1.13.1"
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.2
4
+ version: 1.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-27 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
@@ -140,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
149
  - !ruby/object:Gem::Version
141
150
  version: '0'
142
151
  requirements: []
143
- rubygems_version: 3.2.3
152
+ rubygems_version: 3.3.0.dev
144
153
  signing_key:
145
154
  specification_version: 4
146
155
  summary: Beautiful logging for Ruby.