Skip to content
This repository has been archived by the owner on Dec 1, 2021. It is now read-only.

WithMessage appends message after associated stack traces instead of before #102

Open
noonat opened this issue Feb 6, 2017 · 9 comments · May be fixed by #114
Open

WithMessage appends message after associated stack traces instead of before #102

noonat opened this issue Feb 6, 2017 · 9 comments · May be fixed by #114

Comments

@noonat
Copy link

noonat commented Feb 6, 2017

After amending an error using errors.WithMessage, printing the error with %+v prints the extra information after the stack trace instead of with the original message. For example:

package main

import (
	"log"

	"github.com/pkg/errors"
)

func a() error {
	return errors.New("error details")
}

func b() error {
	if err := a(); err != nil {
		return errors.WithMessage(err, "additional details")
	}
	return nil
}

func main() {
	if err := b(); err != nil {
		log.Fatalf("%+v", err)
	}
}

This prints:

2017/02/06 14:34:13 error details
main.a
	.../example.go:10
main.b
	.../example.go:14
main.main
	.../example.go:21
runtime.main
	/usr/local/Cellar/go/1.7.4_2/libexec/src/runtime/proc.go:183
runtime.goexit
	/usr/local/Cellar/go/1.7.4_2/libexec/src/runtime/asm_amd64.s:2086
additional details
exit status 1

As mentioned in #75, it seems like it would be more helpful to include the additional information with original error message. When using %v with the same example, it prints messages in this way now:

2017/02/06 14:33:32 additional details: error details
exit status 1
mrhwick added a commit to mrhwick/errors that referenced this issue May 4, 2017
@mrhwick mrhwick linked a pull request May 4, 2017 that will close this issue
@benma
Copy link

benma commented Jun 14, 2017

I can confirm the issue. I also expected the full error message at the top.

@davecheney
Copy link
Member

davecheney commented Jun 14, 2017 via email

@benma
Copy link

benma commented Jun 14, 2017

Ok, thanks.

@benma
Copy link

benma commented Jun 14, 2017

For reference, if anyone finds it useful, this is the function I use to format the stack trace. It puts the full error message on top and removes the quadratic blowup of the stack trace when there are many wraps (see #75 (comment)):

func ErrFormat(err error) string {
	if err == nil {
		return ""
	}
	type stackTracer interface {
		StackTrace() errors.StackTrace
	}
	cause := errors.Cause(err)
	if stackTrace, ok := cause.(stackTracer); ok {
		buf := bytes.Buffer{}
		for _, frame := range stackTrace.StackTrace() {
			buf.WriteString(fmt.Sprintf("\n%+v", frame))
		}
		return err.Error() + buf.String()
	}
	return err.Error()
}

@davecheney
Copy link
Member

davecheney commented Jun 14, 2017 via email

@benma
Copy link

benma commented Jun 14, 2017

I am glad you did ;)

Maybe it would be worth it to include a few different formatters in the library so new users don't get any surprises and don't need to spend time doing their own? So far we've identified at least one different one where multiple users have been surprised before, and likely will again, so we could save their time dealing with this.

@davecheney
Copy link
Member

davecheney commented Jun 14, 2017 via email

@benma
Copy link

benma commented Jun 14, 2017

I understand. Thanks!

@dragonsinth
Copy link

dragonsinth commented Jun 30, 2017

Here's a formatter I just finished...

func printError(w io.Writer, err error) {
	// Messages come back from deepest to shallowest; reverse the order for printing.
	msgs, stack := deconstruct(err)
	for i := range msgs {
		if i > 0 {
			fmt.Fprint(w, "\n...caused by ")
		}
		fmt.Fprint(w, msgs[len(msgs)-i-1])
	}
	if stack != nil {
		fmt.Fprintf(w, "%+v\n", stack)
	}
}

func deconstruct(err error) ([]string, errors.StackTrace) {
	if err == nil {
		return nil, nil
	}

	myMsg := err.Error()
	var messages []string
	var stack errors.StackTrace

	type causer interface {
		Cause() error
	}
	if causer, ok := err.(causer); ok {
		messages, stack = deconstruct(causer.Cause())
		if len(messages) > 0 {
			pos := strings.Index(myMsg, ": "+messages[len(messages)-1])
			if pos >= 0 {
				myMsg = myMsg[:pos]
			}
		}
	}

	messages = append(messages, myMsg)

	if stack == nil {
		type stackTracer interface {
			StackTrace() errors.StackTrace
		}
		if tracer, ok := err.(stackTracer); ok {
			stack = tracer.StackTrace()
		}
	}

	return messages, stack
}

Example output:

You do not appear to be inside of a git repo, this tool is meant to be used in a git repo
...caused by Failed to execute 'git rev-parse --show-toplevel'
...caused by fatal: Not a git repository (or any of the parent directories): .git
main.(*gitter).Command
	/Users/scottb/src/mn/projects/fullstory/go/src/fs/transform/main/repoman/git.go:151
main.run
	/Users/scottb/src/mn/projects/fullstory/go/src/fs/transform/main/repoman/repoman.go:224
main.main
	/Users/scottb/src/mn/projects/fullstory/go/src/fs/transform/main/repoman/repoman.go:111
runtime.main
	/usr/local/Cellar/go/1.7.4/libexec/src/runtime/proc.go:183
runtime.goexit
	/usr/local/Cellar/go/1.7.4/libexec/src/runtime/asm_amd64.s:2086

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants