Skip to content

Lucasvmarangoni/logella

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Logella

A simple loggers and errors library.



Logger Package

This package defines the log settings (zerolog). Allows you to use default or customized colors configuration.

Import

import "github.com/Lucasvmarangoni/logella/config/log"

ConfigDefault: It has pre-defined log color settings, meaning there is no need to specify a any parameter.

logger.ConfigDefault(out io.Writer)

Example:

logger.ConfigDefault(os.Stdout)

ConfigCustom: Allows you to customize log level, message, and operation colors.

ConfigCustom(info, err, warn, debug, fatal, message, trace colors, , out io.Writer)

The parameters must be passed using the variables already defined by the package with the name of the colors.

Colors: Black, Red, Green, Yellow, Blue, Magenta, Cyan or White.

Example:

logger.ConfigCustom(logger.Green, logger.Red, logger.Yellow, logger.Cyan, logger.Red, logger.Magenta, logger.Blue, os.Stdout)

Use Case

log.Info().Str("context", "TableRepository").Msg("Database - Created users table successfully.")

Output:

alt text


Errs Package

The Errs package is a custom error handling library. Its primary feature is to attach Traceual information to errors, allowing them to be propagated up the call stack.

It also provides standardized error types, such as invalid and required.

Output Example:

log.Error().Err(errs.Trace(err).Stack()).Msg("example error")
log.Error().Err(errs.Unwrap(err).Stack()).Msg("example error")
log.Error().Err(errs.Unwrap(err).Stack()).Msg("example error")

alt text

➤ It is possible click on the path value (test/test.go:15) to go directly to file and line.

Import

import "github.com/Lucasvmarangoni/logella/err"

Use

func main() {
	_, err := handler()
	log.Error().Err(errs.Unwrap(err).Stack()).Msg(fmt.Sprint(errs.Unwrap(err).Code)) 
	log.Error().Err(errs.Trace(err).ToClient()).Msg(fmt.Sprint(errs.Unwrap(err).Code))
}

func handler() (string, error) {
	_, err := service()
	return "", errs.Trace(err)
}

func service() (string, error) {
	err := repository()
	if err != nil {
		return "", errs.Trace(err)
	}
	return "", nil
}

func repository() error {
	return errs.Wrap(errors.New("test error"), 500)
}

Error: the Error Struct.

Wrap: Used to add the new error and the trace that throw the exception.

Trace: Used to add the trace to stack.

Trace (method): Used to add the trace manuelly to stack.

Stack: Stack returns the error along with the operations stack. Used in internals Logs.

ToClient: Used to send error message to client.

Msg: Used to add a message to error.

Unwrap: It makes the type assertion and is used to access de Error Struct whitout performing other functionality.

GetHTTPStatusFromPgError: Used to determine HTTP status automatically based on database error message.

New: Used to add the trace to stack without http status code. Recommended for incializations exceptions.

FailOnErrLog: Used to throw a fatal log in a simple way and with trace stack.

PanicErr: Used to throw a panic based on a error value, whitout the needs to manualy use an if conditional.

PanicBool: Used to throw a panic based on a boolean value, whitout the needs to manualy use an if conditional.

IsRequiredError: Used to simplify create a "is requered" error with fmt.Errorf.

IsInvalidError: Used to simplify create a "is invalid" error with fmt.Errorf.

Error

type Error struct {
	Cause   error   // The actual error thrown
	Code    int     // HTTP Status Code
	Message string  // Custom message
	trace error
}

Wrap

func Wrap(cause error, code int) error

Example:

errs.Wrap(err, http.StatusInternalServerError)

Use case:

cfg.Db, err = pgx.ParseConfig(url)
if err != nil {
    return nil, errs.Wrap(err, http.StatusInternalServerError)
}

Trace

func Trace(err error) *Error

Example:

errs.Trace(err)

Use Case:

func service() (string, error) {
	err := repository()
	if err != nil {
		return "", errs.Trace(err)
	}
	return "", nil
}

func repository() error {
	return errs.Wrap(errors.New("test error"), 500)
}

Recommendation: When an error is issued in a situation that does not involve a call, such as in a conditional comparison, it is recommended that you encapsulate this inside a function, as follows:

func checkStatusCode(resp *http.Response) error {
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("status code is not OK. got %d (%s), want %d (%s)",
			resp.StatusCode, http.StatusText(resp.StatusCode),
			http.StatusOK, http.StatusText(http.StatusOK))
	}
	return nil
}

if err := checkStatusCode(resp); err != nil {
		return errs.Trace(err)
	}

Or use Trace Method, like above:

Trace-method

This method can be used in different situations when you needed to inject your especific trace manually.

  • When an error is throw inside an anonymous function;
  • When an error is throw from an external library method;
  • Whem an error is issued in a situation that does not involve a call, such as in a conditional comparison.
(e *Error) Trace(trace string) *Error

Example:

errs.Wrap(err, http.StatusBadRequest).Trace("AnonymousFunction")

Use Case:

