litter is a free, open source monitoring & observability project written in Go and released under MIT. It has 1,655 GitHub stars, 61 forks and 17 open issues, and was last pushed 6 months ago. On this registry it ranks #191 of 270 tracked projects in Monitoring & Observability, with 5 head-to-head comparisons available. It gained 2 stars over the last 30 tracked days.

What is litter?

Litter is a Go pretty-printer library that renders data structures as syntactically correct Go literals, built for developers who need readable output while debugging and stable, comparable snapshots in unit tests.

What it is

Litter lives in the Go module ecosystem as github.com/sanity-io/litter, published by Sanity, the company behind the headless CMS construction kit. The version in this registry is written in Go, carries the MIT licence, and sits under Infrastructure & Operations / Monitoring & Observability. It is a library rather than a service: it is imported into a Go program, and the imported package provides two entry points, litter.Dump(value, ...) for writing a rendering to STDOUT and litter.Sdump(value, ...) for returning that same rendering as a string.

The problem it solves is the readability and repeatability of dumped Go values. The default fmt printing of a nested struct is terse and ambiguous, and the alternatives that preceded it tended toward verbosity. Litter was inspired by Spew, but deliberately focuses on terseness and readability instead. Its README also frames it as an alternative to JSON or YAML when a project needs example or snapshot data, because Litter produces consistent, sorted output that can be compared as a plain string. That makes it a replacement for hand-rolled debug printing, for ad-hoc JSON and YAML test fixtures, and for the heavier dump output of Spew.

Key capabilities

  • litter.Dump(value, ...) dumps a data structure to STDOUT, and litter.Sdump(value, ...) returns the dump as a string.
  • Output is syntactically correct Go, so a dumped value can be read as a literal rather than parsed as a bespoke format.
  • Circular references and aliasing are detected, and further references to the same object are replaced with placeholder aliases such as p0.
  • Global configuration through litter.Config, including StripPackageNames, HidePrivateFields, FieldExclusions, HomePackage, Separator, Compact, and DisablePointerReplacement.
  • litter.Config.FieldExclusions takes a regular expression and ships set to hide fields generated by protoc-gen-go via the pattern ^(XXX_.*)$.
  • litter.Options provides a local, compartmentalised configuration for a single dumper instead of mutating global state.
  • The Dumper interface, implemented as LitterDump(w io.Writer), lets a type control its own rendering.

Who uses it and how

  • Go teams writing snapshot tests: the first run writes output to a file such as testdata.txt, and later runs read the file back and compare the strings directly.
  • Developers debugging nested structs, pointers, and slices, using Dump to emit full newlines, indentation, type, and aliasing information.
  • Teams that generate Go types with protoc-gen-go, where the default FieldExclusions setting suppresses the generated XXX_ fields from appearing in dumps.
  • Library and application authors who expose their own types and want stable, readable output, by implementing the Dumper interface and writing to the provided io.Writer.
  • Projects that need to keep dump settings separate between components, using litter.Options rather than the shared global litter.Config.

Getting started

Install with go get -u github.com/sanity-io/litter, add the import line import "github.com/sanity-io/litter" to the file in use, and then call litter.Dump(myVar1) or str := litter.Sdump(myVar1).

How it compares

The README names Spew as the direct inspiration, and positions Litter against it on terseness and readability rather than on feature count. It also names JSON and YAML as the formats it can replace for snapshot and example data, on the grounds that Litter output is consistent and can be compared as a string without a serialisation step. No paid or hosted products are listed as being replaced, so the comparison here is confined to those named tools.

When to use it — and when not to

Litter is a library with no database, storage, or SMTP component to operate, so adoption means adding a dependency and nothing more; the cost is in configuration discipline, because litter.Config is global mutable state and litter.Options exists specifically to compartmentalise it. It should not be chosen when the goal is a wire format or data interchange with non-Go consumers, since its output is Go syntax. Prospective users should also note that the README's custom dumper section ends mid-sentence, the project carries a Travis CI badge, and it has 17 open issues with no release cadence stated, so the documentation and maintenance signal are thinner than the API surface suggests.

project readme (upstream, from github) — read inline

!Build Status

Litter

Litter is a pretty printer library for Go data structures to aid in debugging and testing.


Litter is provided by


Sanity: The Headless CMS Construction Kit

Litter named for the fact that it outputs literals, which you litter your output with. As a side benefit, all Litter output is syntactically correct Go. You can use Litter to emit data during debug, and it's also really nice for "snapshot data" in unit tests, since it produces consistent, sorted output. Litter was inspired by Spew, but focuses on terseness and readability.

Basic example

This:

type Person struct {
	Name   string
	Age    int
	Parent *Person
}

litter.Dump(Person{
	Name:   "Bob",
	Age:    20,
	Parent: &Person{
		Name: "Jane",
		Age:  50,
	},
})

will output:

Person{
	Name: "Bob",
	Age: 20,
	Parent: &Person{
		Name: "Jane",
		Age: 50,
	},
}

Use in tests

Litter is a great alternative to JSON or YAML for providing "snapshots" or example data. For example:

func TestSearch(t *testing.T) {
	result := DoSearch()

	actual := litterOpts.Sdump(result)
	expected, err := ioutil.ReadFile("testdata.txt")
	if err != nil {
		// First run, write test data since it doesn't exist
		if !os.IsNotExist(err) {
			t.Error(err)
		}
		ioutil.Write("testdata.txt", actual, 0644)
		actual = expected
	}
	if expected != actual {
		t.Errorf("Expected %s, got %s", expected, actual)
	}
}

The first run will use Litter to write the data to testdata.txt. On subsequent runs, the test will compare the data. Since Litter always provides a consistent view of a value, you can compare the strings directly.

Circular references

Litter detects circular references or aliasing, and will replace additional references to the same object with aliases. For example:

type Circular struct {
	Self *Circular
}

selfref := Circular{}
selfref.Self = &selfref

litter.Dump(selfref)

will output:

Circular { // p0
	Self: p0,
}

Installation

$ go get -u github.com/sanity-io/litter

Quick start

Add this import line to the file you're working in:

import "github.com/sanity-io/litter"

To dump a variable with full newlines, indentation, type, and aliasing information, use Dump or Sdump:

litter.Dump(myVar1)
str := litter.Sdump(myVar1)

litter.Dump(value, ...)

Dumps the data structure to STDOUT.

litter.Sdump(value, ...)

Returns the dump as a string

Configuration

You can configure litter globally by modifying the default litter.Config

// Strip all package names from types
litter.Config.StripPackageNames = true

// Hide private struct fields from dumped structs
litter.Config.HidePrivateFields = true

// Hide fields matched with given regexp if it is not nil. It is set up to hide fields generate with protoc-gen-go
litter.Config.FieldExclusions = regexp.MustCompile(`^(XXX_.*)$`)

// Sets a "home" package. The package name will be stripped from all its types
litter.Config.HomePackage = "mypackage"

// Sets separator used when multiple arguments are passed to Dump() or Sdump().
litter.Config.Separator = "\n"

// Use compact output: strip newlines and other unnecessary whitespace
litter.Config.Compact = true

// Prevents duplicate pointers from being replaced by placeholder variable names (except in necessary, in the case
// of circular references)
litter.Config.DisablePointerReplacement = true

litter.Options

Allows you to configure a local configuration of litter to allow for proper compartmentalization of state at the expense of some comfort:

	sq := litter.Options {
		HidePrivateFields: true,
		HomePackage: "thispack",
		Separator: " ",
	}

	sq.Dump("dumped", "with", "local", "settings")

Custom dumpers

Implement the interface Dumper on your types to take control of how your type is dumped.

type Dumper interface {
	LitterDump(w io.Writer)
}

Just write your custom dump to the provided stream, using multiple lines divided by "\n" if you need. Litter might indent your output according to context, and optionally decorate your first line with a pointer comment where appropriate.

A couple of examples from the test suite:

type CustomMultiLineDumper struct {}

func (cmld *CustomMultiLineDumper) LitterDump(w io.Writer) {
	w.Write([]byte("{\n  multi\n  line\n}"))
}

type CustomSingleLineDumper int

func (csld CustomSingleLineDumper) LitterDump(w io.Writer) {
	w.Write([]byte(""))
}

Frequently asked questions

Is litter free to use?

litter is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does litter do?

Litter is a pretty printer library for Go data structures to aid in debugging and testing.

What is litter written in?

litter is primarily written in Go. Its source is publicly available at https://github.com/sanity-io/litter, and it has 1,655 GitHub stars.