镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

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

Open

Description

@noonat

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

Activity

  1. added a commit that references this issue on May 4, 2017
    a77ef41
  2. benma commented on Jun 14, 2017

    @benma

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

  3. davecheney commented on Jun 14, 2017

    @davecheney
    Member
  4. benma commented on Jun 14, 2017

    @benma

    Ok, thanks.

  5. benma commented on Jun 14, 2017

    @benma

    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()
    }
  6. davecheney commented on Jun 14, 2017

    @davecheney
    Member
  7. benma commented on Jun 14, 2017

    @benma

    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.

  8. davecheney commented on Jun 14, 2017

    @davecheney
    Member
  9. benma commented on Jun 14, 2017

    @benma

    I understand. Thanks!

  10. dragonsinth commented on Jun 30, 2017

    @dragonsinth

    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 join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions