|
@@ -21,15 +21,6 @@
|
|
|
// return errors.Wrap(err, "read failed")
|
|
// return errors.Wrap(err, "read failed")
|
|
|
// }
|
|
// }
|
|
|
//
|
|
//
|
|
|
-// Retrieving the stack trace of an error or wrapper
|
|
|
|
|
-//
|
|
|
|
|
-// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
|
|
|
|
-// invoked. This information can be retrieved with the following interface.
|
|
|
|
|
-//
|
|
|
|
|
-// type Stack interface {
|
|
|
|
|
-// Stack() []uintptr
|
|
|
|
|
-// }
|
|
|
|
|
-//
|
|
|
|
|
// Retrieving the cause of an error
|
|
// Retrieving the cause of an error
|
|
|
//
|
|
//
|
|
|
// Using errors.Wrap constructs a stack of errors, adding context to the
|
|
// Using errors.Wrap constructs a stack of errors, adding context to the
|
|
@@ -51,25 +42,23 @@
|
|
|
// default:
|
|
// default:
|
|
|
// // unknown error
|
|
// // unknown error
|
|
|
// }
|
|
// }
|
|
|
|
|
+//
|
|
|
|
|
+// Retrieving the stack trace of an error or wrapper
|
|
|
|
|
+//
|
|
|
|
|
+// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
|
|
|
|
+// invoked. This information can be retrieved with the following interface.
|
|
|
|
|
+//
|
|
|
|
|
+// type Stacktrace interface {
|
|
|
|
|
+// Stacktrace() []Frame
|
|
|
|
|
+// }
|
|
|
package errors
|
|
package errors
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
"errors"
|
|
"errors"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"io"
|
|
"io"
|
|
|
- "runtime"
|
|
|
|
|
- "strings"
|
|
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
-// stack represents a stack of program counters.
|
|
|
|
|
-type stack []uintptr
|
|
|
|
|
-
|
|
|
|
|
-func (s *stack) Stack() []uintptr { return *s }
|
|
|
|
|
-
|
|
|
|
|
-func (s *stack) Location() (string, int) {
|
|
|
|
|
- return location((*s)[0] - 1)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// New returns an error that formats as the given text.
|
|
// New returns an error that formats as the given text.
|
|
|
func New(text string) error {
|
|
func New(text string) error {
|
|
|
return struct {
|
|
return struct {
|
|
@@ -167,7 +156,11 @@ func Cause(err error) error {
|
|
|
// Fprint prints the error to the supplied writer.
|
|
// Fprint prints the error to the supplied writer.
|
|
|
// If the error implements the Causer interface described in Cause
|
|
// If the error implements the Causer interface described in Cause
|
|
|
// Print will recurse into the error's cause.
|
|
// Print will recurse into the error's cause.
|
|
|
-// If the error implements the inteface:
|
|
|
|
|
|
|
+// If the error implements one of the following interfaces:
|
|
|
|
|
+//
|
|
|
|
|
+// type Stacktrace interface {
|
|
|
|
|
+// Stacktrace() []Frame
|
|
|
|
|
+// }
|
|
|
//
|
|
//
|
|
|
// type Location interface {
|
|
// type Location interface {
|
|
|
// Location() (file string, line int)
|
|
// Location() (file string, line int)
|
|
@@ -175,18 +168,29 @@ func Cause(err error) error {
|
|
|
//
|
|
//
|
|
|
// Print will also print the file and line of the error.
|
|
// Print will also print the file and line of the error.
|
|
|
// If err is nil, nothing is printed.
|
|
// If err is nil, nothing is printed.
|
|
|
|
|
+//
|
|
|
|
|
+// Deprecated: Fprint will be removed in version 0.7.
|
|
|
func Fprint(w io.Writer, err error) {
|
|
func Fprint(w io.Writer, err error) {
|
|
|
type location interface {
|
|
type location interface {
|
|
|
Location() (string, int)
|
|
Location() (string, int)
|
|
|
}
|
|
}
|
|
|
|
|
+ type stacktrace interface {
|
|
|
|
|
+ Stacktrace() []Frame
|
|
|
|
|
+ }
|
|
|
type message interface {
|
|
type message interface {
|
|
|
Message() string
|
|
Message() string
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for err != nil {
|
|
for err != nil {
|
|
|
- if err, ok := err.(location); ok {
|
|
|
|
|
|
|
+ switch err := err.(type) {
|
|
|
|
|
+ case stacktrace:
|
|
|
|
|
+ frame := err.Stacktrace()[0]
|
|
|
|
|
+ fmt.Fprintf(w, "%+s:%d: ", frame, frame)
|
|
|
|
|
+ case location:
|
|
|
file, line := err.Location()
|
|
file, line := err.Location()
|
|
|
fmt.Fprintf(w, "%s:%d: ", file, line)
|
|
fmt.Fprintf(w, "%s:%d: ", file, line)
|
|
|
|
|
+ default:
|
|
|
|
|
+ // de nada
|
|
|
}
|
|
}
|
|
|
switch err := err.(type) {
|
|
switch err := err.(type) {
|
|
|
case message:
|
|
case message:
|
|
@@ -202,58 +206,3 @@ func Fprint(w io.Writer, err error) {
|
|
|
err = cause.Cause()
|
|
err = cause.Cause()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-func callers() *stack {
|
|
|
|
|
- const depth = 32
|
|
|
|
|
- var pcs [depth]uintptr
|
|
|
|
|
- n := runtime.Callers(3, pcs[:])
|
|
|
|
|
- var st stack = pcs[0:n]
|
|
|
|
|
- return &st
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// location returns the source file and line matching pc.
|
|
|
|
|
-func location(pc uintptr) (string, int) {
|
|
|
|
|
- fn := runtime.FuncForPC(pc)
|
|
|
|
|
- if fn == nil {
|
|
|
|
|
- return "unknown", 0
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Here we want to get the source file path relative to the compile time
|
|
|
|
|
- // GOPATH. As of Go 1.6.x there is no direct way to know the compiled
|
|
|
|
|
- // GOPATH at runtime, but we can infer the number of path segments in the
|
|
|
|
|
- // GOPATH. We note that fn.Name() returns the function name qualified by
|
|
|
|
|
- // the import path, which does not include the GOPATH. Thus we can trim
|
|
|
|
|
- // segments from the beginning of the file path until the number of path
|
|
|
|
|
- // separators remaining is one more than the number of path separators in
|
|
|
|
|
- // the function name. For example, given:
|
|
|
|
|
- //
|
|
|
|
|
- // GOPATH /home/user
|
|
|
|
|
- // file /home/user/src/pkg/sub/file.go
|
|
|
|
|
- // fn.Name() pkg/sub.Type.Method
|
|
|
|
|
- //
|
|
|
|
|
- // We want to produce:
|
|
|
|
|
- //
|
|
|
|
|
- // pkg/sub/file.go
|
|
|
|
|
- //
|
|
|
|
|
- // From this we can easily see that fn.Name() has one less path separator
|
|
|
|
|
- // than our desired output. We count separators from the end of the file
|
|
|
|
|
- // path until it finds two more than in the function name and then move
|
|
|
|
|
- // one character forward to preserve the initial path segment without a
|
|
|
|
|
- // leading separator.
|
|
|
|
|
- const sep = "/"
|
|
|
|
|
- goal := strings.Count(fn.Name(), sep) + 2
|
|
|
|
|
- file, line := fn.FileLine(pc)
|
|
|
|
|
- i := len(file)
|
|
|
|
|
- for n := 0; n < goal; n++ {
|
|
|
|
|
- i = strings.LastIndex(file[:i], sep)
|
|
|
|
|
- if i == -1 {
|
|
|
|
|
- // not enough separators found, set i so that the slice expression
|
|
|
|
|
- // below leaves file unmodified
|
|
|
|
|
- i = -len(sep)
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // get back to 0 or trim the leading separator
|
|
|
|
|
- file = file[i+len(sep):]
|
|
|
|
|
- return file, line
|
|
|
|
|
-}
|
|
|