From d4344d08e411f382869f5c7a4ed9a2189a414553 Mon Sep 17 00:00:00 2001 From: Gimmy Date: Mon, 29 Apr 2024 16:35:24 +0300 Subject: [PATCH 01/11] add WrappedError --- errors.go | 21 ++++++++++++++++++++- werr/errors.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 werr/errors.go diff --git a/errors.go b/errors.go index 4e5a725..ef9da46 100644 --- a/errors.go +++ b/errors.go @@ -1,6 +1,7 @@ package errors import ( + "context" "fmt" "io" "net/http" @@ -8,6 +9,7 @@ import ( "strings" "time" + "github.com/aserto-dev/errors/werr" "github.com/pkg/errors" "github.com/rs/zerolog" "google.golang.org/genproto/googleapis/rpc/errdetails" @@ -27,7 +29,7 @@ var ( ) func NewAsertoError(code string, statusCode codes.Code, httpCode int, msg string) *AsertoError { - asertoError := &AsertoError{code, statusCode, msg, httpCode, map[string]string{}, nil} + asertoError := &AsertoError{code, statusCode, msg, httpCode, map[string]string{}, nil, nil} asertoErrors[code] = asertoError return asertoError } @@ -41,6 +43,14 @@ type AsertoError struct { HTTPCode int data map[string]string errs []error + Ctx context.Context +} + +// Associates a context with the AsertoError. +func (e *AsertoError) WithContext(ctx context.Context) *AsertoError { + c := e.Copy() + c.Ctx = ctx + return c } func (e *AsertoError) Data() map[string]string { @@ -314,6 +324,15 @@ func UnwrapAsertoError(err error) *AsertoError { return aErr } + wErr, ok := err.(*werr.WrappedError) + if ok { + aErr, ok = wErr.Err.(*AsertoError) + if ok { + aErr.Ctx = wErr.Ctx + return aErr + } + } + err = errors.Unwrap(err) if err == nil { break diff --git a/werr/errors.go b/werr/errors.go new file mode 100644 index 0000000..3169283 --- /dev/null +++ b/werr/errors.go @@ -0,0 +1,28 @@ +package werr + +import ( + "context" + "fmt" +) + +// WrappedError represents a standard error +// that can also encapsulate a context. +type WrappedError struct { + Ctx context.Context + Err error +} + +func (w *WrappedError) Error() string { + return fmt.Sprintf("%s", w.Err) +} + +func Wrap(err error, ctx context.Context) *WrappedError { + return &WrappedError{ + Ctx: ctx, + Err: err, + } +} + +func (w *WrappedError) Unwrap() error { + return w.Err +} From 1ecba58b6252b89be71da865597bdd27ff68415c Mon Sep 17 00:00:00 2001 From: Gimmy Date: Tue, 30 Apr 2024 11:11:07 +0300 Subject: [PATCH 02/11] add func that extracts nested logger --- .DS_Store | Bin 0 -> 6148 bytes errors.go | 67 +++++++++++++++++++++++++++++---- errors_test.go | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..153750373303c830067f159b7104bdce44f50a1b GIT binary patch literal 6148 zcmeHKJx&5a6n-NcL2c-?oW#z;gbSFJtc8sY7cl$;Odv~8u-($qLwErWVPfSWtc@N) zzc+7y85b;#A@3zK-|qXF`S>=wk0Bz{oOT;Tbt0-^Fedw$))>cmlx)X)xY+O-!+xXH z$w%W-raDvsRp37=;9h(>r9R!zUG)B%r!Npr7O_#DW$j_!hOc&ccKrJO`FtM7yT8WA zgW%gxd1NiBi?wM$Q))-M)9zI{BjoTf|CqfAhac;V@)eWKSrH$y3qCWtr4HrPrC!Vj zK4BbWgnUk3B0f>piqGb(m=8Y|Xn#*Vn$RR>V&-8i@K(!A7MB{%<=^=^;ihP1JgeO MP6nM+fj?E?8^;2QmjD0& literal 0 HcmV?d00001 diff --git a/errors.go b/errors.go index ef9da46..6869c44 100644 --- a/errors.go +++ b/errors.go @@ -82,6 +82,7 @@ func (e *AsertoError) Copy() *AsertoError { data: dataCopy, errs: e.errs, HTTPCode: e.HTTPCode, + Ctx: e.Ctx, } } @@ -307,6 +308,59 @@ func FromGRPCStatus(grpcStatus status.Status) *AsertoError { return result } +// Return the inner most logger stored in the error context. +func Logger(err error) *zerolog.Logger { + var logger *zerolog.Logger + + if err == nil { + return logger + } + + for { + wErr, ok := err.(*werr.WrappedError) + if ok { + aErr, ok := wErr.Err.(*AsertoError) + if ok { + newLogger := getLogger(aErr.Ctx) + if newLogger != nil { + logger = newLogger + } + } + newLogger := getLogger(wErr.Ctx) + if newLogger != nil { + logger = newLogger + } + } + + aErr, ok := err.(*AsertoError) + if ok { + newLogger := getLogger(aErr.Ctx) + if newLogger != nil { + logger = newLogger + } + } + + err = errors.Unwrap(err) + if err == nil { + break + } + } + + return logger +} + +func getLogger(ctx context.Context) *zerolog.Logger { + if ctx == nil { + return nil + } + logger := zerolog.Ctx(ctx) + if logger == nil || logger == zerolog.DefaultContextLogger || logger.GetLevel() == zerolog.Disabled { + logger = nil + } + + return logger +} + func UnwrapAsertoError(err error) *AsertoError { if err == nil { return nil @@ -319,20 +373,19 @@ func UnwrapAsertoError(err error) *AsertoError { // try to process Aserto error. for { - aErr, ok := err.(*AsertoError) - if ok { - return aErr - } - wErr, ok := err.(*werr.WrappedError) if ok { - aErr, ok = wErr.Err.(*AsertoError) + aErr, ok := wErr.Err.(*AsertoError) if ok { - aErr.Ctx = wErr.Ctx return aErr } } + aErr, ok := err.(*AsertoError) + if ok { + return aErr + } + err = errors.Unwrap(err) if err == nil { break diff --git a/errors_test.go b/errors_test.go index 2ef742b..ed85738 100644 --- a/errors_test.go +++ b/errors_test.go @@ -1,12 +1,16 @@ package errors_test import ( + "context" "net/http" + "os" "testing" "github.com/pkg/errors" + "github.com/rs/zerolog" cerr "github.com/aserto-dev/errors" + "github.com/aserto-dev/errors/werr" "github.com/stretchr/testify/require" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" @@ -198,3 +202,99 @@ func TestWithHttpError(t *testing.T) { unAerr := cerr.UnwrapAsertoError(aerr) assert.Equal(http.StatusNotAcceptable, unAerr.HTTPCode) } + +// returns nil logger if error is nil +func TestLoggerWithNilError(t *testing.T) { + assert := require.New(t) + + var err error + logger := cerr.Logger(err) + assert.Nil(logger) +} + +func TestLoggerWithWrappedNilError(t *testing.T) { + assert := require.New(t) + + var err error + ctx := context.Background() + + logger := cerr.Logger(werr.Wrap(err, ctx)) + assert.Nil(logger) +} + +func TestLoggerWithWrappedErrorsWithContext(t *testing.T) { + assert := require.New(t) + + ctx := context.Background() + err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx) + wrappedErr := errors.Wrap(err, "wrapped error") + + logger := cerr.Logger(wrappedErr) + assert.Nil(logger) +} + +func TestLoggerWithWrappedMultipleWithoutErrorsWithContext(t *testing.T) { + assert := require.New(t) + initialLogger := zerolog.New(os.Stderr) + + ctx1 := context.Background() + ctx := initialLogger.WithContext(ctx1) + err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx) + errWithoutCtx := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error") + wrappedErr := errors.Wrap(errWithoutCtx.Err(err), "wrapped error") + + logger := cerr.Logger(wrappedErr) + assert.NotNil(logger) + assert.Equal(logger, zerolog.Ctx(ctx)) +} + +func TestLoggerWithWrappedMultipleErrorsWithContext(t *testing.T) { + assert := require.New(t) + initialLogger := zerolog.New(os.Stderr) + + ctx1 := context.Background() + ctx := initialLogger.WithContext(ctx1) + err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx) + errWithoutCtx := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error") + wrappedErr := errors.Wrap(err.Err(errWithoutCtx), "wrapped error") + + logger := cerr.Logger(wrappedErr) + assert.NotNil(logger) + assert.Equal(logger, zerolog.Ctx(ctx)) +} + +func TestLoggerWithWrappedMultipleErrorsWithMultipleContexts(t *testing.T) { + assert := require.New(t) + initialLogger := zerolog.New(os.Stderr) + ctx1 := context.Background() + ctx2 := initialLogger.WithContext(ctx1) + err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx1) + err2 := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx2) + wrappedErr := errors.Wrap(err.Err(err2), "wrapped error") + + logger := cerr.Logger(wrappedErr) + ctx1Logger := zerolog.Ctx(ctx1) + ctx2Logger := zerolog.Ctx(ctx2) + + assert.NotNil(logger) + assert.NotEqual(logger, ctx1Logger) + assert.Equal(logger, ctx2Logger) +} + +func TestLoggerWithWrappedMultipleErrorsWithMultipleContextsOuter(t *testing.T) { + assert := require.New(t) + initialLogger := zerolog.New(os.Stderr) + ctx1 := context.Background() + ctx2 := initialLogger.WithContext(ctx1) + err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx1) + err2 := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx2) + wrappedErr := errors.Wrap(err2.Err(err), "wrapped error") + + logger := cerr.Logger(wrappedErr) + ctx1Logger := zerolog.Ctx(ctx1) + ctx2Logger := zerolog.Ctx(ctx2) + + assert.NotNil(logger) + assert.NotEqual(logger, ctx1Logger) + assert.Equal(logger, ctx2Logger) +} From 3000e52f7e81ace7372237b86fdb2e6250db9648 Mon Sep 17 00:00:00 2001 From: Gimmy Date: Tue, 30 Apr 2024 11:21:30 +0300 Subject: [PATCH 03/11] refactor logger extraction --- errors.go | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/errors.go b/errors.go index 6869c44..c5944bd 100644 --- a/errors.go +++ b/errors.go @@ -308,7 +308,15 @@ func FromGRPCStatus(grpcStatus status.Status) *AsertoError { return result } -// Return the inner most logger stored in the error context. +/** + * Logger is a function that retrieves the most inner logger associated with an error. + * + * Parameters: + * - err: The error for which to retrieve the logger. + * + * Returns: + * - logger: The logger associated with the error. If the error is nil or no logger is found, nil is returned. + */ func Logger(err error) *zerolog.Logger { var logger *zerolog.Logger @@ -321,23 +329,15 @@ func Logger(err error) *zerolog.Logger { if ok { aErr, ok := wErr.Err.(*AsertoError) if ok { - newLogger := getLogger(aErr.Ctx) - if newLogger != nil { - logger = newLogger - } - } - newLogger := getLogger(wErr.Ctx) - if newLogger != nil { - logger = newLogger + setLogger(aErr.Ctx, &logger) } + setLogger(wErr.Ctx, &logger) } aErr, ok := err.(*AsertoError) if ok { - newLogger := getLogger(aErr.Ctx) - if newLogger != nil { - logger = newLogger - } + setLogger(aErr.Ctx, &logger) + } err = errors.Unwrap(err) @@ -349,16 +349,25 @@ func Logger(err error) *zerolog.Logger { return logger } -func getLogger(ctx context.Context) *zerolog.Logger { +/** + * setLogger sets the logger pointer to the logger stored in the provided context. + * If the context is nil or the logger in the context is nil, the logger pointer remains unchanged. + * If the logger in the context is the default context logger or has a disabled level, the logger pointer remains unchanged. + * + * @param ctx The context from which to retrieve the logger. + * @param logger The pointer to the logger to be set. + */ +func setLogger(ctx context.Context, logger **zerolog.Logger) { if ctx == nil { - return nil + return } - logger := zerolog.Ctx(ctx) - if logger == nil || logger == zerolog.DefaultContextLogger || logger.GetLevel() == zerolog.Disabled { - logger = nil + + newLogger := zerolog.Ctx(ctx) + if newLogger == nil || newLogger == zerolog.DefaultContextLogger || newLogger.GetLevel() == zerolog.Disabled { + return } - return logger + *logger = newLogger } func UnwrapAsertoError(err error) *AsertoError { From 23e368637a3bafb1b3d8f0fac4178d70c6b96650 Mon Sep 17 00:00:00 2001 From: Gimmy Date: Tue, 30 Apr 2024 11:28:39 +0300 Subject: [PATCH 04/11] fix linter --- errors.go | 8 ++++---- errors_test.go | 2 +- werr/errors.go | 7 +++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/errors.go b/errors.go index c5944bd..f4e546b 100644 --- a/errors.go +++ b/errors.go @@ -327,8 +327,8 @@ func Logger(err error) *zerolog.Logger { for { wErr, ok := err.(*werr.WrappedError) if ok { - aErr, ok := wErr.Err.(*AsertoError) - if ok { + aErr, aOk := wErr.Err.(*AsertoError) + if aOk { setLogger(aErr.Ctx, &logger) } setLogger(wErr.Ctx, &logger) @@ -384,8 +384,8 @@ func UnwrapAsertoError(err error) *AsertoError { for { wErr, ok := err.(*werr.WrappedError) if ok { - aErr, ok := wErr.Err.(*AsertoError) - if ok { + aErr, aOk := wErr.Err.(*AsertoError) + if aOk { return aErr } } diff --git a/errors_test.go b/errors_test.go index ed85738..577e936 100644 --- a/errors_test.go +++ b/errors_test.go @@ -203,7 +203,7 @@ func TestWithHttpError(t *testing.T) { assert.Equal(http.StatusNotAcceptable, unAerr.HTTPCode) } -// returns nil logger if error is nil +// returns nil logger if error is nil. func TestLoggerWithNilError(t *testing.T) { assert := require.New(t) diff --git a/werr/errors.go b/werr/errors.go index 3169283..1890733 100644 --- a/werr/errors.go +++ b/werr/errors.go @@ -23,6 +23,13 @@ func Wrap(err error, ctx context.Context) *WrappedError { } } +func (w *WrappedError) WithContext(ctx context.Context) *WrappedError { + return &WrappedError{ + Ctx: ctx, + Err: w.Err, + } +} + func (w *WrappedError) Unwrap() error { return w.Err } From 017a5edd0d0e6ef75d56cfac8e2847d3f07017c7 Mon Sep 17 00:00:00 2001 From: Gimmy Date: Tue, 30 Apr 2024 11:31:23 +0300 Subject: [PATCH 05/11] cleanup --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 153750373303c830067f159b7104bdce44f50a1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKJx&5a6n-NcL2c-?oW#z;gbSFJtc8sY7cl$;Odv~8u-($qLwErWVPfSWtc@N) zzc+7y85b;#A@3zK-|qXF`S>=wk0Bz{oOT;Tbt0-^Fedw$))>cmlx)X)xY+O-!+xXH z$w%W-raDvsRp37=;9h(>r9R!zUG)B%r!Npr7O_#DW$j_!hOc&ccKrJO`FtM7yT8WA zgW%gxd1NiBi?wM$Q))-M)9zI{BjoTf|CqfAhac;V@)eWKSrH$y3qCWtr4HrPrC!Vj zK4BbWgnUk3B0f>piqGb(m=8Y|Xn#*Vn$RR>V&-8i@K(!A7MB{%<=^=^;ihP1JgeO MP6nM+fj?E?8^;2QmjD0& From e34572ccf6830f8ad53ef13a4f8af2dbadcbe9d0 Mon Sep 17 00:00:00 2001 From: Gimmy Date: Tue, 30 Apr 2024 13:55:17 +0300 Subject: [PATCH 06/11] update docs --- errors.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/errors.go b/errors.go index f4e546b..6fe33cb 100644 --- a/errors.go +++ b/errors.go @@ -309,13 +309,7 @@ func FromGRPCStatus(grpcStatus status.Status) *AsertoError { } /** - * Logger is a function that retrieves the most inner logger associated with an error. - * - * Parameters: - * - err: The error for which to retrieve the logger. - * - * Returns: - * - logger: The logger associated with the error. If the error is nil or no logger is found, nil is returned. + * Retrieves the most inner logger associated with an error. */ func Logger(err error) *zerolog.Logger { var logger *zerolog.Logger From 2468a96a84cf383ef6c7377510706905cf9474ad Mon Sep 17 00:00:00 2001 From: Gimmy Date: Thu, 2 May 2024 09:42:16 +0300 Subject: [PATCH 07/11] add context_errors --- context_errors.go | 28 ++++++++++++++++++ errors.go | 73 ++++++++++++++--------------------------------- errors_test.go | 66 ++++++++++++++++++++++++++++++------------ werr/errors.go | 35 ----------------------- 4 files changed, 97 insertions(+), 105 deletions(-) create mode 100644 context_errors.go delete mode 100644 werr/errors.go diff --git a/context_errors.go b/context_errors.go new file mode 100644 index 0000000..dbfa329 --- /dev/null +++ b/context_errors.go @@ -0,0 +1,28 @@ +package errors + +import ( + "context" + "fmt" +) + +// ContextError represents a standard error +// that can also encapsulate a context. +type ContextError struct { + Err error + Ctx context.Context +} + +func WrapWithContext(err error, ctx context.Context) *ContextError { + return &ContextError{ + Err: err, + Ctx: ctx, + } +} + +func (ce *ContextError) Error() string { + return fmt.Sprintf("%s", ce.Err) +} + +func (ce *ContextError) Unwrap() error { + return ce.Err +} diff --git a/errors.go b/errors.go index 6fe33cb..7c08371 100644 --- a/errors.go +++ b/errors.go @@ -9,7 +9,6 @@ import ( "strings" "time" - "github.com/aserto-dev/errors/werr" "github.com/pkg/errors" "github.com/rs/zerolog" "google.golang.org/genproto/googleapis/rpc/errdetails" @@ -29,7 +28,7 @@ var ( ) func NewAsertoError(code string, statusCode codes.Code, httpCode int, msg string) *AsertoError { - asertoError := &AsertoError{code, statusCode, msg, httpCode, map[string]string{}, nil, nil} + asertoError := &AsertoError{code, statusCode, msg, httpCode, map[string]string{}, nil} asertoErrors[code] = asertoError return asertoError } @@ -43,14 +42,6 @@ type AsertoError struct { HTTPCode int data map[string]string errs []error - Ctx context.Context -} - -// Associates a context with the AsertoError. -func (e *AsertoError) WithContext(ctx context.Context) *AsertoError { - c := e.Copy() - c.Ctx = ctx - return c } func (e *AsertoError) Data() map[string]string { @@ -82,7 +73,6 @@ func (e *AsertoError) Copy() *AsertoError { data: dataCopy, errs: e.errs, HTTPCode: e.HTTPCode, - Ctx: e.Ctx, } } @@ -283,6 +273,10 @@ func (e *AsertoError) WithHTTPStatus(httpStatus int) *AsertoError { return c } +func (e *AsertoError) Ctx(ctx context.Context) error { + return WrapWithContext(e, ctx) +} + // Returns an Aserto error based on a given grpcStatus. The details that are not of type errdetails.ErrorInfo are dropped. // and if there are details from multiple errors, the aserto error will be constructed based on the first one. func FromGRPCStatus(grpcStatus status.Status) *AsertoError { @@ -319,19 +313,11 @@ func Logger(err error) *zerolog.Logger { } for { - wErr, ok := err.(*werr.WrappedError) - if ok { - aErr, aOk := wErr.Err.(*AsertoError) - if aOk { - setLogger(aErr.Ctx, &logger) + if ce, ok := err.(*ContextError); ok { + newLogger := extractLogger(ce.Ctx) + if newLogger != nil { + logger = newLogger } - setLogger(wErr.Ctx, &logger) - } - - aErr, ok := err.(*AsertoError) - if ok { - setLogger(aErr.Ctx, &logger) - } err = errors.Unwrap(err) @@ -343,27 +329,6 @@ func Logger(err error) *zerolog.Logger { return logger } -/** - * setLogger sets the logger pointer to the logger stored in the provided context. - * If the context is nil or the logger in the context is nil, the logger pointer remains unchanged. - * If the logger in the context is the default context logger or has a disabled level, the logger pointer remains unchanged. - * - * @param ctx The context from which to retrieve the logger. - * @param logger The pointer to the logger to be set. - */ -func setLogger(ctx context.Context, logger **zerolog.Logger) { - if ctx == nil { - return - } - - newLogger := zerolog.Ctx(ctx) - if newLogger == nil || newLogger == zerolog.DefaultContextLogger || newLogger.GetLevel() == zerolog.Disabled { - return - } - - *logger = newLogger -} - func UnwrapAsertoError(err error) *AsertoError { if err == nil { return nil @@ -376,14 +341,6 @@ func UnwrapAsertoError(err error) *AsertoError { // try to process Aserto error. for { - wErr, ok := err.(*werr.WrappedError) - if ok { - aErr, aOk := wErr.Err.(*AsertoError) - if aOk { - return aErr - } - } - aErr, ok := err.(*AsertoError) if ok { return aErr @@ -426,3 +383,15 @@ func Equals(err1, err2 error) bool { func CodeToAsertoError(code string) *AsertoError { return asertoErrors[code] } + +func extractLogger(ctx context.Context) *zerolog.Logger { + if ctx == nil { + return nil + } + logger := zerolog.Ctx(ctx) + if logger == nil || logger == zerolog.DefaultContextLogger || logger.GetLevel() == zerolog.Disabled { + logger = nil + } + + return logger +} diff --git a/errors_test.go b/errors_test.go index 577e936..574ce79 100644 --- a/errors_test.go +++ b/errors_test.go @@ -10,7 +10,6 @@ import ( "github.com/rs/zerolog" cerr "github.com/aserto-dev/errors" - "github.com/aserto-dev/errors/werr" "github.com/stretchr/testify/require" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" @@ -218,30 +217,44 @@ func TestLoggerWithWrappedNilError(t *testing.T) { var err error ctx := context.Background() - logger := cerr.Logger(werr.Wrap(err, ctx)) + logger := cerr.Logger(cerr.WrapWithContext(err, ctx)) assert.Nil(logger) } -func TestLoggerWithWrappedErrorsWithContext(t *testing.T) { +func TestLoggerWithWrappedErrorsWithEmptyContext(t *testing.T) { assert := require.New(t) ctx := context.Background() - err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx) + err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) wrappedErr := errors.Wrap(err, "wrapped error") logger := cerr.Logger(wrappedErr) assert.Nil(logger) } +func TestLoggerWithWrappedErrorsWithLoggerContext(t *testing.T) { + assert := require.New(t) + initialLogger := zerolog.New(os.Stderr) + + ctx := context.Background() + ctx = initialLogger.WithContext(ctx) + err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) + wrappedErr := errors.Wrap(err, "wrapped error") + + logger := cerr.Logger(wrappedErr) + assert.NotNil(logger) + assert.Equal(logger, zerolog.Ctx(ctx)) +} + func TestLoggerWithWrappedMultipleWithoutErrorsWithContext(t *testing.T) { assert := require.New(t) initialLogger := zerolog.New(os.Stderr) - ctx1 := context.Background() - ctx := initialLogger.WithContext(ctx1) - err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx) + ctx := context.Background() + ctx = initialLogger.WithContext(ctx) + err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) errWithoutCtx := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error") - wrappedErr := errors.Wrap(errWithoutCtx.Err(err), "wrapped error") + wrappedErr := errWithoutCtx.Err(errors.Wrap(err, "wrapped error")) logger := cerr.Logger(wrappedErr) assert.NotNil(logger) @@ -252,11 +265,11 @@ func TestLoggerWithWrappedMultipleErrorsWithContext(t *testing.T) { assert := require.New(t) initialLogger := zerolog.New(os.Stderr) - ctx1 := context.Background() - ctx := initialLogger.WithContext(ctx1) - err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx) + ctx := context.Background() + ctx = initialLogger.WithContext(ctx) + err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) errWithoutCtx := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error") - wrappedErr := errors.Wrap(err.Err(errWithoutCtx), "wrapped error") + wrappedErr := errors.Wrap(errWithoutCtx.Err(err), "wrapped error") logger := cerr.Logger(wrappedErr) assert.NotNil(logger) @@ -268,9 +281,8 @@ func TestLoggerWithWrappedMultipleErrorsWithMultipleContexts(t *testing.T) { initialLogger := zerolog.New(os.Stderr) ctx1 := context.Background() ctx2 := initialLogger.WithContext(ctx1) - err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx1) - err2 := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx2) - wrappedErr := errors.Wrap(err.Err(err2), "wrapped error") + err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx1) + wrappedErr := cerr.WrapWithContext(cerr.WrapWithContext(err, ctx2), ctx1) logger := cerr.Logger(wrappedErr) ctx1Logger := zerolog.Ctx(ctx1) @@ -286,9 +298,27 @@ func TestLoggerWithWrappedMultipleErrorsWithMultipleContextsOuter(t *testing.T) initialLogger := zerolog.New(os.Stderr) ctx1 := context.Background() ctx2 := initialLogger.WithContext(ctx1) - err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx1) - err2 := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error").WithContext(ctx2) - wrappedErr := errors.Wrap(err2.Err(err), "wrapped error") + err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx1) + err2 := cerr.WrapWithContext(cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error"), ctx2) + wrappedErr := errors.Wrap(errors.Wrap(err2, err.Error()), "wrapped error") + + logger := cerr.Logger(wrappedErr) + ctx1Logger := zerolog.Ctx(ctx1) + ctx2Logger := zerolog.Ctx(ctx2) + + assert.NotNil(logger) + assert.NotEqual(logger, ctx1Logger) + assert.Equal(logger, ctx2Logger) +} + +func TestLoggerWithWrappedMultipleAsertoErrorsWithMultipleContextsOuter(t *testing.T) { + assert := require.New(t) + initialLogger := zerolog.New(os.Stderr) + ctx1 := context.Background() + ctx2 := initialLogger.WithContext(ctx1) + err := cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error").Ctx(ctx1) + err2 := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error").Ctx(ctx2) + wrappedErr := errors.Wrap(errors.Wrap(err2, err.Error()), "wrapped error") logger := cerr.Logger(wrappedErr) ctx1Logger := zerolog.Ctx(ctx1) diff --git a/werr/errors.go b/werr/errors.go deleted file mode 100644 index 1890733..0000000 --- a/werr/errors.go +++ /dev/null @@ -1,35 +0,0 @@ -package werr - -import ( - "context" - "fmt" -) - -// WrappedError represents a standard error -// that can also encapsulate a context. -type WrappedError struct { - Ctx context.Context - Err error -} - -func (w *WrappedError) Error() string { - return fmt.Sprintf("%s", w.Err) -} - -func Wrap(err error, ctx context.Context) *WrappedError { - return &WrappedError{ - Ctx: ctx, - Err: err, - } -} - -func (w *WrappedError) WithContext(ctx context.Context) *WrappedError { - return &WrappedError{ - Ctx: ctx, - Err: w.Err, - } -} - -func (w *WrappedError) Unwrap() error { - return w.Err -} From 5452898119343ddc51e948875e871ae4f4caa186 Mon Sep 17 00:00:00 2001 From: Gimmy Date: Tue, 7 May 2024 16:27:21 +0300 Subject: [PATCH 08/11] update .gitingore --- .gitignore | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6abea10..d89d9c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ /cover.out /tenant.db -/.ext \ No newline at end of file +/.ext + +# https://github.com/golang/go/issues/53502 +# go.work.sum is machine specific and should not be checked in +# go.work.sum + +.DS_Store From 94705ea13aa754b7992be09fa744d6e236dc3d73 Mon Sep 17 00:00:00 2001 From: Gimmy Date: Tue, 7 May 2024 16:27:39 +0300 Subject: [PATCH 09/11] update Error() and add Cause() --- context_errors.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/context_errors.go b/context_errors.go index dbfa329..c3443fb 100644 --- a/context_errors.go +++ b/context_errors.go @@ -2,7 +2,6 @@ package errors import ( "context" - "fmt" ) // ContextError represents a standard error @@ -20,7 +19,11 @@ func WrapWithContext(err error, ctx context.Context) *ContextError { } func (ce *ContextError) Error() string { - return fmt.Sprintf("%s", ce.Err) + return ce.Err.Error() +} + +func (ce *ContextError) Cause() error { + return ce.Err } func (ce *ContextError) Unwrap() error { From e05744d481cfc4b8fa6d378fe0d52756593296b7 Mon Sep 17 00:00:00 2001 From: Gimmy Date: Tue, 7 May 2024 16:33:57 +0300 Subject: [PATCH 10/11] add WrapContext and WrapfContext --- context_errors.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/context_errors.go b/context_errors.go index c3443fb..5f900e8 100644 --- a/context_errors.go +++ b/context_errors.go @@ -2,6 +2,8 @@ package errors import ( "context" + + "github.com/pkg/errors" ) // ContextError represents a standard error @@ -18,6 +20,14 @@ func WrapWithContext(err error, ctx context.Context) *ContextError { } } +func WrapConext(err error, ctx context.Context, message string) *ContextError { + return WrapWithContext(errors.Wrap(err, message), ctx) +} + +func WrapfConext(err error, ctx context.Context, format string, args ...interface{}) *ContextError { + return WrapWithContext(errors.Wrapf(err, format, args...), ctx) +} + func (ce *ContextError) Error() string { return ce.Err.Error() } From 769fd0c0390bec18994230e646373b3b94d5f04b Mon Sep 17 00:00:00 2001 From: Gimmy Date: Wed, 15 May 2024 11:00:27 +0300 Subject: [PATCH 11/11] fix typos, address feedback --- context_errors.go => context.go | 12 ++++++------ errors.go | 18 ++++++++++-------- errors_test.go | 20 +++++++++----------- 3 files changed, 25 insertions(+), 25 deletions(-) rename context_errors.go => context.go (52%) diff --git a/context_errors.go b/context.go similarity index 52% rename from context_errors.go rename to context.go index 5f900e8..58a3543 100644 --- a/context_errors.go +++ b/context.go @@ -13,19 +13,19 @@ type ContextError struct { Ctx context.Context } -func WrapWithContext(err error, ctx context.Context) *ContextError { +func WithContext(err error, ctx context.Context) *ContextError { return &ContextError{ Err: err, Ctx: ctx, } } -func WrapConext(err error, ctx context.Context, message string) *ContextError { - return WrapWithContext(errors.Wrap(err, message), ctx) +func WrapContext(err error, ctx context.Context, message string) *ContextError { + return WithContext(errors.Wrap(err, message), ctx) } -func WrapfConext(err error, ctx context.Context, format string, args ...interface{}) *ContextError { - return WrapWithContext(errors.Wrapf(err, format, args...), ctx) +func WrapfContext(err error, ctx context.Context, format string, args ...interface{}) *ContextError { + return WithContext(errors.Wrapf(err, format, args...), ctx) } func (ce *ContextError) Error() string { @@ -33,7 +33,7 @@ func (ce *ContextError) Error() string { } func (ce *ContextError) Cause() error { - return ce.Err + return errors.Cause(ce.Unwrap()) } func (ce *ContextError) Unwrap() error { diff --git a/errors.go b/errors.go index 7c08371..0a42614 100644 --- a/errors.go +++ b/errors.go @@ -205,7 +205,6 @@ func (e *AsertoError) Time(key string, value time.Time) *AsertoError { func (e *AsertoError) FromReader(key string, value io.Reader) *AsertoError { buf := &strings.Builder{} _, err := io.Copy(buf, value) - if err != nil { return e.Err(err) } @@ -253,7 +252,6 @@ func (e *AsertoError) GRPCStatus() *status.Status { Metadata: e.Data(), Domain: e.Code, }) - if err != nil { return status.New(codes.Internal, "internal failure setting up error details, please contact the administrator") } @@ -274,7 +272,7 @@ func (e *AsertoError) WithHTTPStatus(httpStatus int) *AsertoError { } func (e *AsertoError) Ctx(ctx context.Context) error { - return WrapWithContext(e, ctx) + return WithContext(e, ctx) } // Returns an Aserto error based on a given grpcStatus. The details that are not of type errdetails.ErrorInfo are dropped. @@ -307,16 +305,16 @@ func FromGRPCStatus(grpcStatus status.Status) *AsertoError { */ func Logger(err error) *zerolog.Logger { var logger *zerolog.Logger + var ce *ContextError if err == nil { return logger } for { - if ce, ok := err.(*ContextError); ok { - newLogger := extractLogger(ce.Ctx) - if newLogger != nil { - logger = newLogger + if errors.As(err, &ce) { + if ctxLogger := extractLogger(ce.Ctx); ctxLogger != nil { + logger = ctxLogger } } @@ -384,12 +382,16 @@ func CodeToAsertoError(code string) *AsertoError { return asertoErrors[code] } +/** + * Retrieve the logger associated with the context using zerolog.Ctx(ctx). + * If the retrieved logger is either the default context logger or has a disabled level, it returns nil. + */ func extractLogger(ctx context.Context) *zerolog.Logger { if ctx == nil { return nil } logger := zerolog.Ctx(ctx) - if logger == nil || logger == zerolog.DefaultContextLogger || logger.GetLevel() == zerolog.Disabled { + if logger == zerolog.DefaultContextLogger || logger.GetLevel() == zerolog.Disabled { logger = nil } diff --git a/errors_test.go b/errors_test.go index 574ce79..a9abd87 100644 --- a/errors_test.go +++ b/errors_test.go @@ -101,7 +101,6 @@ func TestFromGRPCStatus(t *testing.T) { Metadata: initialErr.Data(), Domain: initialErr.Code, }) - if err != nil { assert.Fail(err.Error()) } @@ -129,7 +128,6 @@ func TestEquals(t *testing.T) { err2 := ErrAlreadyExists.Msgf("error 2").Str("key2", "val2").Err(errors.New("zoom")) assert.True(cerr.Equals(err1, err2)) - } func TestEqualsNil(t *testing.T) { @@ -217,7 +215,7 @@ func TestLoggerWithWrappedNilError(t *testing.T) { var err error ctx := context.Background() - logger := cerr.Logger(cerr.WrapWithContext(err, ctx)) + logger := cerr.Logger(cerr.WithContext(err, ctx)) assert.Nil(logger) } @@ -225,7 +223,7 @@ func TestLoggerWithWrappedErrorsWithEmptyContext(t *testing.T) { assert := require.New(t) ctx := context.Background() - err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) + err := cerr.WithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) wrappedErr := errors.Wrap(err, "wrapped error") logger := cerr.Logger(wrappedErr) @@ -238,7 +236,7 @@ func TestLoggerWithWrappedErrorsWithLoggerContext(t *testing.T) { ctx := context.Background() ctx = initialLogger.WithContext(ctx) - err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) + err := cerr.WithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) wrappedErr := errors.Wrap(err, "wrapped error") logger := cerr.Logger(wrappedErr) @@ -252,7 +250,7 @@ func TestLoggerWithWrappedMultipleWithoutErrorsWithContext(t *testing.T) { ctx := context.Background() ctx = initialLogger.WithContext(ctx) - err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) + err := cerr.WithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) errWithoutCtx := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error") wrappedErr := errWithoutCtx.Err(errors.Wrap(err, "wrapped error")) @@ -267,7 +265,7 @@ func TestLoggerWithWrappedMultipleErrorsWithContext(t *testing.T) { ctx := context.Background() ctx = initialLogger.WithContext(ctx) - err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) + err := cerr.WithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx) errWithoutCtx := cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error") wrappedErr := errors.Wrap(errWithoutCtx.Err(err), "wrapped error") @@ -281,8 +279,8 @@ func TestLoggerWithWrappedMultipleErrorsWithMultipleContexts(t *testing.T) { initialLogger := zerolog.New(os.Stderr) ctx1 := context.Background() ctx2 := initialLogger.WithContext(ctx1) - err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx1) - wrappedErr := cerr.WrapWithContext(cerr.WrapWithContext(err, ctx2), ctx1) + err := cerr.WithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx1) + wrappedErr := cerr.WithContext(cerr.WithContext(err, ctx2), ctx1) logger := cerr.Logger(wrappedErr) ctx1Logger := zerolog.Ctx(ctx1) @@ -298,8 +296,8 @@ func TestLoggerWithWrappedMultipleErrorsWithMultipleContextsOuter(t *testing.T) initialLogger := zerolog.New(os.Stderr) ctx1 := context.Background() ctx2 := initialLogger.WithContext(ctx1) - err := cerr.WrapWithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx1) - err2 := cerr.WrapWithContext(cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error"), ctx2) + err := cerr.WithContext(cerr.NewAsertoError("E00001", codes.Internal, http.StatusInternalServerError, "internal error"), ctx1) + err2 := cerr.WithContext(cerr.NewAsertoError("E00002", codes.Internal, http.StatusInternalServerError, "internal error"), ctx2) wrappedErr := errors.Wrap(errors.Wrap(err2, err.Error()), "wrapped error") logger := cerr.Logger(wrappedErr)