func ExternalMethod() error {
	return errors.New("test error")
}

func Repository() error {
err := ExternalMethod()
	if err != nil {
		return errs.Wrap(err, http.StatusBadRequest).Trace("ExternalMethod")
	}
	return nil
}
// path main.go:105 trace ExternalMethod:repository ➤ service ➤ handler ➤ main"

Stack

func (e *Error) Stack() error 

Example:

log.Error().Err(errs.Trace(err).Stack())

Use Case:

authdata, err := u.userService.VerifyTOTP(id, totpToken.Token)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(errs.Unwrap(err).Code)
		json.NewEncoder(w).Encode(map[string]string{
			"status":     http.StatusText(errs.Unwrap(err).Code),
			"message":    fmt.Sprintf("%v", errs.Unwrap(err).ToClient()),
			"request_id": requestID,
		})
		log.Error().Err(errs.Trace(err).Stack()).Msgf("error validate totp. | (%s)", requestID)
		return
	}

ToClient

Check the Code of the error to if it is 500 return "Internal Server Error" instead of Cause (actual error)

func (e *Error) ToClient() error  

Example:

errs.Unwrap(err).ToClient()
	log.Error().Err(errs.Unwrap(err).ToClient()).Msg(fmt.Sprint(errs.Unwrap(err).Code)) 
	// OUTPUT: 2025-02-20T18:29:26-03:00 ERROR ⇝ 500 error"Internal Server Error"

Use Case:

authdata, err := u.userService.VerifyTOTP(id, totpToken.Token)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(errs.Unwrap(err).Code)
		json.NewEncoder(w).Encode(map[string]string{
			"status":     http.StatusText(errs.Unwrap(err).Code),
			"message":    fmt.Sprintf("%v", errs.Unwrap(err).ToClient()),
			"request_id": requestID,
		})
		log.Error().Err(errs.Trace(err).Stack()).Msgf("error validate totp. | (%s)", requestID)
		return
	}

Msg

func (e *Error) Msg(message string) 

Example:

errs.Trace(err).Msg("Message")
message := errs.Trace(err).Message

Unwrap

Used when you should not add a trace to the error. Otherwise a duplicate trace will be added.

func Unwrap(err error) *Error 

Example:

errs.Unwrap(err).Msg("Message")
message := errs.Unwrap(err).Message
code := errs.Unwrap(err).Code
cause := errs.Unwrap(err).Cause

Use Case

log.Error().Err(errs.Trace(err).Stack()).Msg(fmt.Sprint(errs.Unwrap(err).Code))
authdata, err := u.userService.VerifyTOTP(id, totpToken.Token)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(errs.Unwrap(err).Code)
		json.NewEncoder(w).Encode(map[string]string{
			"status":     http.StatusText(errs.Unwrap(err).Code),
			"message":    fmt.Sprintf("%v", errs.Unwrap(err).ToClient()),
			"request_id": requestID,
		})
		log.Error().Err(errs.Trace(err).Stack()).Msgf("%s. | (%s)", errs.Unwrap(err).Message, requestID)
		return
	}

GetHTTPStatusFromPgError

Compatible with PGX V5 library

A function to determine HTTP status automatically based on database error message.

func GetHTTPStatusFromPgError(err error) int

Example:

return errs.Wrap(err, "row.Scan", errs.GetHTTPStatusFromPgError(err))

Use Case:

func (r *UserRepositoryDb) UpdateOTP(user *entities.User, ctx Trace.Trace) error {
	sql := `UPDATE users SET otp_auth_url = encrypt($2::BYTES, $4::BYTES, 'aes'), otp_secret = encrypt($3::BYTES, $4::BYTES, 'aes') WHERE id = $1`
	err := crdbpgx.ExecuteTx(ctx, r.conn, pgx.TxOptions{}, func(tx pgx.Tx) error {
		_, err := tx.Exec(ctx, sql,
			user.ID,
			user.OtpAuthUrl,
			user.OtpSecret,
			r.key,
		)
		if err != nil {
			return errs.Wrap(err, errs.GetHTTPStatusFromPgError(err))
		}
		return nil
	})
	if err != nil {
		return errs.Trace(err)
	}
	return nil
}

New

New(cause error) *Error

Example:

err := errs.New(errors.New("error"))

Use Case:

func (r *TableRepositoryDb) initUserTable(ctx context.Context) error {
	_, err := r.tx.Exec(ctx, `CREATE TABLE IF NOT EXISTS users (
			id UUID PRIMARY KEY NOT NULL,
			name VARCHAR(15) NOT NULL,
			last_name BYTEA NOT NULL,				
			email BYTEA UNIQUE NOT NULL,				
			password TEXT NOT NULL,		
			created_at TIMESTAMP NOT NULL			
		)`)
	if err != nil {
		return errs.New(err)
	}
	log.Info().Str("context", "TableRepository").Msg("Database - Created users table successfully.")
	return nil
}

FailOnErrLog

FailOnErrLog(err error, msg string)

Example:

errs.FailOnErrLog(err, "database failled")

