class Console::Output::Terminal
def self.start_at!(environment = ENV)
def self.start_at!(environment = ENV) if time_string = environment[CONSOLE_START_AT] start_at = Time.parse(time_string) rescue nil end unless start_at start_at = Time.now environment[CONSOLE_START_AT] = start_at.to_s end return start_at end
def build_prefix(name)
def build_prefix(name) if @verbose "#{time_offset_prefix} #{name.rjust(8)}" else time_offset_prefix end end
def call(subject = nil, *arguments, name: nil, severity: UNKNOWN, event: nil, **options, &block)
def call(subject = nil, *arguments, name: nil, severity: UNKNOWN, event: nil, **options, &block) width = @terminal.width prefix = build_prefix(name || severity.to_s) indent = " " * prefix.size buffer = Buffer.new("#{indent}| ") indent_size = buffer.prefix.size format_subject(severity, prefix, subject, buffer) arguments.each do |argument| format_argument(argument, buffer) end if block_given? if block.arity.zero? format_argument(yield, buffer) else yield(buffer, @terminal) end end if event format_event(event, buffer, width - indent_size) end if options&.any? format_options(options, buffer) end @io.write buffer.string end
def default_suffix(object = nil)
def default_suffix(object = nil) buffer = +"" if @verbose if annotation = Fiber.current.annotation # While typically annotations should be strings, that is not always the case. annotation = annotation.to_s # If the annotation is empty, we don't want to print it, as it will look like a formatting bug. if annotation.size > 0 buffer << ": #{@terminal[:annotation]}#{annotation}#{@terminal.reset}" end end end buffer << " #{@terminal[:logger_suffix]}" if object buffer << "[oid=0x#{object.object_id.to_s(16)}] " end buffer << "[ec=0x#{Fiber.current.object_id.to_s(16)}] [pid=#{Process.pid}] [#{::Time.now}]#{@terminal.reset}" return buffer end
def format_argument(argument, output)
def format_argument(argument, output) argument.to_s.each_line do |line| output.puts line end end
def format_event(event, buffer, width)
def format_event(event, buffer, width) event = event.to_hash type = event[:type] if formatter = @formatters[type] formatter.format(event, buffer, verbose: @verbose, width: width) else format_value(::JSON.pretty_generate(event), buffer) end end
def format_object_subject(severity, prefix, subject, output)
def format_object_subject(severity, prefix, subject, output) prefix_style = @terminal[severity] if @verbose suffix = default_suffix(subject) end prefix = "#{prefix_style}#{prefix}:#{@terminal.reset} " output.puts "#{@terminal[:subject]}#{subject.class}#{@terminal.reset}#{suffix}", prefix: prefix end
def format_options(options, output)
def format_options(options, output) format_value(::JSON.pretty_generate(options), output) end
def format_string_subject(severity, prefix, subject, output)
def format_string_subject(severity, prefix, subject, output) prefix_style = @terminal[severity] if @verbose suffix = default_suffix end prefix = "#{prefix_style}#{prefix}:#{@terminal.reset} " output.puts "#{@terminal[:subject]}#{subject}#{@terminal.reset}#{suffix}", prefix: prefix end
def format_subject(severity, prefix, subject, buffer)
def format_subject(severity, prefix, subject, buffer) if subject.is_a?(String) format_string_subject(severity, prefix, subject, buffer) elsif subject.is_a?(Module) format_string_subject(severity, prefix, subject.to_s, buffer) else format_object_subject(severity, prefix, subject, buffer) end end
def format_value(value, output)
def format_value(value, output) string = value.to_s string.each_line do |line| line.chomp! output.puts "#{@terminal[:value]}#{line}#{@terminal.reset}" end end
def initialize(output, verbose: nil, start_at: Terminal.start_at!, format: nil, **options)
def initialize(output, verbose: nil, start_at: Terminal.start_at!, format: nil, **options) @io = output @start_at = start_at @terminal = format.nil? ? Console::Terminal.for(@io) : format.new(@io) if verbose.nil? @verbose = !@terminal.colors? else @verbose = verbose end @terminal[:logger_suffix] ||= @terminal.style(:white, nil, :faint) @terminal[:subject] ||= @terminal.style(nil, nil, :bold) @terminal[:debug] = @terminal.style(:cyan) @terminal[:info] = @terminal.style(:green) @terminal[:warn] = @terminal.style(:yellow) @terminal[:error] = @terminal.style(:red) @terminal[:fatal] = @terminal[:error] @terminal[:annotation] = @terminal.reset @terminal[:value] = @terminal.style(:blue) @formatters = {} self.register_formatters end
def register_formatters(namespace = Console::Terminal::Formatter)
def register_formatters(namespace = Console::Terminal::Formatter) namespace.constants.each do |name| formatter = namespace.const_get(name) @formatters[formatter::KEY] = formatter.new(@terminal) end end
def time_offset_prefix
def time_offset_prefix Clock.formatted_duration(Time.now - @start_at).rjust(6) end
def verbose!(value = true)
def verbose!(value = true) @verbose = value end