mirror of
https://github.com/pkg/errors.git
synced 2025-05-25 00:40:26 +00:00
Merge pull request #81 from pkg/withMessage-withStack
Refactor withMessage/withStack
This commit is contained in:
commit
9cadab9279
136
errors.go
136
errors.go
@ -91,68 +91,60 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// _error is an error implementation returned by New and Errorf
|
|
||||||
// that implements its own fmt.Formatter.
|
|
||||||
type _error struct {
|
|
||||||
msg string
|
|
||||||
*stack
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e _error) Error() string { return e.msg }
|
|
||||||
|
|
||||||
func (e _error) Format(s fmt.State, verb rune) {
|
|
||||||
switch verb {
|
|
||||||
case 'v':
|
|
||||||
if s.Flag('+') {
|
|
||||||
io.WriteString(s, e.msg)
|
|
||||||
fmt.Fprintf(s, "%+v", e.StackTrace())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case 's':
|
|
||||||
io.WriteString(s, e.msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns an error with the supplied message.
|
// New returns an error with the supplied message.
|
||||||
|
// New also records the stack trace at the point it was called.
|
||||||
func New(message string) error {
|
func New(message string) error {
|
||||||
return _error{
|
return &fundamental{
|
||||||
message,
|
msg: message,
|
||||||
callers(),
|
stack: callers(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errorf formats according to a format specifier and returns the string
|
// Errorf formats according to a format specifier and returns the string
|
||||||
// as a value that satisfies error.
|
// as a value that satisfies error.
|
||||||
|
// Errorf also records the stack trace at the point it was called.
|
||||||
func Errorf(format string, args ...interface{}) error {
|
func Errorf(format string, args ...interface{}) error {
|
||||||
return _error{
|
return &fundamental{
|
||||||
fmt.Sprintf(format, args...),
|
msg: fmt.Sprintf(format, args...),
|
||||||
callers(),
|
stack: callers(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type cause struct {
|
// fundamental is an error that has a message and a stack, but no caller.
|
||||||
cause error
|
type fundamental struct {
|
||||||
msg string
|
msg string
|
||||||
}
|
|
||||||
|
|
||||||
func (c cause) Error() string { return fmt.Sprintf("%s: %v", c.msg, c.Cause()) }
|
|
||||||
func (c cause) Cause() error { return c.cause }
|
|
||||||
|
|
||||||
// wrapper is an error implementation returned by Wrap and Wrapf
|
|
||||||
// that implements its own fmt.Formatter.
|
|
||||||
type wrapper struct {
|
|
||||||
cause
|
|
||||||
*stack
|
*stack
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w wrapper) Format(s fmt.State, verb rune) {
|
func (f *fundamental) Error() string { return f.msg }
|
||||||
|
|
||||||
|
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||||
switch verb {
|
switch verb {
|
||||||
case 'v':
|
case 'v':
|
||||||
if s.Flag('+') {
|
if s.Flag('+') {
|
||||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
io.WriteString(s, f.msg)
|
||||||
io.WriteString(s, w.msg)
|
f.stack.Format(s, verb)
|
||||||
fmt.Fprintf(s, "%+v", w.StackTrace())
|
return
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case 's', 'q':
|
||||||
|
io.WriteString(s, f.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type withStack struct {
|
||||||
|
error
|
||||||
|
*stack
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *withStack) Cause() error { return w.error }
|
||||||
|
|
||||||
|
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||||
|
switch verb {
|
||||||
|
case 'v':
|
||||||
|
if s.Flag('+') {
|
||||||
|
fmt.Fprintf(s, "%+v", w.Cause())
|
||||||
|
w.stack.Format(s, verb)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
@ -165,31 +157,61 @@ func (w wrapper) Format(s fmt.State, verb rune) {
|
|||||||
|
|
||||||
// Wrap returns an error annotating err with message.
|
// Wrap returns an error annotating err with message.
|
||||||
// If err is nil, Wrap returns nil.
|
// If err is nil, Wrap returns nil.
|
||||||
|
// Wrap is conceptually the same as calling
|
||||||
|
//
|
||||||
|
// errors.WithStack(errors.WithMessage(err, msg))
|
||||||
func Wrap(err error, message string) error {
|
func Wrap(err error, message string) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return wrapper{
|
err = &withMessage{
|
||||||
cause: cause{
|
cause: err,
|
||||||
cause: err,
|
msg: message,
|
||||||
msg: message,
|
}
|
||||||
},
|
return &withStack{
|
||||||
stack: callers(),
|
err,
|
||||||
|
callers(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapf returns an error annotating err with the format specifier.
|
// Wrapf returns an error annotating err with the format specifier.
|
||||||
// If err is nil, Wrapf returns nil.
|
// If err is nil, Wrapf returns nil.
|
||||||
|
// Wrapf is conceptually the same as calling
|
||||||
|
//
|
||||||
|
// errors.WithStack(errors.WithMessage(err, format, args...))
|
||||||
func Wrapf(err error, format string, args ...interface{}) error {
|
func Wrapf(err error, format string, args ...interface{}) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return wrapper{
|
err = &withMessage{
|
||||||
cause: cause{
|
cause: err,
|
||||||
cause: err,
|
msg: fmt.Sprintf(format, args...),
|
||||||
msg: fmt.Sprintf(format, args...),
|
}
|
||||||
},
|
return &withStack{
|
||||||
stack: callers(),
|
err,
|
||||||
|
callers(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type withMessage struct {
|
||||||
|
cause error
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||||
|
func (w *withMessage) Cause() error { return w.cause }
|
||||||
|
|
||||||
|
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||||
|
switch verb {
|
||||||
|
case 'v':
|
||||||
|
if s.Flag('+') {
|
||||||
|
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||||
|
io.WriteString(s, w.msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case 's', 'q':
|
||||||
|
io.WriteString(s, w.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,8 @@ func TestFormatNew(t *testing.T) {
|
|||||||
"\t.+/github.com/pkg/errors/format_test.go:25",
|
"\t.+/github.com/pkg/errors/format_test.go:25",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for i, tt := range tests {
|
||||||
testFormatRegexp(t, tt.error, tt.format, tt.want)
|
testFormatRegexp(t, i, tt.error, tt.format, tt.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,8 +55,8 @@ func TestFormatErrorf(t *testing.T) {
|
|||||||
"\t.+/github.com/pkg/errors/format_test.go:51",
|
"\t.+/github.com/pkg/errors/format_test.go:51",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for i, tt := range tests {
|
||||||
testFormatRegexp(t, tt.error, tt.format, tt.want)
|
testFormatRegexp(t, i, tt.error, tt.format, tt.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,14 +83,32 @@ func TestFormatWrap(t *testing.T) {
|
|||||||
Wrap(io.EOF, "error"),
|
Wrap(io.EOF, "error"),
|
||||||
"%s",
|
"%s",
|
||||||
"error: EOF",
|
"error: EOF",
|
||||||
|
}, {
|
||||||
|
Wrap(io.EOF, "error"),
|
||||||
|
"%v",
|
||||||
|
"error: EOF",
|
||||||
|
}, {
|
||||||
|
Wrap(io.EOF, "error"),
|
||||||
|
"%+v",
|
||||||
|
"EOF\n" +
|
||||||
|
"error\n" +
|
||||||
|
"github.com/pkg/errors.TestFormatWrap\n" +
|
||||||
|
"\t.+/github.com/pkg/errors/format_test.go:91",
|
||||||
|
}, {
|
||||||
|
Wrap(Wrap(io.EOF, "error1"), "error2"),
|
||||||
|
"%+v",
|
||||||
|
"EOF\n" +
|
||||||
|
"error1\n" +
|
||||||
|
"github.com/pkg/errors.TestFormatWrap\n" +
|
||||||
|
"\t.+/github.com/pkg/errors/format_test.go:98\n",
|
||||||
}, {
|
}, {
|
||||||
Wrap(New("error with space"), "context"),
|
Wrap(New("error with space"), "context"),
|
||||||
"%q",
|
"%q",
|
||||||
`"context: error with space"`,
|
`"context: error with space"`,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for i, tt := range tests {
|
||||||
testFormatRegexp(t, tt.error, tt.format, tt.want)
|
testFormatRegexp(t, i, tt.error, tt.format, tt.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,20 +118,24 @@ func TestFormatWrapf(t *testing.T) {
|
|||||||
format string
|
format string
|
||||||
want string
|
want string
|
||||||
}{{
|
}{{
|
||||||
|
Wrapf(io.EOF, "error%d", 2),
|
||||||
|
"%s",
|
||||||
|
"error2: EOF",
|
||||||
|
}, {
|
||||||
|
Wrapf(io.EOF, "error%d", 2),
|
||||||
|
"%v",
|
||||||
|
"error2: EOF",
|
||||||
|
}, {
|
||||||
|
Wrapf(io.EOF, "error%d", 2),
|
||||||
|
"%+v",
|
||||||
|
"EOF\n" +
|
||||||
|
"error2\n" +
|
||||||
|
"github.com/pkg/errors.TestFormatWrapf\n" +
|
||||||
|
"\t.+/github.com/pkg/errors/format_test.go:129",
|
||||||
|
}, {
|
||||||
Wrapf(New("error"), "error%d", 2),
|
Wrapf(New("error"), "error%d", 2),
|
||||||
"%s",
|
"%s",
|
||||||
"error2: error",
|
"error2: error",
|
||||||
}, {
|
|
||||||
Wrap(io.EOF, "error"),
|
|
||||||
"%v",
|
|
||||||
"error: EOF",
|
|
||||||
}, {
|
|
||||||
Wrap(io.EOF, "error"),
|
|
||||||
"%+v",
|
|
||||||
"EOF\n" +
|
|
||||||
"error\n" +
|
|
||||||
"github.com/pkg/errors.TestFormatWrapf\n" +
|
|
||||||
"\t.+/github.com/pkg/errors/format_test.go:111",
|
|
||||||
}, {
|
}, {
|
||||||
Wrapf(New("error"), "error%d", 2),
|
Wrapf(New("error"), "error%d", 2),
|
||||||
"%v",
|
"%v",
|
||||||
@ -123,22 +145,15 @@ func TestFormatWrapf(t *testing.T) {
|
|||||||
"%+v",
|
"%+v",
|
||||||
"error\n" +
|
"error\n" +
|
||||||
"github.com/pkg/errors.TestFormatWrapf\n" +
|
"github.com/pkg/errors.TestFormatWrapf\n" +
|
||||||
"\t.+/github.com/pkg/errors/format_test.go:122",
|
"\t.+/github.com/pkg/errors/format_test.go:144",
|
||||||
}, {
|
|
||||||
Wrap(Wrap(io.EOF, "error1"), "error2"),
|
|
||||||
"%+v",
|
|
||||||
"EOF\n" +
|
|
||||||
"error1\n" +
|
|
||||||
"github.com/pkg/errors.TestFormatWrapf\n" +
|
|
||||||
"\t.+/github.com/pkg/errors/format_test.go:128\n",
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for i, tt := range tests {
|
||||||
testFormatRegexp(t, tt.error, tt.format, tt.want)
|
testFormatRegexp(t, i, tt.error, tt.format, tt.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFormatRegexp(t *testing.T, arg interface{}, format, want string) {
|
func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) {
|
||||||
got := fmt.Sprintf(format, arg)
|
got := fmt.Sprintf(format, arg)
|
||||||
lines := strings.SplitN(got, "\n", -1)
|
lines := strings.SplitN(got, "\n", -1)
|
||||||
for i, w := range strings.SplitN(want, "\n", -1) {
|
for i, w := range strings.SplitN(want, "\n", -1) {
|
||||||
@ -147,7 +162,7 @@ func testFormatRegexp(t *testing.T, arg interface{}, format, want string) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !match {
|
if !match {
|
||||||
t.Errorf("fmt.Sprintf(%q, err): got: %q, want: %q", format, got, want)
|
t.Errorf("test %d: line %d: fmt.Sprintf(%q, err): got: %q, want: %q", n+1, i+1, format, got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
stack.go
13
stack.go
@ -100,6 +100,19 @@ func (st StackTrace) Format(s fmt.State, verb rune) {
|
|||||||
// stack represents a stack of program counters.
|
// stack represents a stack of program counters.
|
||||||
type stack []uintptr
|
type stack []uintptr
|
||||||
|
|
||||||
|
func (s *stack) Format(st fmt.State, verb rune) {
|
||||||
|
switch verb {
|
||||||
|
case 'v':
|
||||||
|
switch {
|
||||||
|
case st.Flag('+'):
|
||||||
|
for _, pc := range *s {
|
||||||
|
f := Frame(pc)
|
||||||
|
fmt.Fprintf(st, "\n%+v", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stack) StackTrace() StackTrace {
|
func (s *stack) StackTrace() StackTrace {
|
||||||
f := make([]Frame, len(*s))
|
f := make([]Frame, len(*s))
|
||||||
for i := 0; i < len(f); i++ {
|
for i := 0; i < len(f); i++ {
|
||||||
|
@ -120,8 +120,8 @@ func TestFrameFormat(t *testing.T) {
|
|||||||
"unknown:0",
|
"unknown:0",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for i, tt := range tests {
|
||||||
testFormatRegexp(t, tt.Frame, tt.format, tt.want)
|
testFormatRegexp(t, i, tt.Frame, tt.format, tt.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,12 +155,12 @@ func TestTrimGOPATH(t *testing.T) {
|
|||||||
"github.com/pkg/errors/stack_test.go",
|
"github.com/pkg/errors/stack_test.go",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for i, tt := range tests {
|
||||||
pc := tt.Frame.pc()
|
pc := tt.Frame.pc()
|
||||||
fn := runtime.FuncForPC(pc)
|
fn := runtime.FuncForPC(pc)
|
||||||
file, _ := fn.FileLine(pc)
|
file, _ := fn.FileLine(pc)
|
||||||
got := trimGOPATH(fn.Name(), file)
|
got := trimGOPATH(fn.Name(), file)
|
||||||
testFormatRegexp(t, got, "%s", tt.want)
|
testFormatRegexp(t, i, got, "%s", tt.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ func TestStackTrace(t *testing.T) {
|
|||||||
"\t.+/github.com/pkg/errors/stack_test.go:198", // this is the stack of Errorf's caller's caller
|
"\t.+/github.com/pkg/errors/stack_test.go:198", // this is the stack of Errorf's caller's caller
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
for _, tt := range tests {
|
for i, tt := range tests {
|
||||||
x, ok := tt.err.(interface {
|
x, ok := tt.err.(interface {
|
||||||
StackTrace() StackTrace
|
StackTrace() StackTrace
|
||||||
})
|
})
|
||||||
@ -214,7 +214,7 @@ func TestStackTrace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
st := x.StackTrace()
|
st := x.StackTrace()
|
||||||
for j, want := range tt.want {
|
for j, want := range tt.want {
|
||||||
testFormatRegexp(t, st[j], "%+v", want)
|
testFormatRegexp(t, i, st[j], "%+v", want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,7 +286,7 @@ func TestStackTraceFormat(t *testing.T) {
|
|||||||
`\[\]errors.Frame{stack_test.go:225, stack_test.go:284}`,
|
`\[\]errors.Frame{stack_test.go:225, stack_test.go:284}`,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for i, tt := range tests {
|
||||||
testFormatRegexp(t, tt.StackTrace, tt.format, tt.want)
|
testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user