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

Overview

!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("<custom>"))
}
Issues
  • "Latest" release is not the latest

    image

    https://github.com/sanity-io/litter/releases https://github.com/sanity-io/litter/tags

    It might be easier to just delete those two releases and keep only tags.

    opened by AlekSi 7
  • add colour syntax highlighting  (quick hack)

    add colour syntax highlighting (quick hack)

    I don't think you want to just merge this (it adds a bunch of dependencies, and you could do separately), but im adding just in case youre interested. (cc: #27 )

    opened by elliots 5
  • Fix pointers resued in maps key

    Fix pointers resued in maps key

    When I use liiter to dump golang.org/x/tools/go/packages.Package, I found litter repeats in an endless loop. Then I found that reused pointers in maps key is the reason.

    I fixed it and added the test TestSdump_RecursiveMaps.

    opened by ray2011 4
  • Pointers to simple types are not rendered as valid Go

    Pointers to simple types are not rendered as valid Go

    Consider the example:

    package main
    
    import (
    	"github.com/sanity-io/litter"
    )
    
    type T struct {
    	I *int
    }
    
    func main() {
    	i := 0
    	t := T{
    		I: &i,
    	}
    	litter.Dump(t)
    }
    

    This prints:

    main.T{
      I: &0,
    }
    

    which is not valid Go.

    When the type of a field is a simple type, like int, string, float64, etc. I would suggest that litter prints:

    main.T{
      I: litter.Int(0),
    }
    

    where litter.Int is defined as:

    package litter
    
    func Int(i int) *int { return &i }
    

    Similarly for other types.

    opened by twpayne 4
  • Option to hide autogenerated protobuf fields

    Option to hide autogenerated protobuf fields

    Hi!

    We are using gRPC (with protobuf) and the latest version of protoc-gen-go generates 3 service fields named

    XXX_NoUnkeyedLiteral
    XXX_unrecognized
    XXX_sizecache
    

    and it is getting annoying to see them in the output. So, I added special option to hide these fields which is set to true by default.

    I decided to filter out exactly these three fields instead of filtering out everything started from XXX_ as explicit is better than implicit.

    opened by sirkon 4
  • Add DumpFunc option

    Add DumpFunc option

    Adds new config option DumpFunc which allows overriding any value's formatting. I don't think this is ready as-is, but I'm posting an early iteration to get feedback.

    My usecase is printing a yaml ast for debugging (specifically the Node https://pkg.go.dev/gopkg.in/yaml.v3#Node). The Kind field doesn't implement fmt.Stringer so it just shows up as a number. In that case, I'd add a dump func like this

    package main
    
    import (
    	"fmt"
    	"io"
    	"reflect"
    
    	"gopkg.in/yaml.v3"
    )
    
    func KindString(k yaml.Kind) string {
    	switch k {
    	case yaml.DocumentNode:
    		return "DocumentNode"
    	case yaml.SequenceNode:
    		return "SequenceNode"
    	case yaml.ScalarNode:
    		return "MappingNode"
    	case yaml.ScalarNode:
    		return "ScalarNode"
    	case yaml.AliasNode:
    		return "AliasNode"
    	default:
    		return fmt.Sprintf("Unknown(%d)", k)
    	}
    }
    
    func main() {
    	cfg.DumpFunc = func(v reflect.Value, w io.Writer) bool {
    		if !v.CanInterface() {
    			return false
    		}
    		if k, ok := v.Interface().(yaml.Kind); ok {
    			io.WriteString(w, KindString(k))
    			return true
    		}
    		return false
    	}
    }
    
    opened by icholy 3
  • Issues/34: More compact array/map representation

    Issues/34: More compact array/map representation

    What:

    • Where the elements in a slice/array are primitive types; dump in a compact manner.
    • Where the elements(not keys) in a map are primitive types; dump in a compact manner

    Why:

    • Fixes: https://github.com/sanity-io/litter/issues/34
    opened by komuw 2
  • Prevent panic when dumping unexported map

    Prevent panic when dumping unexported map

    Fixes #18

    Tried some of the standard tricks for accessing unexported values but none worked. This fix isn't ideal because the resulting key order is non-deterministic but at least it doesn't panic.

    opened by bradleyjkemp 2
  • Test failure

    Test failure

    Running go test on master yields:

    --- FAIL: TestSdump_customDumper (0.00s)
        --- FAIL: TestSdump_customDumper/customDumper (0.00s)
            Error Trace:    dump_test.go:213
        	            	dump_test.go:171
        	Error:      	Strings are different (left is expected, right is actual):
        	            	map[string]interface {}{					map[string]interface {}{
        	            	  "v1": *litter_test.CustomMultiLineDumper{ // p1	      |	  "v1": *litter_test.CustomMultiLineDumper{ // p0
        	            	    multi							    multi
        	            	    line							    line
        	            	  },								  },
        	            	  "v2": p1,						      |	  "v2": p0,
        	            	  "v2x": *litter_test.CustomMultiLineDumper{			  "v2x": *litter_test.CustomMultiLineDumper{
        	            	    multi							    multi
        	            	    line							    line
        	            	  },								  },
        	            	  "v3": litter_test.CustomSingleLineDumper<custom>,		  "v3": litter_test.CustomSingleLineDumper<custom>,
        	            	  "v4": *litter_test.CustomSingleLineDumper<custom>, // p0    |	  "v4": *litter_test.CustomSingleLineDumper<custom>, // p1
        	            	  "v5": p0,						      |	  "v5": p1,
        	            	  "v6": *litter_test.CustomSingleLineDumper<custom>,		  "v6": *litter_test.CustomSingleLineDumper<custom>,
        	            	}								}
    FAIL
    exit status 1
    FAIL	github.com/sanity-io/litter	0.021s
    
    opened by erikgrinaker 2
  • Output does not have correct float64 type for numbers.

    Output does not have correct float64 type for numbers.

    I am using litter to output structures that include map[string]interface{} objects that were produced by the standard JSON encode/decode package. The standard JSON decoder returns all numbers in a property map as float64 values. When I run litter on the resulting struct it outputs structures where the numbers have no specified type, just literal values. As a result, numbers that had integral values get converter to int by the compiler. Is there a way to get litter to retain the float64 type specification?

    opened by jofowler 1
  • AddingPowerSupport_For_golang-github-sanity-io-litter

    AddingPowerSupport_For_golang-github-sanity-io-litter

    Adding Power Support.

    Adding power support ppc64le This is part of the Ubuntu distribution for ppc64le. This helps us simplify testing later when distributions are re-building and re-releasing. For more info tag @gerrith3.

    The Build is successful for both arch: amd64/ppc64le. Please find the Travis Link: https://travis-ci.com/github/santosh653/litter

    opened by santosh653 1
  • Not support String() implement function?

    Not support String() implement function?

    Seems like litter not accept costumed String() implement func

    Example comparing go-spew

    package main
    
    import (
    	"github.com/davecgh/go-spew/spew"
    	"github.com/sanity-io/litter"
    )
    
    func main() {
    	TT()
    }
    
    type A struct {
    	Tag string
    	Num int
    	InA InA
    }
    
    type InA struct {
    	Id   int
    	Type string
    }
    
    func (t InA) String() string {
    	return t.Type
    }
    
    func TT() {
    	a := A{
    		Tag: "tag-A",
    		Num: 1,
    		InA: InA{
    			Id:   100,
    			Type: "test",
    		},
    	}
    	spew.Dump(a)
    	litter.Dump(a)
    }
    

    Output compare

    (main.A) {
     Tag: (string) (len=5) "tag-A",
     Num: (int) 1,
     InA: (main.InA) test
    }
    main.A{
      Tag: "tag-A",
      Num: 1,
      InA: main.InA{
        Id: 100,
        Type: "test",
      },
    }
    
    opened by rsgok 2
  • Dump with Error()/String() if possible

    Dump with Error()/String() if possible

    Dump with Error()/String() if possible, like fmt, spew.

    A DisableMethods config added, controlling the Error(), String()(should include LittleDump?) method invoking or not.

    May be a solution of https://github.com/sanity-io/litter/issues/12.

    opened by zeeshen 0
  • More compact array representation

    More compact array representation

    This gets old fast:

           Source: &json.RawMessage{
              123,
              34,
              97,
              103,
              101,
              110,
              116,
              95,
              117,
              105,
              100,
              34,
    ...
    

    We should emit arrays in a compact way for primitive types such as bytes.

    opened by atombender 3
  • Panic when dumping a gorilla named route

    Panic when dumping a gorilla named route

    Seems similar to #18 but still happens with current version:

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    
    	"github.com/gorilla/mux"
    	"github.com/sanity-io/litter"
    )
    
    func main() {
    	r := mux.NewRouter()
    
    	r.HandleFunc(`/foo`, AnyHandler).Name("named")
    	r.HandleFunc("/", AnyHandler).Name("front")
    
    	srv := &http.Server{
    		Handler: r,
    		Addr:    "127.0.0.1:8000",
    	}
    
    	log.Fatal(srv.ListenAndServe())
    }
    
    func AnyHandler(w http.ResponseWriter, r *http.Request) {
    	route := mux.CurrentRoute(r)
    	litter.Config.HidePrivateFields = false
    	fmt.Fprintln(w, route.GetName())
    	defer func() {
    		p := recover()
    		if p != nil {
    			fmt.Printf("Recovered: %#v\n", p)
    		}
    	}()
    	litter.Sdump(route)
    }
    
    • Navigate to either / or /foo
    • Panics with: "reflect.Value.Interface: cannot return value obtained from unexported field or method"
    • Does not panic if only one of the routes is handled
    • Does not panic if either of the routes is not named
    opened by fgm 0
  • Dumping context.Context() doesn't show useful info

    Dumping context.Context() doesn't show useful info

    Code: litter.Dump(r.Context()) where r is a http.Request.

    Output

    &context.valueCtx{
      Context: &context.valueCtx{
        Context: &context.valueCtx{
          Context: &context.valueCtx{
            Context: &context.valueCtx{
              Context: &context.valueCtx{ // p2
                Context: &context.valueCtx{
                  Context: &context.cancelCtx{
                    Context: &context.cancelCtx{
                      Context: &context.valueCtx{
                        Context: &context.valueCtx{
                          Context: &0,
                        },
                      },
                    },
                  },
                },
              },
            },
          },
        },
      },
    }
    
    

    While github.com/davecgh/go-spew does show Values and other properties set in Context.

    opened by rskumar 6
