From 59cbfb186e38ac5aa32984e93ecc9d0206945707 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 9 Oct 2024 09:25:07 -0700 Subject: [PATCH] util/vizerror: add func WithInternal, Error.InternalError accessor Updates tailscale/corp#23781 Change-Id: I072d0169703aefb0f37b1fa715881f1ccb561f03 Signed-off-by: Brad Fitzpatrick --- util/vizerror/vizerror.go | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/util/vizerror/vizerror.go b/util/vizerror/vizerror.go index 158786494..11fe3f3c7 100644 --- a/util/vizerror/vizerror.go +++ b/util/vizerror/vizerror.go @@ -12,7 +12,8 @@ // Error is an error that is safe to display to end users. type Error struct { - err error + err error + internalErr error } // Error implements the error interface. @@ -20,14 +21,21 @@ func (e Error) Error() string { 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. 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. 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. @@ -36,11 +44,15 @@ func (e Error) Unwrap() 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 { if err == nil { return nil } - return Error{err} + return Error{err: err} } // 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) 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} +}