Package neilotoole/jsoncolor
is a drop-in replacement for encoding/json
that outputs colorized JSON.
Why? Well, jq
colorizes its output by default, and color output is desirable for
many Go CLIs. This package performs colorization (and indentation) inline in the encoder.
From the example jc
app:
Get the package per the normal mechanism:
go get -u github.com/neilotoole/jsoncolor
Then:
package main
import (
"fmt"
"github.com/mattn/go-colorable"
json "github.com/neilotoole/jsoncolor"
"os"
)
func main() {
var enc *json.Encoder
// Note: this check will fail if running inside Goland (and
// other IDEs?) as IsColorTerminal will return false.
if json.IsColorTerminal(os.Stdout) {
// Safe to use color
out := colorable.NewColorable(os.Stdout) // needed for Windows
enc = json.NewEncoder(out)
// DefaultColors are similar to jq
clrs := json.DefaultColors()
// Change some values, just for fun
clrs.Bool = json.Color("\x1b[36m") // Change the bool color
clrs.String = json.Color{} // Disable the string color
enc.SetColors(clrs)
} else {
// Can't use color; but the encoder will still work
enc = json.NewEncoder(os.Stdout)
}
m := map[string]interface{}{
"a": 1,
"b": true,
"c": "hello",
}
if err := enc.Encode(m); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
To enable colorization, invoke enc.SetColors
.
The jsoncolor.Colors
struct holds color config. The zero value
and nil
are both safe for use (resulting in no colorization).
The DefaultColors
func returns a Colors
struct that produces results
similar to jq
:
// DefaultColors returns the default Colors configuration.
// These colors largely follow jq's default colorization,
// with some deviation.
func DefaultColors() *Colors {
return &Colors{
Null: Color("\x1b[2m"),
Bool: Color("\x1b[1m"),
Number: Color("\x1b[36m"),
String: Color("\x1b[32m"),
Key: Color("\x1b[34;1m"),
Bytes: Color("\x1b[2m"),
Time: Color("\x1b[32;2m"),
Punc: Color{}, // No colorization
}
}
As seen above, use the Color
zero value (Color{}
) to
disable colorization for that JSON element.
It can be inconvenient to use terminal codes, e.g. json.Color("\x1b[36m")
.
A helper package provides an adapter for fatih/color
.
// import "github.com/neilotoole/jsoncolor/helper/fatihcolor"
// import "github.com/fatih/color"
out := colorable.NewColorable(os.Stdout) // needed for Windows
enc = json.NewEncoder(out)
fclrs := fatihcolor.DefaultColors()
// Change some values, just for fun
fclrs.Number = color.New(color.FgBlue)
fclrs.String = color.New(color.FgCyan)
clrs := fatihcolor.ToCoreColors(fclrs)
enc.SetColors(clrs)
This package is a full drop-in for stdlib encoding/json
(thanks to the segmentio/encoding/json
pkg being a full drop-in).
To drop-in, just use an import alias:
import json "github.com/neilotoole/jsoncolor"
This package is an extract of sq
's JSON encoding
package, which itself is a fork of the segment.io/encoding
JSON
encoding package (aka segmentj
).
Note that the original jsoncolor
codebase was forked from Segment's codebase at v0.1.14
, so
the codebases are quite of out sync by now.
See ./cmd/jc
for a trivial CLI implementation that can accept JSON input,
and output that JSON in color.
# From project root
go install ./cmd/jc
cat ./testdata/sakila_actor.json | jc
Note that this package contains golang_bench_test.go
, which is inherited from segmentj
.
But we're mainly interested in benchmark_test.go:BenchmarkEncode
, which benchmarks the following:
- Stdlib
encoding/json
: Go 1.16 segmentj
:v0.1.14
, which was whenjsoncolor
was forked. The newersegmentj
code performs even better.neilotoole/jsoncolor
: (this package)v0.2.0
.nwidger/jsoncolor
:v0.3.0
, latest at time of benchmarks.
Note that two other Go JSON colorization packages (hokaccha/go-prettyjson
and
TylerBrock/colorjson
) are excluded from
these benchmarks because they do not provide a stdlib-compatible Encoder
impl.
$ go test -bench=BenchmarkEncode -benchtime="5s"
goarch: amd64
pkg: github.com/neilotoole/jsoncolor
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkEncode/stdlib_NoIndent-16 181 33047390 ns/op 8870685 B/op 120022 allocs/op
BenchmarkEncode/stdlib_Indent-16 124 48093178 ns/op 10470366 B/op 120033 allocs/op
BenchmarkEncode/segmentj_NoIndent-16 415 14658699 ns/op 3788911 B/op 10020 allocs/op
BenchmarkEncode/segmentj_Indent-16 195 30628798 ns/op 5404492 B/op 10025 allocs/op
BenchmarkEncode/neilotoole_NoIndent_NoColor-16 362 16522399 ns/op 3789034 B/op 10020 allocs/op
BenchmarkEncode/neilotoole_Indent_NoColor-16 303 20146856 ns/op 5460753 B/op 10021 allocs/op
BenchmarkEncode/neilotoole_NoIndent_Color-16 295 19989420 ns/op 10326019 B/op 10029 allocs/op
BenchmarkEncode/neilotoole_Indent_Color-16 246 24714163 ns/op 11996890 B/op 10030 allocs/op
BenchmarkEncode/nwidger_NoIndent_NoColor-16 10 541107983 ns/op 92934231 B/op 4490210 allocs/op
BenchmarkEncode/nwidger_Indent_NoColor-16 7 798088086 ns/op 117258321 B/op 6290213 allocs/op
BenchmarkEncode/nwidger_indent_NoIndent_Colo-16 10 542002051 ns/op 92935639 B/op 4490224 allocs/op
BenchmarkEncode/nwidger_indent_Indent_Color-16 7 799928353 ns/op 117259195 B/op 6290220 allocs/op
As always, take benchmarks with a large grain of salt, as they're based on a (small) synthetic benchmark.
More benchmarks would give a better picture (and note as well that the benchmarked segmentj
is an older version, v0.1.14
).
All that having been said, what can we surmise from these particular results?
segmentj
performs better thanstdlib
at all encoding tasks.jsoncolor
performs better thansegmentj
for indentation (which makes sense, as indentation is performed inline).jsoncolor
performs better thanstdlib
at all encoding tasks.
Again, trust these benchmarks at your peril. Create your own benchmarks for your own workload.
- The
.golangci.yml
linter settings have been fiddled with to hush linting issues inherited from thesegmentio
codebase at the time of forking. Thus, the linter report may not be of great use. In an ideal world, thejsoncolor
functionality would be ported to a more recent (and better-linted) version of thesegementio
codebase. - The
segmentio
encoder (at least as ofv0.1.14
) encodestime.Duration
as string, whilestdlib
outputs theint64
. This package followsstdlib
.
jq
: sine qua non.segmentio/encoding
:jsoncolor
is layered into Segment's JSON encoder. They did the hard work. Much gratitude to that team.sq
:jsoncolor
is effectively an extract of the code created specifically forsq
.mattn/go-colorable
: no project is complete withoutmattn
having played a role.fatih/color
: the color library.