Use Case:

if err = Database(); err != nil {
	errs.FailOnErrLog(err, "database failled")
}

PanicErr

PanicErr(err error, msg string)

PanicBool

PanicBool(boolean bool, msg string)

Standard Errors

The package provides standardized errors, such as IsInvalidError and IsRequiredError. Here's an example of how to use IsInvalidError:

errs.IsInvalidError("Customer", "Must be google uuid")
  • IsInvalidError(fieldName, msg string) error
  • IsRequiredError(fieldName, msg string) error
  • FailOnErrLog(err error, msg string)
  • PanicErr(err error, ctx string)
  • PanicBool(boolean bool, msg string)

Router Package

The Router is a logging package for initializing routes using go-chi.

Alt text

Import

import "github.com/Lucasvmarangoni/logella/router"

Use

router := router.NewRouter()
r := router.Chi
userRouter := routers.NewUsersRouter(userHandler, router)

r.Route("/", func() {
		r.Use(jwtauth.Verifier(tokenAuth))
		r.Use(jwtauth.Authenticator)
		userRouter.UserRoutes(r)	
})
router.Route("/authn", func() {
	router.Post("/login", u.userHandler.Authentication)
	router.Group(func(){
		router.Use(httprate.Limit(
			4,
			60*time.Minute,
			httprate.WithKeyFuncs(httprate.KeyByRealIP, httprate.KeyByEndpoint),
			httprate.WithLimitHandler(func(w http.ResponseWriter, r *http.Request) {
				http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
			}),
		))	
		router.Post("/login", u.userHandler.Authentication)
	})
})

Router: The Router Struct.

NewRouter: To create a new instance of the Router, and chi.Router instance (chi.NewRouter()).

Methods: All http Methods. Used in the same way as in go-chi lib.

Route: Reclaces go-chi's route method.

Router

type Router struct {
	Chi    chi.Router
	mux    *chi.Mux
	Prefix string
}

Instance Creation

func NewRouter() *Router {
	r := &Router{
		Chi: chi.NewRouter(),
	}
	return r
}
router := router.NewRouter()

Methods

(r *Router) Group(fn func()) *Router
(r *Router) Use(ms ...func(http.Handler) http.Handler) *Router
(r *Router) Post(pattern string, handler http.HandlerFunc)
(r *Router) Get(pattern string, handler http.HandlerFunc)
(r *Router) Put(pattern string, handler http.HandlerFunc)
(r *Router) Path(pattern string, handler http.HandlerFunc)
(r *Router) Delete(pattern string, handler http.HandlerFunc)

Route

(r *Router) Route(pattern string, fn func(sub *Router))

Response Package

Handles HTTP error responses with a cleaner syntax.

  • Depends on the errs package from the logella library.

Attention: The errs.Error struct has a Message field, but in the response package, this field is only included in logs, not in the response body sent to the client. To include a custom message in the response body, use the Msg(msg string) method provided by the response.Response struct.

New: Creates a new Response instance from an error. Wrap your error with a status code using errs.Wrap.

Log: Sets a log message to be included in the application log. Should come after Req and User, if present.

Msg: Sets a custom message to be included in the response body.

Req: Adds the request ID to the response.>

User: Adds the user ID to the response.

Date: Sets a fixed timestamp for the response. Useful for tests or consistent logging.

Doc: Adds a documentation string (e.g., a URL or identifier) to the response.

Send: Finalizes the chain. Writes the JSON response and status code to the http.ResponseWriter.

Use Case

  • The err parameter must be of type errs.Error from the logella errs package (e.g., created with errs.Wrap).

From external error

response.New(errs.Wrap(errors.New("some error"), http.StatusBadRequest)).
		Msg("error msg method").
		Req("123").
		User("12345").
		Log("LOG MESSAGE").
		Date(&fixed).
		Send(w)

From Internal error using logella errs package

response.New(err, http.StatusBadRequest).
		Msg(errs.Unwrap(err).Message).
		Req("123").
		User("12345").
		Log("LOG MESSAGE").
		Date(&fixed).
		Send(w)

Struct

type Response struct {
	Err           error      `json:"-"`
	Error         string     `json:"error"`
	Message       string     `json:"message,omitempty"`
	Status        string     `json:"status"`
	RequestID     string     `json:"request_id,omitempty"`
	UserID        string     `json:"user_id,omitempty"`
	Timestamp     *time.Time `json:"timestamp,omitempty"`
	Documentation string     `json:"documentation,omitempty"`
}

Constructor

New(err error) *Response

Parameters Methods

(r *Response) Log(msg string) *Response
(r *Response) Msg(msg string) *Response
(r *Response) Req(requestID string) *Response
(r *Response) User(userID string) *Response
(r *Response) Date(timestamp *time.Time) *Response
(r *Response) Doc(documentation string) *Response
  • Log should come after Req and User, if present.

Send

Must be called at the end of the chain.

(r *Response) Send(w http.ResponseWriter)

About

A Golang minimalistic logger and error library.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages