This package defines the log settings (zerolog). Allows you to use default or customized colors configuration.
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:
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")➤ It is possible click on the path value (test/test.go:15) to go directly to file and line.
import "github.com/Lucasvmarangoni/logella/err"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.
type Error struct {
Cause error // The actual error thrown
Code int // HTTP Status Code
Message string // Custom message
trace error
}func Wrap(cause error, code int) errorExample:
errs.Wrap(err, http.StatusInternalServerError)Use case:
cfg.Db, err = pgx.ParseConfig(url)
if err != nil {
return nil, errs.Wrap(err, http.StatusInternalServerError)
}func Trace(err error) *ErrorExample:
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:
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) *ErrorExample:
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"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
}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
}func (e *Error) Msg(message string) Example:
errs.Trace(err).Msg("Message")
message := errs.Trace(err).MessageUsed 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).CauseUse 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
}Compatible with PGX V5 library
A function to determine HTTP status automatically based on database error message.
func GetHTTPStatusFromPgError(err error) intExample:
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(cause error) *ErrorExample:
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(err error, msg string)Example:
errs.FailOnErrLog(err, "database failled")Use Case:
if err = Database(); err != nil {
errs.FailOnErrLog(err, "database failled")
}PanicErr(err error, msg string)PanicBool(boolean bool, msg string)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)
The Router is a logging package for initializing routes using go-chi.
import "github.com/Lucasvmarangoni/logella/router"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.
type Router struct {
Chi chi.Router
mux *chi.Mux
Prefix string
}func NewRouter() *Router {
r := &Router{
Chi: chi.NewRouter(),
}
return r
}router := router.NewRouter()(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)(r *Router) Route(pattern string, fn func(sub *Router))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.
- The err parameter must be of type errs.Error from the logella errs package (e.g., created with errs.Wrap).
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)response.New(err, http.StatusBadRequest).
Msg(errs.Unwrap(err).Message).
Req("123").
User("12345").
Log("LOG MESSAGE").
Date(&fixed).
Send(w)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"`
}New(err error) *Response(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.
Must be called at the end of the chain.
(r *Response) Send(w http.ResponseWriter)

