diff --git a/errors.go b/errors.go index dcc1456..31ca468 100644 --- a/errors.go +++ b/errors.go @@ -6,23 +6,41 @@ import ( "io" "os" "runtime" + "strings" ) +type loc uintptr + +func (l loc) Location() (string, int) { + pc := uintptr(l) + fn := runtime.FuncForPC(pc) + if fn == nil { + return "unknown", 0 + } + + _, prefix, _, _ := runtime.Caller(0) + file, line := fn.FileLine(pc) + if i := strings.LastIndex(prefix, "github.com/pkg/errors"); i > 0 { + file = file[i:] + } + return file, line +} + // New returns an error that formats as the given text. func New(text string) error { return struct { error - pc uintptr + loc }{ fmt.Errorf(text), - pc(), + loc(pc()), } } type e struct { cause error message string - pc uintptr + loc } func (e *e) Error() string { @@ -42,7 +60,7 @@ func Wrap(cause error, message string) error { return &e{ cause: cause, message: message, - pc: pc(), + loc: loc(pc()), } } @@ -77,6 +95,15 @@ type locationer interface { } // Print prints the error to Stderr. +// If the error implements the Causer interface described in Cause +// Print will recurse into the error's cause. +// If the error implements the inteface: +// +// type Location interface { +// Location() (file string, line int) +// } +// +// Print will also print the file and line of the error. func Print(err error) { Fprint(os.Stderr, err) } @@ -89,7 +116,7 @@ func Fprint(w io.Writer, err error) { location, ok := err.(locationer) if ok { file, line := location.Location() - fmt.Fprint(w, "%s:%d: ", file, line) + fmt.Fprintf(w, "%s:%d: ", file, line) } switch err := err.(type) { case *e: diff --git a/errors_test.go b/errors_test.go index 2eb3de7..220a4f9 100644 --- a/errors_test.go +++ b/errors_test.go @@ -124,10 +124,10 @@ func TestFprint(t *testing.T) { want: "error\n", }, { err: Wrap(x, "message"), - want: "message\nerror\n", + want: "github.com/pkg/errors/errors_test.go:126: message\nerror\n", }, { err: Wrap(Wrap(x, "message"), "another message"), - want: "another message\nmessage\nerror\n", + want: "github.com/pkg/errors/errors_test.go:129: another message\ngithub.com/pkg/errors/errors_test.go:129: message\nerror\n", }} for i, tt := range tests { diff --git a/example_test.go b/example_test.go index 3c72e4f..98c0ac7 100644 --- a/example_test.go +++ b/example_test.go @@ -23,7 +23,10 @@ func ExampleWrap() { } func fn() error { - return errors.Wrap(errors.Wrap(errors.Wrap(errors.New("error"), "inner"), "middle"), "outer") + e1 := errors.New("error") + e2 := errors.Wrap(e1, "inner") + e3 := errors.Wrap(e2, "middle") + return errors.Wrap(e3, "outer") } func ExampleCause() { @@ -39,8 +42,8 @@ func ExampleFprint() { err := fn() errors.Fprint(os.Stdout, err) - // Output: outer - // middle - // inner + // Output: github.com/pkg/errors/example_test.go:29: outer + // github.com/pkg/errors/example_test.go:28: middle + // github.com/pkg/errors/example_test.go:27: inner // error }