Rainbow is a log file colorer that act as a stream processor. Match and action rules are applied according to configuration to each line read from stdin, outputting them to stdout.
One instance where this tool may be useful is when developing applications that log lots of data to traditional log files, maybe an embedded system. When you are looking for certain patterns, coloring may greatly speed up troubleshooting. A custom configuration is quickly created to troubleshoot particular issues.
- Low latency, output buffering is per line only.
- Good performance. Care has been taken to minimize memory allocations. Currently most time is spent in Go's regexp package.
- Easy to create customized log filters.
Configuration files are searched for in ~/.config/rainbow/
with the suffix
.rainbow
automatically added. e.g. the command rainbow example
would try to
load the config file ~/.config/rainbow/example.rainbow
. Alternatively a full
path to a config file can be specified using the -config
flag.
-help Show help
-color Force color for non-TTY output
-config FILE Use config FILE
CONFIG Use config from ~/.config/rainbow/CONFIG.rainbow
go build
./rainbow -config testdata/config/example.rainbow < testdata/logs/example.log
A rather silly example that gives an idea about what the tool can do.
The basic configuration primitives (strings, lists, association lists) are described at https://github.com/johan-bolmsjo/saft/blob/master/README.md. Details specific to rainbow follows.
An example configuration first, coloring "testdata/config/example.rainbow".
{
filter: {
name: logLevel
filter: {
regexp: `\d \[((EMERG|ALERT|CRIT)|(ERROR)|(WARN))\]`
properties: {
2: { // emerg...
color: white
bgcolor: red
modifiers: bold
}
3: { // error
color: white
bgcolor: red
}
4: { // warn
color: black
bgcolor: yellow
}
}
}
// Color whole INFO logs gray as base color.
filter: {
name: info
regexp: `^([\d-:\. ]+ \[INFO\].*)$`
properties: {
1: {
color: iblack
}
}
}
// Color whole DEBUG logs cyan as base color.
filter: {
name: debug
regexp: `^([\d-:\. ]+ \[DEBUG\].*)$`
properties: {
1: {
color: cyan
}
}
}
}
filter: {
name: time
regexp: `^\d{4}-\d{2}-\d{2} (\d{2}:\d{2}:\d{2})`
}
filter: {
name: timeHighlight
regexpFrom: time
properties: {
1: {
modifiers: bold
}
}
}
filter: {
name: domain
regexp: `\[\w+\]\s+(\w+):`
properties: {
1: {
modifiers: bold
}
}
}
filter: {
name: variable
regexp: `([\w]+)=`
properties: {
1: {
modifiers: bold
}
}
}
apply: {
filters: [time logLevel]
}
apply: {
cond: [and [filter-match? time]
[not [equal? [filter-result time 0] [filter-result time 1]]]]
filters: timeHighlight
}
apply: {
cond: [not [filter-match? logLevel/info logLevel/debug]]
filters: domain
}
apply: {
filters: variable
}
}
filter: { ... }
Filters specify line matching in the form of a regexp and what coloring to perform uppon match. When an executed filter matches a line the fact that it matched and the result of the match is saved. The match status can be used to implement primitive flow control for what filters are to be applied.
Filters can be nested to group filters to apply. Named nested filters can be referred to using '/' as path separator.
name: NAME
Optional filter name to be able to apply or reference filter.
regexp: REGEXP
Regular expression of filter.
regexpFrom: REFERENCE
Use regexp defined by other filter. The match result is reused
if the regexp has already been executed by the referenced filter.
properties: { REGEXP_GROUP: { PROPERTIES } }
Properties to apply to individually matched regexp groups.
filter: { ... }
Nested filters
NAME:
May not contain '/' as it is used to reference nested filters.
REGEXP:
See https://golang.org/pkg/regexp/syntax/ for syntax.
REFERENCE:
Reference to named filter using '/' as nested filter separator.
e.g. 'logLevel/debug'.
REGEXP_GROUP:
Positive integer value identifying the matched regexp group counting
from 1. Groups are counted from the left with each opening parenthesis.
PROPERTIES:
See [Filter Properties]
Regexp match properties.
color: COLOR
Foreground color.
bgcolor: COLOR
Background color.
modifiers: MODIFIER | [MODIFIER ...]
One or more modifiers.
COLOR:
black red green yellow blue magenta cyan white
iblack ired igreen iyellow iblue imagenta icyan iwhite
ANSI defines 8 terminal colors and intense versions of the same. Intense
black yields a grayish color. Intense colors are prefixed with "i".
MODIFIER:
bold underline reverse blink
apply: { ... }
The order in which filters are applied are specified by apply clauses.
cond: EXPR
A lisp like expression that must evaluate to true for the apply clause
to be applied. See [Condition Expression].
filters: FILTER | [FILTER ...]
One ore more filters to apply if the condition evaluated to true.
A couple of built in functions are available to build an expression that evaluates to true or false.
Functions are lisp like in that the first list element is the function and the following elements its arguments.
[not arg]
Evaluates to true if arg is considered false, else false
[and arg...]
Evaluates to true with zero arguments.
Evaluates to the first argument considered false or the last argument.
[or arg...]
Evaluates to false with zero arguments.
Evaluates to the first argument considered true or the last argument.
[equal? arg1 arg2]
Evaluates to true if arg1 and arg2 are considered equal, else false.
[filter-match? filterName...]
Evaluates to true if any of the listed filters regexp matched, else false.
[filter-result filterName idx]
Get filter regexp match result as a list of strings of all matched regexp
groups, idx=0 is current match, idx=1 is previous