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

Extended stacktrace output #49

Merged
merged 6 commits into from
Jun 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,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