Skip to content

Commit

Permalink
Extended stacktrace output (pkg#49)
Browse files Browse the repository at this point in the history
* Extended stacktrace output

* Make format tests less sensitive to sourcecode prefix

* use testFormatRegexp for all Sprintf tests

* replace output test with sample output

* work around the different fn.Name format in go 1.4

* fix windows tests when there is a drive letter in the source path
  • Loading branch information
davecheney authored Jun 11, 2016
1 parent d4b5735 commit 7896481
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 66 deletions.
4 changes: 3 additions & 1 deletion errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ func (e _error) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v: ", e.Stacktrace()[0])
io.WriteString(s, e.msg)
fmt.Fprintf(s, "%+v", e.Stacktrace())
return
}
fallthrough
case 's':
Expand Down
84 changes: 75 additions & 9 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,22 @@ func ExampleNew_printf() {
err := errors.New("whoops")
fmt.Printf("%+v", err)

// Output: github.com/pkg/errors/example_test.go:17: whoops
// Example output:
// whoops
// github.com/pkg/errors_test.ExampleNew_printf
// /home/dfc/src/github.com/pkg/errors/example_test.go:17
// testing.runExample
// /home/dfc/go/src/testing/example.go:114
// testing.RunExamples
// /home/dfc/go/src/testing/example.go:38
// testing.(*M).Run
// /home/dfc/go/src/testing/testing.go:744
// main.main
// /github.com/pkg/errors/_test/_testmain.go:106
// runtime.main
// /home/dfc/go/src/runtime/proc.go:183
// runtime.goexit
// /home/dfc/go/src/runtime/asm_amd64.s:2059
}

