xtrace

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Mar 7, 2020 License: Apache-2.0 Imports: 9 Imported by: 1

README

xtrace

Build Status

xerrors is pretty awesome. It allows you to wrap your errors and provide more context. Sadly, one of the features of it that isn't really available until Go 1.13 is the ability to print out a stack trace. No longer!

Installation

go get -u github.com/ollien/xtrace

Basic Usage

The following example will print a trace of all of the wrapped errors to stderr.

package main

import (
	"errors"

	"github.com/ollien/xtrace"
	"golang.org/x/xerrors"
)

func main() {
	baseErr := errors.New("aw shucks, something broke")
	err2 := xerrors.Errorf("things went wrong!: %w", baseErr)

	traceErr := xtrace.Trace(err2)
	if traceErr != nil {
		panic("can not trace")
	}
	// aw shucks, something broke
	// things went wrong!
	// github.com/ollien/xtrace.ExampleTracer_Format
	//    /home/nick/Documents/code/xtrace/example.go:12
}

If more customization is desired, one can use a Tracer. One of Tracer's key features is its compatibility with fmt.

// ...
tracer, err := xtrace.NewTracer(err2)
if err != nil {
	panic("can not make tracer")
}

fmt.Printf("%v", tracer)
// aw shucks, something broke
// things went wrong!

That's nice, and we can see a trace of all of our errors, but xerrors provides much more for information for us. To get this information, all we have to do is change our format string to %+v.

// ... (repeated from above)

fmt.Printf("%+v", tracer)
// aw shucks, something broke
// things went wrong!
// github.com/ollien/xtrace.ExampleTracer_Format
//    /home/nick/Documents/code/xtrace/example.go:18

If you don't want to use fmt and just want to get this trace as a string, you can simply use ReadNext to get the next error in the trace. (Tracer also implements io.Reader if you prefer to use that.)

// ... (repeated from above)

output, err := tracer.ReadNext()
if err != nil {
	panic("can not read from tracer")
}

fmt.Println(output)
// aw shucks, something broke

See the docs for more usages.

Customization

Output customization is one of the explicit goals of xtrace. For instance, if you wish to flip the output of your trace so that the newest errors are on top (i.e. with the root cause at the bottom), all you have to do is the following.

tracer, err := NewTracer(err, Ordering(NewestFirstOrdering))

You can also set up custom formatters for your traces. There are several included (such as NestedMessageFormatter and NilFormatter). If you want, you can also write your own formatter by simply implementing the TraceFormatter interface. For instance, if you wanted to make sure that everyone hears your errors, you can make all of them capitalized.

type capsFormatter struct{}

func (formatter capsFormatter) FormatTrace(previous []string, message string) string {
	return strings.ToUpper(message)
}

You can then set a Tracer's formatter by doing

tracer, err := NewTracer(err, Formatter(capsFormatter{}))

If you wish to combine your loud errors with newest-first ordering you can pass them both as arguments to NewTracer.

tracer, err := NewTracer(err, Ordering(NewestFirstOrdering), Formatter(capsFormatter{}))

See the docs for more details.

Documentation

Overview

Package xtrace provides the ability to generate a trace of wrapped errors from xerrors. This is facilitated through the Tracer type, the output of which can be customized with a TraceFormatter. For more information on how to wrap errors, see https://godoc.org/golang.org/x/xerrors.

Basic Usage

The following example will print a trace of all of the wrapped errors to stderr.

package main

import (
	"errors"

	"github.com/ollien/xtrace"
	"golang.org/x/xerrors"
)

func main() {
	baseErr := errors.New("aw shucks, something broke")
	err2 := xerrors.Errorf("things went wrong!: %w", baseErr)

	traceErr := xtrace.Trace(err2)
	if traceErr != nil {
		panic("can not trace")
	}
	// aw shucks, something broke
	// things went wrong!
	// github.com/ollien/xtrace.ExampleTracer_Format
	//    /home/nick/Documents/code/xtrace/example.go:12
}

If more customization is desired, one can use a Tracer. One of Tracer's key features is its compatibility with fmt.

