Skip to content

Commit

Permalink
implements suggested solution
Browse files Browse the repository at this point in the history
  • Loading branch information
ccoVeille committed Dec 19, 2024
1 parent 856a340 commit 2c3c779
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 13 deletions.
6 changes: 6 additions & 0 deletions docs/advanced-guide/gofr-errors/page.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ dbErr2 := datasource.ErrorDB{Message : "database connection timed out!"}
GoFr's error structs implements an interface with `Error() string` and `StatusCode() int` methods, users can override the
status code by implementing it for their custom error.

You can optionally define a log level for your error with the `LogLevel() logging.Level` methods

#### Usage:
```go
type customError struct {
Expand All @@ -57,4 +59,8 @@ func (c customError) Error() string {
func (c customError) StatusCode() int {
return http.StatusMethodNotAllowed
}

func (c customError) LogLevel() logging.Level {
return logging.WARN
}
```
30 changes: 19 additions & 11 deletions pkg/gofr/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,22 +145,30 @@ func panicRecoveryHandler(re any, log logging.Logger, panicked chan struct{}) {
})
}

//nolint:nestif // Log the error(if any) with traceID and errorMessage.
// Log the error(if any) with traceID and errorMessage.
func (h handler) logError(traceID string, err error) {
if err != nil {
errorLog := &ErrorLogEntry{TraceID: traceID, Error: err.Error()}

if errors.As(err, &gofrHTTP.ErrorEntityAlreadyExist{}) {
h.container.Logger.Info(errorLog)
} else if errors.As(err, &gofrHTTP.ErrorEntityNotFound{}) {
h.container.Logger.Info(errorLog)
} else if errors.As(err, &gofrHTTP.ErrorInvalidParam{}) {
h.container.Logger.Info(errorLog)
} else if errors.As(err, &gofrHTTP.ErrorMissingParam{}) {
h.container.Logger.Info(errorLog)
} else {
h.container.Logger.Error(errorLog)
// define the default log level for error
loggerHelper := h.container.Logger.Error

switch logging.GetLogLevelForError(err) {
case logging.ERROR:
// we use the default log level for error
case logging.INFO:
loggerHelper = h.container.Logger.Info
case logging.NOTICE:
loggerHelper = h.container.Logger.Notice
case logging.DEBUG:
loggerHelper = h.container.Logger.Debug
case logging.WARN:
loggerHelper = h.container.Logger.Warn
case logging.FATAL:
loggerHelper = h.container.Logger.Fatal
}

loggerHelper(errorLog)
}
}

Expand Down
52 changes: 50 additions & 2 deletions pkg/gofr/http/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"net/http"
"strings"

"gofr.dev/pkg/gofr/logging"
)

const alreadyExistsMessage = "entity already exists"
Expand All @@ -24,10 +26,13 @@ func (ErrorEntityNotFound) StatusCode() int {
return http.StatusNotFound
}

// ErrorEntityAlreadyExist represents an error for when entity is already present in the storage and we are trying to make duplicate entry.
type ErrorEntityAlreadyExist struct {
func (ErrorEntityNotFound) LogLevel() logging.Level {
return logging.INFO
}

// ErrorEntityAlreadyExist represents an error for when entity is already present in the storage and we are trying to make duplicate entry.
type ErrorEntityAlreadyExist struct{}

func (ErrorEntityAlreadyExist) Error() string {
return alreadyExistsMessage
}
Expand All @@ -36,6 +41,10 @@ func (ErrorEntityAlreadyExist) StatusCode() int {
return http.StatusConflict
}

func (ErrorEntityAlreadyExist) LogLevel() logging.Level {
return logging.WARN
}

// ErrorInvalidParam represents an error for invalid parameter values.
type ErrorInvalidParam struct {
Params []string `json:"param,omitempty"` // Params contains the list of invalid parameter names.
Expand All @@ -49,6 +58,10 @@ func (ErrorInvalidParam) StatusCode() int {
return http.StatusBadRequest
}

func (ErrorInvalidParam) LogLevel() logging.Level {
return logging.INFO
}

// ErrorMissingParam represents an error for missing parameters in a request.
type ErrorMissingParam struct {
Params []string `json:"param,omitempty"`
Expand All @@ -58,6 +71,10 @@ func (e ErrorMissingParam) Error() string {
return fmt.Sprintf("'%d' missing parameter(s): %s", len(e.Params), strings.Join(e.Params, ", "))
}

func (ErrorMissingParam) LogLevel() logging.Level {
return logging.INFO
}

func (ErrorMissingParam) StatusCode() int {
return http.StatusBadRequest
}
Expand All @@ -69,6 +86,10 @@ func (ErrorInvalidRoute) Error() string {
return "route not registered"
}

func (ErrorInvalidRoute) LogLevel() logging.Level {
return logging.INFO
}

func (ErrorInvalidRoute) StatusCode() int {
return http.StatusNotFound
}
Expand All @@ -84,6 +105,10 @@ func (ErrorRequestTimeout) StatusCode() int {
return http.StatusRequestTimeout
}

func (ErrorRequestTimeout) LogLevel() logging.Level {
return logging.INFO
}

// ErrorPanicRecovery represents an error for request which panicked.
type ErrorPanicRecovery struct{}

Expand All @@ -94,3 +119,26 @@ func (ErrorPanicRecovery) Error() string {
func (ErrorPanicRecovery) StatusCode() int {
return http.StatusInternalServerError
}

func (ErrorPanicRecovery) LogLevel() logging.Level {
return logging.ERROR
}

// validate the errors satisfy the underlying interfaces they depend on.
var (
_ statusCodeResponder = ErrorEntityNotFound{}
_ statusCodeResponder = ErrorEntityAlreadyExist{}
_ statusCodeResponder = ErrorInvalidParam{}
_ statusCodeResponder = ErrorMissingParam{}
_ statusCodeResponder = ErrorInvalidRoute{}
_ statusCodeResponder = ErrorRequestTimeout{}
_ statusCodeResponder = ErrorPanicRecovery{}

_ logging.LogLevelResponder = ErrorEntityNotFound{}
_ logging.LogLevelResponder = ErrorEntityAlreadyExist{}
_ logging.LogLevelResponder = ErrorInvalidParam{}
_ logging.LogLevelResponder = ErrorMissingParam{}
_ logging.LogLevelResponder = ErrorInvalidRoute{}
_ logging.LogLevelResponder = ErrorRequestTimeout{}
_ logging.LogLevelResponder = ErrorPanicRecovery{}
)
18 changes: 18 additions & 0 deletions pkg/gofr/logging/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,21 @@ func checkIfTerminal(w io.Writer) bool {
func (l *logger) ChangeLevel(level Level) {
l.level = level
}

// LogLevelResponder is an interface that provides a method to get the log level.
type LogLevelResponder interface {
LogLevel() Level
}

// GetLogLevelForError returns the log level for the given error.
// If the error implements [logLevelResponder], its log level is returned.
// Otherwise, the default log level "error" is returned.
func GetLogLevelForError(err error) Level {
level := ERROR

if e, ok := err.(LogLevelResponder); ok {
level = e.LogLevel()
}

return level
}

0 comments on commit 2c3c779

Please sign in to comment.