mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-04 20:20:19 +00:00
184 lines
6.1 KiB
Go
184 lines
6.1 KiB
Go
|
package merry
|
||
|
|
||
|
import "fmt"
|
||
|
|
||
|
// Wrapper knows how to wrap errors with context information.
|
||
|
type Wrapper interface {
|
||
|
// Wrap returns a new error, wrapping the argument, and typically adding some context information.
|
||
|
// skipCallers is how many callers to skip when capturing a stack to skip to the caller of the merry
|
||
|
// API surface. It's intended to make it possible to write wrappers which capture stacktraces. e.g.
|
||
|
//
|
||
|
// func CaptureStack() Wrapper {
|
||
|
// return WrapperFunc(func(err error, skipCallers int) error {
|
||
|
// s := make([]uintptr, 50)
|
||
|
// // Callers
|
||
|
// l := runtime.Callers(2+skipCallers, s[:])
|
||
|
// return WithStack(s[:l]).Wrap(err, skipCallers + 1)
|
||
|
// })
|
||
|
// }
|
||
|
Wrap(err error, skipCallers int) error
|
||
|
}
|
||
|
|
||
|
// WrapperFunc implements Wrapper.
|
||
|
type WrapperFunc func(error, int) error
|
||
|
|
||
|
// Wrap implements the Wrapper interface.
|
||
|
func (w WrapperFunc) Wrap(err error, callerDepth int) error {
|
||
|
return w(err, callerDepth+1)
|
||
|
}
|
||
|
|
||
|
// WithValue associates a key/value pair with an error.
|
||
|
func WithValue(key, value interface{}) Wrapper {
|
||
|
return WrapperFunc(func(err error, _ int) error {
|
||
|
return Set(err, key, value)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// WithMessage overrides the value returned by err.Error().
|
||
|
func WithMessage(msg string) Wrapper {
|
||
|
return WithValue(errKeyMessage, msg)
|
||
|
}
|
||
|
|
||
|
// WithMessagef overrides the value returned by err.Error().
|
||
|
func WithMessagef(format string, args ...interface{}) Wrapper {
|
||
|
return WrapperFunc(func(err error, _ int) error {
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return Set(err, errKeyMessage, fmt.Sprintf(format, args...))
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// WithUserMessage associates an end-user message with an error.
|
||
|
func WithUserMessage(msg string) Wrapper {
|
||
|
return WithValue(errKeyUserMessage, msg)
|
||
|
}
|
||
|
|
||
|
// WithUserMessagef associates a formatted end-user message with an error.
|
||
|
func WithUserMessagef(format string, args ...interface{}) Wrapper {
|
||
|
return WrapperFunc(func(err error, _ int) error {
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return Set(err, errKeyUserMessage, fmt.Sprintf(format, args...))
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// AppendMessage a message after the current error message, in the format "original: new".
|
||
|
func AppendMessage(msg string) Wrapper {
|
||
|
return WrapperFunc(func(err error, _ int) error {
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return Set(err, errKeyMessage, err.Error()+": "+msg)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// AppendMessagef is the same as AppendMessage, but with a formatted message.
|
||
|
func AppendMessagef(format string, args ...interface{}) Wrapper {
|
||
|
return WrapperFunc(func(err error, _ int) error {
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return Set(err, errKeyMessage, err.Error()+": "+fmt.Sprintf(format, args...))
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// PrependMessage a message before the current error message, in the format "new: original".
|
||
|
func PrependMessage(msg string) Wrapper {
|
||
|
return WrapperFunc(func(err error, _ int) error {
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return Set(err, errKeyMessage, msg+": "+err.Error())
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// PrependMessagef is the same as PrependMessage, but with a formatted message.
|
||
|
func PrependMessagef(format string, args ...interface{}) Wrapper {
|
||
|
return WrapperFunc(func(err error, _ int) error {
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return Set(err, errKeyMessage, fmt.Sprintf(format, args...)+": "+err.Error())
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// WithHTTPCode associates an HTTP status code with an error.
|
||
|
func WithHTTPCode(statusCode int) Wrapper {
|
||
|
return WithValue(errKeyHTTPCode, statusCode)
|
||
|
}
|
||
|
|
||
|
// WithStack associates a stack of caller frames with an error. Generally, this package
|
||
|
// will automatically capture and associate a stack with errors which are created or
|
||
|
// wrapped by this package. But this allows the caller to associate an externally
|
||
|
// generated stack.
|
||
|
func WithStack(stack []uintptr) Wrapper {
|
||
|
return WithValue(errKeyStack, stack)
|
||
|
}
|
||
|
|
||
|
// WithFormattedStack associates a stack of pre-formatted strings describing frames of a
|
||
|
// stacktrace. Generally, a formatted stack is generated from the raw []uintptr stack
|
||
|
// associated with the error, but a pre-formatted stack can be associated with the error
|
||
|
// instead, and takes precedence over the raw stack. This is useful if pre-formatted
|
||
|
// stack information is coming from some other source.
|
||
|
func WithFormattedStack(stack []string) Wrapper {
|
||
|
return WithValue(errKeyStack, stack)
|
||
|
}
|
||
|
|
||
|
// NoCaptureStack will suppress capturing a stack, even if StackCaptureEnabled() == true.
|
||
|
func NoCaptureStack() Wrapper {
|
||
|
return WrapperFunc(func(err error, _ int) error {
|
||
|
// if this err already has a stack set, there is no need to set the
|
||
|
// stack property again, and we don't want to override the prior the stack
|
||
|
if HasStack(err) {
|
||
|
return err
|
||
|
}
|
||
|
return Set(err, errKeyStack, nil)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// CaptureStack will override an earlier stack with a stack captured from the current
|
||
|
// call site. If StackCaptureEnabled() == false, this is a no-op.
|
||
|
//
|
||
|
// If force is set, StackCaptureEnabled() will be ignored: a stack will always be captured.
|
||
|
func CaptureStack(force bool) Wrapper {
|
||
|
return WrapperFunc(func(err error, callerDepth int) error {
|
||
|
return captureStack(err, callerDepth+1, force || StackCaptureEnabled())
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// WithCause sets one error as the cause of another error. This is useful for associating errors
|
||
|
// from lower API levels with sentinel errors in higher API levels. errors.Is() and errors.As()
|
||
|
// will traverse both the main chain of error wrappers, as well as down the chain of causes.
|
||
|
func WithCause(err error) Wrapper {
|
||
|
return WrapperFunc(func(nerr error, _ int) error {
|
||
|
if nerr == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return &errWithCause{err: nerr, cause: err}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Set wraps an error with a key/value pair. This is the simplest form of associating
|
||
|
// a value with an error. It does not capture a stacktrace, invoke hooks, or do any
|
||
|
// other processing. It is mainly intended as a primitive for writing Wrapper implementations.
|
||
|
//
|
||
|
// if err is nil, returns nil.
|
||
|
//
|
||
|
// Keeping this private for now. If it proves useful, it may be made public later, but
|
||
|
// for now, external packages can get the same behavor with this:
|
||
|
//
|
||
|
// WithValue(key, value).Wrap(err)
|
||
|
//
|
||
|
func Set(err error, key, value interface{}) error {
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
return &errWithValue{
|
||
|
err: err,
|
||
|
key: key,
|
||
|
value: value,
|
||
|
}
|
||
|
}
|