func ExampleWrap() {
Expand All @@ -44,14 +59,34 @@ func ExampleCause() {
// error
}

func ExampleCause_printf() {
func ExampleWrap_extended() {
err := fn()
fmt.Printf("%+v\n", err)

// Output: github.com/pkg/errors/example_test.go:32: error
// github.com/pkg/errors/example_test.go:33: inner
// github.com/pkg/errors/example_test.go:34: middle
// github.com/pkg/errors/example_test.go:35: outer
// Example output:
// error
// github.com/pkg/errors_test.fn
// /home/dfc/src/github.com/pkg/errors/example_test.go:47
// github.com/pkg/errors_test.ExampleCause_printf
// /home/dfc/src/github.com/pkg/errors/example_test.go:63
// testing.runExample
// /home/dfc/go/src/testing/example.go:114
// testing.RunExamples
// /home/dfc/go/src/testing/example.go:38
// testing.(*M).Run
// /home/dfc/go/src/testing/testing.go:744
// main.main
// /github.com/pkg/errors/_test/_testmain.go:104
// runtime.main
// /home/dfc/go/src/runtime/proc.go:183
// runtime.goexit
// /home/dfc/go/src/runtime/asm_amd64.s:2059
// github.com/pkg/errors_test.fn
// /home/dfc/src/github.com/pkg/errors/example_test.go:48: inner
// github.com/pkg/errors_test.fn
// /home/dfc/src/github.com/pkg/errors/example_test.go:49: middle
// github.com/pkg/errors_test.fn
// /home/dfc/src/github.com/pkg/errors/example_test.go:50: outer
}

func ExampleWrapf() {
Expand All @@ -62,11 +97,26 @@ func ExampleWrapf() {
// Output: oh noes #2: whoops
}

func ExampleErrorf() {
func ExampleErrorf_extended() {
err := errors.Errorf("whoops: %s", "foo")
fmt.Printf("%+v", err)

// Output: github.com/pkg/errors/example_test.go:66: whoops: foo
// Example output:
// whoops: foo
// github.com/pkg/errors_test.ExampleErrorf
// /home/dfc/src/github.com/pkg/errors/example_test.go:101
// testing.runExample
// /home/dfc/go/src/testing/example.go:114
// testing.RunExamples
// /home/dfc/go/src/testing/example.go:38
// testing.(*M).Run
// /home/dfc/go/src/testing/testing.go:744
// main.main
// /github.com/pkg/errors/_test/_testmain.go:102
// runtime.main
// /home/dfc/go/src/runtime/proc.go:183
// runtime.goexit
// /home/dfc/go/src/runtime/asm_amd64.s:2059
}

func Example_stacktrace() {
Expand All @@ -82,5 +132,21 @@ func Example_stacktrace() {
st := err.Stacktrace()
fmt.Printf("%+v", st[0:2]) // top two frames

// Output: [github.com/pkg/errors/example_test.go:32 github.com/pkg/errors/example_test.go:77]
// Example output:
// github.com/pkg/errors_test.fn
// /home/dfc/src/github.com/pkg/errors/example_test.go:47
// github.com/pkg/errors_test.Example_stacktrace
// /home/dfc/src/github.com/pkg/errors/example_test.go:127
}

func ExampleCause_printf() {
err := errors.Wrap(func() error {
return func() error {
return errors.Errorf("hello %s", fmt.Sprintf("world"))
}()
}(), "failed")

fmt.Printf("%v", err)

// Output: failed: hello world
}
85 changes: 70 additions & 15 deletions format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ package errors
import (
"fmt"
"io"
"regexp"
"strings"
"testing"
)

func TestFormat(t *testing.T) {
func TestFormatNew(t *testing.T) {
tests := []struct {
error
format string
want string
}{{

New("error"),
"%s",
"error",
Expand All @@ -23,8 +24,22 @@ func TestFormat(t *testing.T) {
}, {
New("error"),
"%+v",
"github.com/pkg/errors/format_test.go:24: error",
}, {
"error\n" +
"github.com/pkg/errors.TestFormatNew\n" +
"\t.+/github.com/pkg/errors/format_test.go:25",
}}

for _, tt := range tests {
testFormatRegexp(t, tt.error, tt.format, tt.want)
}
}

func TestFormatErrorf(t *testing.T) {
tests := []struct {
error
format string
want string
}{{
Errorf("%s", "error"),
"%s",
"error",
Expand All @@ -35,8 +50,22 @@ func TestFormat(t *testing.T) {
}, {
Errorf("%s", "error"),
"%+v",
"github.com/pkg/errors/format_test.go:36: error",
}, {
"error\n" +
"github.com/pkg/errors.TestFormatErrorf\n" +
"\t.+/github.com/pkg/errors/format_test.go:51",
}}

for _, tt := range tests {
testFormatRegexp(t, tt.error, tt.format, tt.want)
}
}

func TestFormatWrap(t *testing.T) {
tests := []struct {
error
format string
want string
}{{
Wrap(New("error"), "error2"),
"%s",
"error2: error",
Expand All @@ -47,13 +76,26 @@ func TestFormat(t *testing.T) {
}, {
Wrap(New("error"), "error2"),
"%+v",
"github.com/pkg/errors/format_test.go:48: error\n" +
"github.com/pkg/errors/format_test.go:48: error2",
"error\n" +
"github.com/pkg/errors.TestFormatWrap\n" +
"\t.+/github.com/pkg/errors/format_test.go:77",
}, {
Wrap(io.EOF, "error"),
"%s",
"error: EOF",
}, {
}}

for _, tt := range tests {
testFormatRegexp(t, tt.error, tt.format, tt.want)
}
}

func TestFormatWrapf(t *testing.T) {
tests := []struct {
error
format string
want string
}{{
Wrapf(New("error"), "error%d", 2),
"%s",
"error2: error",
Expand All @@ -65,22 +107,35 @@ func TestFormat(t *testing.T) {
Wrap(io.EOF, "error"),
"%+v",
"EOF\n" +
"github.com/pkg/errors/format_test.go:65: error",
"github.com/pkg/errors.TestFormatWrapf\n" +
"\t.+/github.com/pkg/errors/format_test.go:107: error",
}, {
Wrapf(New("error"), "error%d", 2),
"%v",
"error2: error",
}, {
Wrapf(New("error"), "error%d", 2),
"%+v",
"github.com/pkg/errors/format_test.go:74: error\n" +
"github.com/pkg/errors/format_test.go:74: error2",
"error\n" +
"github.com/pkg/errors.TestFormatWrapf\n" +
"\t.+/github.com/pkg/errors/format_test.go:117",
}}

for _, tt := range tests {
got := fmt.Sprintf(tt.format, tt.error)
if got != tt.want {
t.Errorf("fmt.Sprintf(%q, err): got: %q, want: %q", tt.format, got, tt.want)
testFormatRegexp(t, tt.error, tt.format, tt.want)
}
}

func testFormatRegexp(t *testing.T, arg interface{}, format, want string) {
got := fmt.Sprintf(format, arg)
lines := strings.SplitN(got, "\n", -1)
for i, w := range strings.SplitN(want, "\n", -1) {
match, err := regexp.MatchString(w, lines[i])
if err != nil {
t.Fatal(err)
}
if !match {
t.Errorf("fmt.Sprintf(%q, err): got: %q, want: %q", format, got, want)
}
}
}
6 changes: 4 additions & 2 deletions stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (f Frame) Format(s fmt.State, verb rune) {
io.WriteString(s, "unknown")
} else {
file, _ := fn.FileLine(pc)
io.WriteString(s, trimGOPATH(fn.Name(), file))
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
}
default:
io.WriteString(s, path.Base(f.file()))
Expand All @@ -84,7 +84,9 @@ func (st Stacktrace) Format(s fmt.State, verb rune) {
case 'v':
switch {
case s.Flag('+'):
fmt.Fprintf(s, "%+v", []Frame(st))
for _, f := range st {
fmt.Fprintf(s, "\n%+v", f)
}
case s.Flag('#'):
fmt.Fprintf(s, "%#v", []Frame(st))
default:
Expand Down
Loading

0 comments on commit 7896481

Please sign in to comment.