Rbcat
Introduction
Rbcat it's a CLI tool written in ruby which reads from standard input (STDIN), colorizes content by set of regex rules from a config file, and then writes it to standard output. Inspired by grcat. You can use rbcat in your ruby/ROR projects or as a standalone CLI tool (similar to grcat).
Install rbcat first: gem install rbcat
# rbcat_example.rb
description = "Rbcat it's a CLI tool written in ruby which reads from standard input (stdin),\ncolorizes content by set of regex rules from a config file, and then writes it\nto standard output.\nYou can use rbcat in your ruby/ROR projects or as a standalone CLI tool (similar to grcat).\n"
rules = {
ruby_word: {
regexp: /ruby/m,
color: :red
},
upcase_words: {
regexp: /[A-Z]{2,}/m,
color: :bold
},
inside_round_brackets: {
regexp: /\(.*?\)/m,
color: :cyan
},
gem_name: {
regexp: /rbcat/mi,
color: :green
}
}
require "rbcat"
colorizer = Rbcat::Colorizer.new(rules: rules)
puts colorizer.colorize(description)
Same using CLI:
# rbcat_config.yaml
---
:ruby_word:
:regexp: !ruby/regexp /ruby/m
:color: :red
:upcase_words:
:regexp: !ruby/regexp /[A-Z]{2,}/m
:color: :bold
:inside_round_brackets:
:regexp: !ruby/regexp /\(.*?\)/m
:color: :cyan
:gem_name:
:regexp: !ruby/regexp /rbcat/mi
:color: :green
$ echo "Rbcat it's a CLI tool written in ruby which reads from standard input (stdin),
colorizes content by set of regex rules from a config file, and then writes it
to standard output.
You can use rbcat in your ruby/ROR projects or as a standalone CLI tool (similar to grcat)." > description.txt
$ cat description.txt | rbcat --rules=rbcat_config.yaml # or
$ rbcat --rules=rbcat_config.yaml < description.txt
Configuration
Configure
You can configure Rbcat this way:
require "rbcat"
Rbcat.configure do |config|
require "yaml"
config.rules = YAML.load_file(File.("rbcat_config.yaml"))
config.predefined = [:logger]
end
And then everywhere in the ruby code just:
colorizer = Rbcat::Colorizer.new
puts colorizer.colorize("String to colorize")
Regex rules and colors
Config contains rules. Each rule has options. Example:
config = {
ruby_word: {
regexp: /ruby/m, # Regex mask (required)
color: :red, # Color (required)
once: true # Colorize only first occurrence, then skip others (optional, defalut value is false)
}
}
Predefined color sets
There are predefined sets of rules: jsonhash (colorizes strings that contain json or ruby hash) and logger (colorizes DEBUG, INFO, WARN and ERROR).
Usage: --predefined=jsonhash,logger
(CLI), or Rbcat::Colorizer.new(predefined: [:jsonhash, logger])
(ruby)
Let's see:
You can use both custom and predefined rules in the same time.
Colors
To print all available colors: $ rbcat --print_colors
Yaml config
Correct yaml config should be convertible to the Ruby hash. Here is an example of Rbcat config in both Ruby hash and yaml:
# rbcat_config.yaml
---
:ruby_word:
:regexp: !ruby/regexp /ruby/m
:color: :red
:upcase_words:
:regexp: !ruby/regexp /[A-Z]{2,}/m
:color: :bold
:inside_round_brackets:
:regexp: !ruby/regexp /\(.*?\)/m
:color: :cyan
:gem_name:
:regexp: !ruby/regexp /rbcat/mi
:color: :green
:once: true
# ruby hash
{
ruby_word: {
regexp: /ruby/m,
color: :red
},
upcase_words: {
regexp: /[A-Z]{2,}/m,
color: :bold
},
inside_round_brackets: {
regexp: /\(.*?\)/m,
color: :cyan
},
gem_name: {
regexp: /rbcat/mi,
color: :green
once: true
}
}
Using inside ruby project
It's a good idea to use rbcat with logger, so you can configure logger with colorizer once and then use it to print info to the console everywhere in the ruby code.
What we need to do is to define formatter for the logger, while creating one.
Using with a default ruby logger
Here is the simple example:
require "logger"
require "rbcat"
# configure rbcat first
Rbcat.configure do |config|
require "yaml"
config.rules = YAML.load_file(File.("rbcat_config.yaml"))
end
# define formatter
formatter = proc do |severity, datetime, progname, msg|
# default ruby logger layout:
output = "%s, [%s#%d] %5s -- %s: %s\n".freeze % [severity[0..0], datetime, $$, severity, progname, msg]
colorizer = Rbcat::Colorizer.new
colorizer.colorize(output)
end
# logger instance
logger = ::Logger.new(STDOUT, formatter: formatter)
logger.info "Message to colorize"
This is a nice example but almost isn't usable. In the normal ruby project, there are many classes and we need somehow have access to ours colorized logger instance from everywhere.
One of possible solutions is to use logger module and then include it to every class where we need it:
require "logger"
module Log
def logger
@logger ||= begin
::Logger.new(STDOUT, formatter: proc { |severity, datetime, progname, msg|
# default ruby logger layout
output = "%s, [%s#%d] %5s -- %s: %s\n".freeze % [severity[0..0], datetime, $$, severity, progname, msg]
colorizer = Rbcat::Colorizer.new
colorizer.colorize(output)
})
end
end
end
class SomeClass
include Log
def (msg)
logger.info msg
end
end
SomeClass.new.("Colorized message")
In the example above, we still need every time include Log module for every class. There is another more convenient way, using Log class with class logger methods:
require "logger"
require "forwardable"
class Log
class << self
extend Forwardable
delegate [:debug, :info, :warn, :error, :fatal] => :logger
def logger
@logger ||= begin
::Logger.new(STDOUT, formatter: proc { |severity, datetime, progname, msg|
# default ruby logger layout
output = "%s, [%s#%d] %5s -- %s: %s\n".freeze % [severity[0..0], datetime, $$, severity, progname, msg]
colorizer = Rbcat::Colorizer.new(predefined: [:logger])
colorizer.colorize(output)
})
end
end
end
end
With this approach, you can use colorized logger everywhere in the code.
Using with other logger libraries
require "logstash-logger"
require "rbcat"
logger = begin
formatter = proc { |severity, datetime, progname, msg|
output = "%s, [%s#%d] %5s -- %s: %s\n".freeze % [severity[0..0], datetime, $$, severity, progname, msg]
colorizer = Colorizer.new
colorizer.colorize(output)
}
LogStashLogger.new(type: :stdout, formatter: formatter)
end
logger.info "Info message to colorize"
Write clear log to the file and print colorized output to the console at the same time
Suddenly, default ruby logger can't output info to the several sources at the same time. But it can do Logstash for example:
require "logstash-logger"
require "rbcat"
logger = begin
formatter = proc { |severity, datetime, progname, msg|
output = "%s, [%s#%d] %5s -- %s: %s\n".freeze % [severity[0..0], datetime, $$, severity, progname, msg]
colorizer = Colorizer.new
colorizer.colorize(output)
}
outputs = [
{ type: :stdout, formatter: formatter },
{ type: :file, formatter: ::Logger::Formatter }
]
LogStashLogger.new(type: :multi_logger, formatter: formatter, outputs: outputs)
end
Q&A
I have a problem with a printing delay to the console using rbcat CLI
The same problem has grcat.
Example:
$ ruby -e "loop { puts 'INFO: This is info message'; sleep 0.1 }" | rbcat --predefined=logger
This code should print "INFO: This is info message" to the console every 0.1 seconds. But its don't, because of nature of STDOUT buffering. Here is a great article about it.
One of possible solutions is to use unbuffer tool (sudo apt install expect
for ubuntu):
$ unbuffer ruby -e "loop { puts 'INFO: This is info message'; sleep 0.1 }" | rbcat --predefined=logger
Now message prints to the console every 0.1 seconds without any delay.
I don't like colors which colorizer prints
Colorizer uses standard ANSI escape color codes, but each terminal can have an individual color palette. You can install additional color schemes, here for example themes for Gnome terminal.
I want to temporary disable colorizer
Define environment variable RBCAT_COLORIZER
with value false
. Or use RBCAT_COLORIZER=false
as the first parameter of command:
$ RBCAT_COLORIZER=false ruby grcat_example.rb
License
MIT