// ...
tracer, err := xtrace.NewTracer(err2)
if err != nil {
	panic("can not make tracer")
}

fmt.Printf("%v", tracer)
// aw shucks, something broke
// things went wrong!

You can also add %+v for more detailed information.

// ...
fmt.Printf("%+v", tracer)
// aw shucks, something broke
// things went wrong!
// github.com/ollien/xtrace.ExampleTracer_Format
//    /home/nick/Documents/code/xtrace/example.go:18

Using fmt is not required, though. You may instead read the errors one at a time from the trace with the ReadNext and Read functions.

// ...
output, err := tracer.ReadNext()
if err != nil {
	panic("can not read from tracer")
}

fmt.Println(output)
// aw shucks, something broke

Customization

All output of a Tracer can be customized. By default, the Tracer will ensure that all messages end in a newline. If you want more customization than that, then you can create your own TraceFormatter. For instance, to make all of your errors in all caps, you can use the following TraceFormatter.

type capsFormatter struct{}

func (formatter capsFormatter) FormatTrace(previous []string, message string) string {
	return strings.ToUpper(message)
}

You can then set a Tracer's TraceFormatter like so

tracer, err := NewTracer(err, Formatter(capsFormatter{}))

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DetailedOutput

func DetailedOutput(enabled bool) func(*Tracer) error

DetailedOutput will enable detailed output when this is passed to NewTracer. While the specifics of this detailed output is defined by the xerrors.Formatter for the passed error, it will generally provide more detailed information about the error, such as the file and line number of the error. Defaults to true.

func Formatter

func Formatter(formatter TraceFormatter) func(*Tracer) error

Formatter will set the given TracerFormatter as the formatter of the Tracer generated by NewTracer when this is passed to it. Defaults to NewLineFormatter.

func Naive

func Naive(naive bool) func(*NewLineFormatter) error

Naive will set the naive flag when passed to NewNewLineFormatter. This flag, if set, will instruct the formatter to perform the naive version of this algorithm, which simply adds/removes a newline from the end of each message. xerrors has a habit of sending indentation in the previous line (i.e. "<error>\n "), so the naive algorithm produces weird output. Nevertheless, this may be desirable depending on the implementation of xerrors.Formatter, so it is left as an option. Defaults to false.

func NestingIndentation

func NestingIndentation(indentation string) func(*NestedMessageFormatter) error

NestingIndentation sets the string used as indentation for the NestedMessageFormatter that is produced when this is passed to NewNestedMessageFormatter. Defaults to "\t".

func Ordering

func Ordering(method TraceOrderingMethod) func(*Tracer) error

Ordering sets the order in which the traces will be outputted from the Read methods, when passed to NewTracer. Defaults to OldestFirstOrdering.

Example
baseErr := errors.New("aw shucks, something broke")
err2 := xerrors.Errorf("things went wrong!: %w", baseErr)
tracer, err := NewTracer(err2, Ordering(NewestFirstOrdering))
if err != nil {
	panic("can not make tracer")
}

fmt.Printf("%v", tracer)
Output:

things went wrong!
aw shucks, something broke

func Trace added in v0.2.0

func Trace(baseErr error) error

Trace prints a trace of errors wrapped by xerrors to stderr, with a terminating newline. If more customization is desired, please use Tracer.

Types

type NestedMessageFormatter

type NestedMessageFormatter struct {
	// contains filtered or unexported fields
}

NestedMessageFormatter will leave the leading line with no indentation, but indents all lines following, stripping whitespace from the both the left and right of each line and replacing it with a newline, unless it is the last message. In this case, no newline is inserted, but whitespace is still stripped.

func NewNestedMessageFormatter

func NewNestedMessageFormatter(options ...func(*NestedMessageFormatter) error) (*NestedMessageFormatter, error)

NewNestedMessageFormatter makes a new NestedMessageFormatter.

func (NestedMessageFormatter) FormatTrace

func (formatter NestedMessageFormatter) FormatTrace(previousMessages []string, message string) string

FormatTrace formats the message as dictated by the contract for NestedMessageFormatter.

type NewLineFormatter

type NewLineFormatter struct {
	// contains filtered or unexported fields
}

NewLineFormatter ensures that all messages except the last end in a newline after all error content.

func NewNewLineFormatter

func NewNewLineFormatter(options ...func(*NewLineFormatter) error) (*NewLineFormatter, error)

NewNewLineFormatter will make a new NewLineFormatter.

func (*NewLineFormatter) FormatTrace

func (formatter *NewLineFormatter) FormatTrace(previousMessages []string, message string) (formatted string)

FormatTrace formats the message as dictated by the contract for NewLineFormatter.

type NilFormatter

type NilFormatter struct{}

NilFormatter applies no formatting and returns the given message as xerrors sends them. Note that the messages that xerrors sends aren't always the most intuitive (e.g. there are no newlines after error messages), and the usage of this formatter is not strictly recommended. It is mainly provided for those that want a simple shim.

func NewNilFormatter

func NewNilFormatter() *NilFormatter

NewNilFormatter makes a new NilFormatter.

func (NilFormatter) FormatTrace

func (formatter NilFormatter) FormatTrace(previousMessages []string, message string) string

FormatTrace applies no formatting and returns the given message as is.

type TraceFormatter

type TraceFormatter interface {
	// FormatTrace takes all previous messages and a message and will return a formatted message. Any previous message
	// may be updated based on the current message, and the outputted trace will respect these changes.
	FormatTrace(previousMessages []string, message string) string
}

TraceFormatter allows for the formatting of any message in the given trace.

type TraceOrderingMethod

type TraceOrderingMethod int

TraceOrderingMethod represents a way to order the errors within the produced trace.

const (
	// OldestFirstOrdering orders the trace such that the root cause of the error comes first, with all subsequent
	// errors after it (default).
	OldestFirstOrdering TraceOrderingMethod = iota
	// NewestFirstOrdering orders the trace such that the root cause of the error comes last, with all subsequent
	// errors before it.
	NewestFirstOrdering
)

type Tracer

type Tracer struct {
	// contains filtered or unexported fields
}

Tracer gets the trace of errors wrapped by xerrors.

func NewTracer

func NewTracer(baseErr error, options ...func(*Tracer) error) (*Tracer, error)

NewTracer returns a new Tracer for the given error.

Example
baseErr := errors.New("aw shucks, something broke")
// capsFormatter is a custom formatter that simply applies strings.ToUpper to all messages
tracer, err := NewTracer(baseErr, Formatter(capsFormatter{}))
if err != nil {
	panic("can not make tracer")
}
output, err := tracer.ReadNext()
if err != nil {
	panic("can not read from tracer")
}

fmt.Println(output)
Output:

AW SHUCKS, SOMETHING BROKE

func (*Tracer) Format

func (tracer *Tracer) Format(s fmt.State, verb rune)

Format allows for tracer to implement fmt.Formatter. This will simply make a clone of the tracer and print out the full trace. DetailedOutput will be given when %+v is provided, and normal output when %v is provided.

Example
baseErr := errors.New("aw shucks, something broke")
err2 := xerrors.Errorf("things went wrong!: %w", baseErr)
tracer, err := NewTracer(err2)
if err != nil {
	panic("can not make tracer")
}

fmt.Printf("%v", tracer)
Output:

aw shucks, something broke
things went wrong!

func (*Tracer) Read

func (tracer *Tracer) Read(dest []byte) (n int, err error)

Read implements the io.Reader interface. Will read up to len(dest) bytes of the current error. Note that this means dest will only be filled up the contents of the error, regardless of if there are other errors to be read in the error stack. Returns io.EOF when there are no more errors to read, but notably will not be returned when the last error is returned.

func (*Tracer) ReadNext

func (tracer *Tracer) ReadNext() (string, error)

ReadNext will read one unwrapped error and its associated trace If Read() has been called, but the buffer has not been exhausted, its contents will be discarded. Returns io.EOF when there are no more errors to read, but notably will not be returned when the last error is returned.

func (*Tracer) Trace added in v0.2.0

func (tracer *Tracer) Trace(writer io.Writer) error

Trace makes a clone of the Tracer and writes the full trace to the provided io.Writer.

Jump to

Keyboard shortcuts

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