hierr

package module
v0.0.0-...-7d09c01 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 24, 2017 License: MIT Imports: 4 Imported by: 1

README

Hierarchical errors made right Go Report Card

Hate seeing error: exit status 128 in the output of programs without actual explanation what is going wrong?

Or, maybe, you're more advanced in programming, and use errors concatenation?

can't pull remote 'origin': can't run git fetch 'origin' 'refs/tokens/*:refs/tokens/*': exit status 128

Better, but still unreadable.

hierr

Transform error reports into hierarchy:

can't pull remote 'origin'
└─ can't run git fetch 'origin' 'refs/tokens/*:refs/tokens/*'
   └─ exit status 128

To use hierarchy error reporting, just convert fmt.Errorf calls:

return fmt.Errorf("can't pull remote '%s': %s", remote, err)

return hierr.Errorf(err, "can't pull remote '%s'", remote)

Docs: https://godoc.org/github.com/seletskiy/hierr

Documentation

Overview

Package hierr provides a simple way to return and display hierarchical errors.

Transforms:

can't pull remote 'origin': can't run git fetch 'origin' 'refs/tokens/*:refs/tokens/*': exit status 128

Into:

can't pull remote 'origin'
└─ can't run git fetch 'origin' 'refs/tokens/*:refs/tokens/*'
   └─ exit status 128

Index

Examples

Constants

View Source
const (
	// BranchDelimiterASCII represents a simple ASCII delimiter for hierarchy
	// branches.
	//
	// Use: hierr.BranchDelimiter = hierr.BranchDelimiterASCII
	BranchDelimiterASCII = `\_ `

	// BranchDelimiterBox represents UTF8 delimiter for hierarchy branches.
	//
	// Use: hierr.BranchDelimiter = hierr.BranchDelimiterBox
	BranchDelimiterBox = `└─ `

	// BranchChainerASCII represents a simple ASCII chainer for hierarchy
	// branches.
	//
	// Use: hierr.BranchChainer = hierr.BranchChainerASCII
	BranchChainerASCII = `| `

	// BranchChainerBox represents UTF8 chainer for hierarchy branches.
	//
	// Use: hierr.BranchChainer = hierr.BranchChainerBox
	BranchChainerBox = `│ `

	// BranchSplitterASCII represents a simple ASCII splitter for hierarchy
	// branches.
	//
	// Use: hierr.BranchSplitter = hierr.BranchSplitterASCII
	BranchSplitterASCII = `+ `

	// BranchSplitterBox represents UTF8 splitter for hierarchy branches.
	//
	// Use: hierr.BranchSplitter = hierr.BranchSplitterBox
	BranchSplitterBox = `├─ `
)

Variables

View Source
var (
	// BranchDelimiter set delimiter each nested error text will be started
	// from.
	BranchDelimiter = BranchDelimiterBox

	// BranchChainer set chainer each nested error tree text will be started
	// from.
	BranchChainer = BranchChainerBox

	// BranchSplitter set splitter each nested errors splitted by.
	BranchSplitter = BranchSplitterBox

	// BranchIndent set number of spaces each nested error will be indented by.
	BranchIndent = 3
)

Functions

func Context

func Context(node NestedError, description ...NestedError) error

Context adds context to specified top-level node.

Context can be passed to rest of the call to add multiple labels to given error:

hierr.Context(
	err,
	hierr.Context(`mailer`, `localhost:25`),
	hierr.Context(`config`, `/path/to/config.toml`),
)
Example
testcases := []error{
	Context(
		Errorf(
			errors.New(`failed to parse int`),
			`no config field: %s`,
			`some_config_field`,
		),
		Context(fmt.Sprintf(`config: %s`, `/path/to/config.yaml`)),
	),

	Context(
		errors.New(`fatal error`),
		Context(`database`, `localhost:1234`),
	),

	Context(
		Errorf(
			errors.New(`fatal error`),
			`some error occured`,
		),
		Context(`database`, `localhost:1234`),
		Context(`node`, `node-a.localdomain`),
	),
}

for _, test := range testcases {
	fmt.Println()
	fmt.Println("{{{")
	fmt.Println(test.Error())
	fmt.Println("}}}")
}
Output:


{{{
no config field: some_config_field
├─ failed to parse int
└─ config: /path/to/config.yaml
}}}

{{{
fatal error
└─ database
   └─ localhost:1234
}}}

{{{
some error occured
├─ fatal error
│
├─ database
│  └─ localhost:1234
│
└─ node
   └─ node-a.localdomain
}}}

func Errorf

func Errorf(
	nestedError NestedError,
	message string,
	args ...interface{},
) error

Errorf creates new hierarchy error.

With nestedError == nil call will be equal to `fmt.Errorf()`.

func Fatalf

func Fatalf(
	nestedError NestedError,
	message string,
	args ...interface{},
)

Fatalf creates new hierarchy error, prints to stderr and exit 1

Have same semantics as `hierr.Errorf()`.

func Push

func Push(topError NestedError, childError ...NestedError) error

Push creates new hierarchy error with multiple branches separated by separator, delimited by delimiter and prolongated by prolongator.

Example
testcases := []error{
	Push(
		"the godfather",
		Push(
			"son A",
			"A's son 1",
			Push(
				"A's son 2",
				Push("2' son X",
					Push("X's son @"),
					Push("X's son #"),
				),
			),
		),
		Push("son B",
			errors.New("B's son 1"),
			errors.New("B's son 2"),
			Push("orphan"),
		),
		Errorf(
			fmt.Sprintf("%s", "B's son 1"),
			"son B",
		),
		errors.New("police"),
	),
}

for _, test := range testcases {
	fmt.Println()
	fmt.Println("{{{")
	fmt.Println(test.Error())
	fmt.Println("}}}")
}
Output:


{{{
the godfather
├─ son A
│  ├─ A's son 1
│  │
│  └─ A's son 2
│     └─ 2' son X
│        ├─ X's son @
│        └─ X's son #
│
├─ son B
│  ├─ B's son 1
│  ├─ B's son 2
│  └─ orphan
│
├─ son B
│  └─ B's son 1
│
└─ police
}}}

func String

func String(object interface{}) string

Types

type Error

type Error struct {
	// Message is formatter error message, which will be reported when Error()
	// will be invoked.
	Message string

	// Nested error, which can be hierr.Error as well.
	Nested interface{}
}

Error represents hierarchy error, linked with nested error.

Example
testcases := []error{
	Errorf(nil, ""),
	Errorf(nil, "simple error"),
	Errorf(nil, "integer: %d", 1),
	Errorf(errors.New("nested"), "top level"),
	Errorf(errors.New("nested"), "top level: %s", "formatting"),
	Errorf(Errorf(errors.New("low level"), "nested"), "top level"),
	Errorf(Errorf(fmt.Sprintf("%s", "string"), "nested"), "top level"),
	Errorf([]byte("byte"), "top level"),
}

for _, test := range testcases {
	fmt.Println()
	fmt.Println("{{{")
	fmt.Println(test.Error())
	fmt.Println("}}}")
}

fmt.Println()

exiter = func(code int) {
	fmt.Println("exit code:", code)
}

tempfile, err := ioutil.TempFile(os.TempDir(), "stderr")
if err != nil {
	panic(err)
}

os.Stderr = tempfile

Fatalf(fmt.Sprintf("%s", "wow"), "critical error")

_, err = tempfile.Seek(0, 0)
if err != nil {
	panic(err)
}

text, err := ioutil.ReadAll(tempfile)
if err != nil {
	panic(err)
}

fmt.Println("stderr:\n" + string(text))
Output:


{{{

}}}

{{{
simple error
}}}

{{{
integer: 1
}}}

{{{
top level
└─ nested
}}}

{{{
top level: formatting
└─ nested
}}}

{{{
top level
└─ nested
   └─ low level
}}}

{{{
top level
└─ nested
   └─ string
}}}

{{{
top level
└─ byte
}}}

exit code: 1
stderr:
critical error
└─ wow

func (Error) Error

func (err Error) Error() string

Error returns string representation of hierarchical error. If no nested error was specified, then only current error message will be returned.

func (Error) GetMessage

func (err Error) GetMessage() string

GetMessage returns top-level error message.

func (Error) GetNested

func (err Error) GetNested() []NestedError

GetNested returns nested errors, embedded into error.

func (Error) HierarchicalError

func (err Error) HierarchicalError() string

HierarchicalError returns pretty hierarchical rendering.

type HierarchicalError

type HierarchicalError interface {
	// HierarchicalError returns hierarhical string representation.
	HierarchicalError() string

	// GetNested returns slice of nested errors.
	GetNested() []NestedError

	// GetMessage returns top-level error message.
	GetMessage() string
}

HierarchicalError represents interface, which methods will be used instead of calling String() and Error() methods.

Example
testcases := []error{
	Errorf(
		Errorf(
			smartError{"smart", errors.New("hierarchical")},
			"second",
		),
		"top level",
	),
	Errorf(
		Errorf(
			fmt.Sprintf(
				"%s",
				smartError{"smart plain", errors.New("error")},
			),
			"second",
		),
		"top level",
	),
	Push(
		smartError{"smart", errors.New("hierarchical")},
		smartError{"smart", errors.New("hierarchical")},
	),
	Push(
		smartError{"smart", errors.New("hierarchical")},
		Push(
			smartError{"smart", errors.New("hierarchical")},
			smartError{"smart", errors.New("hierarchical")},
			smartError{"smart", Errorf(fmt.Sprintf("%s", "nest"), "top")},
		),
	),
}

for _, test := range testcases {
	fmt.Println()
	fmt.Println("{{{")
	fmt.Println(test.Error())
	fmt.Println("}}}")
}
Output:


{{{
top level
└─ second
   └─ smart
      └─ hierarchical
}}}

{{{
top level
└─ second
   └─ {smart plain error}
}}}

{{{
smart
└─ hierarchical
└─ smart
   └─ hierarchical
}}}

{{{
smart
└─ hierarchical
└─ smart
   └─ hierarchical
   ├─ smart
   │  └─ hierarchical
   │
   └─ smart
      └─ top
         └─ nest
}}}

type NestedError

type NestedError interface{}

NestedError is either `error` or string.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL