util/vizerror: add func WithInternal, Error.InternalError accessor

Updates tailscale/corp#23781

Change-Id: I072d0169703aefb0f37b1fa715881f1ccb561f03
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2024-10-09 09:25:07 -07:00
parent f6d4d03355
commit 59cbfb186e

View File

@ -12,7 +12,8 @@
// Error is an error that is safe to display to end users. // Error is an error that is safe to display to end users.
type Error struct { type Error struct {
err error err error
internalErr error
} }
// Error implements the error interface. // Error implements the error interface.
@ -20,14 +21,21 @@ func (e Error) Error() string {
return e.err.Error() return e.err.Error()
} }
// InternalError returns the internal error that should not be shown to end
// users. It will be nil if the user-visible error was not constructed using
// WithInternal.
func (e Error) InternalError() error {
return e.internalErr
}
// New returns an error that formats as the given text. It always returns a vizerror.Error. // New returns an error that formats as the given text. It always returns a vizerror.Error.
func New(text string) error { func New(text string) error {
return Error{errors.New(text)} return Error{err: errors.New(text)}
} }
// Errorf returns an Error with the specified format and values. It always returns a vizerror.Error. // Errorf returns an Error with the specified format and values. It always returns a vizerror.Error.
func Errorf(format string, a ...any) error { func Errorf(format string, a ...any) error {
return Error{fmt.Errorf(format, a...)} return Error{err: fmt.Errorf(format, a...)}
} }
// Unwrap returns the underlying error. // Unwrap returns the underlying error.
@ -36,11 +44,15 @@ func (e Error) Unwrap() error {
} }
// Wrap wraps err with a vizerror.Error. // Wrap wraps err with a vizerror.Error.
//
// Deprecated: this is almost always the wrong thing to do. Are you really sure
// you know exactly what err.Error() will stringify to and be safe to show to
// users?
func Wrap(err error) error { func Wrap(err error) error {
if err == nil { if err == nil {
return nil return nil
} }
return Error{err} return Error{err: err}
} }
// As returns the first vizerror.Error in err's chain. // As returns the first vizerror.Error in err's chain.
@ -48,3 +60,10 @@ func As(err error) (e Error, ok bool) {
ok = errors.As(err, &e) ok = errors.As(err, &e)
return return
} }
// WithInternal returns a new ErrorWithInternal combining a user-visible error
// string and an internal error to pass around for internal logging but not
// to be shown to end users.
func WithInternal(visibleError string, internalErr error) error {
return Error{err: errors.New(visibleError), internalErr: internalErr}
}