Releases(v1.2.0)
Owner
Sanity
Sanity.io a platform for structured content that comes with an open source editor that you can customize with React.js.
Sanity
Implements a deep pretty printer for Go data structures to aid in debugging

spew Spew implements a deep pretty printer for Go data structures to aid in debugging. A comprehensive suite of tests with 100% test coverage is provi

null 89 Jul 27, 2022
Colored pretty printer for Go language

pp Colored pretty printer for Go language Usage Just call pp.Print(). import "github.com/k0kubun/pp" m := map[string]string{"foo": "bar", "hello": "w

Takashi Kokubun 1.3k Aug 8, 2022
Quick and dirty debugging output for tired Go programmers

q q is a better way to do print statement debugging. Type q.Q instead of fmt.Printf and your variables will be printed like this: Why is this better t

Ryan Boehning 1.4k Jul 26, 2022
A simple to use log system, minimalist but with features for debugging and differentiation of messages

A simple to use log system, minimalist but with features for debugging and differentiation of messages

structy 4 Jan 27, 2022
🪵 A dead simple, pretty, and feature-rich logger for golang

?? lumber ?? A dead simple, pretty, and feature-rich logger for golang ?? Install ?? Logging Functions lumber.Success() lumber.Info() lumber.Debug() l

Matt Gleich 50 Jul 20, 2022
Package httpretty prints the HTTP requests you make with Go pretty on your terminal.

httpretty Package httpretty prints the HTTP requests of your Go programs pretty on your terminal screen. It is mostly inspired in curl's --verbose mod

Henrique Vicente 268 Jul 31, 2022
Log-generator - A simple CLI tool that generates near real logs for testing

Log-generator - A simple CLI tool that generates near real logs for testing

RexWu 2 Jan 22, 2022
Tracetest - Trace-based testing. End-to-end tests powered by your OpenTelemetry Traces.

End-to-end tests powered by OpenTelemetry. For QA, Dev, & Ops. Live Demo | Documentation | Twitter | Discord | Blog Click on the image or this link to

kubeshop 157 Aug 7, 2022
A flexible process data collection, metrics, monitoring, instrumentation, and tracing client library for Go

Package monkit is a flexible code instrumenting and data collection library. See documentation at https://godoc.org/gopkg.in/spacemonkeygo/monkit.v3 S

Space Monkey Go 465 Aug 1, 2022
Parametrized JSON logging library in Golang which lets you obfuscate sensitive data and marshal any kind of content.

Noodlog Summary Noodlog is a Golang JSON parametrized and highly configurable logging library. It allows you to: print go structs as JSON messages; pr

Gyoza Tech 37 Apr 19, 2022
The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.

The open-source platform for monitoring and observability. Grafana allows you to query, visualize, alert on and understand your metrics no matter wher

Grafana Labs 50.3k Aug 9, 2022
Open source framework for processing, monitoring, and alerting on time series data

Kapacitor Open source framework for processing, monitoring, and alerting on time series data Installation Kapacitor has two binaries: kapacitor – a CL

InfluxData 2.1k Aug 3, 2022
pprof is a tool for visualization and analysis of profiling data

Introduction pprof is a tool for visualization and analysis of profiling data. pprof reads a collection of profiling samples in profile.proto format a

Google 5.6k Jul 30, 2022
Golang beautify data display for Humans

Golang beautify data display for Humans English 简体中文 Usage Examples package main import ( ffmt "gopkg.in/ffmt.v1" ) func main() { example() } typ

ffmt 275 Jul 26, 2022
Very simple charts with some debug data for Go programs

debugcharts Go memory debug charts. This package uses Plotly chart library. It is open source and free for use. Installation go get -v -u github.com/m

Marko Kevac 477 Jul 25, 2022
Visualise Go program GC trace data in real time

This project is no longer maintained I'm sorry but I do not have the bandwidth to maintain this tool. Please do not send issues or PRs. Thank you. gcv

Dave Cheney 1.1k Jun 28, 2022
Interfaces for LZ77-based data compression

Pack Interfaces for LZ77-based data compression. Introduction Many compression libraries have two main parts: Something that looks for repeated sequen

Andy Balholm 3 Oct 19, 2021
mtail - extract internal monitoring data from application logs for collection into a timeseries database

mtail - extract internal monitoring data from application logs for collection into a timeseries database mtail is a tool for extracting metrics from a

Google 3.3k Aug 9, 2022
Go Huobi Market Price Data Monitor

火币(Huobi)价格监控 由于部分交易对火币官方未提供价格监控,因此写了个小程序,长期屯币党可以用它来提醒各种现货价格。 该工具只需要提前安装Go环境和Redis即可。 消息推送使用的「钉钉」,需要提前配置好钉钉机器人(企业群类型、带webhook的机器人)。 使用方法 下载本项目 拷贝根目录下

ROC 5 Jul 21, 2022