You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
5.1 KiB
177 lines
5.1 KiB
# 🧠 **errs: Gestión de Errores Estructurada para Go**
|
|
|
|
*Parte del núcleo de Active-Go Core*
|
|
|
|
`errs` es un módulo esencial de la librería core de Active-Go, diseñado para modernizar y enriquecer el manejo de errores en Go, especialmente en sistemas distribuidos y arquitecturas de microservicios. Proporciona una estructura robusta que permite **clasificar**, **componer** y **contextualizar** los errores de forma precisa y eficaz.
|
|
|
|
---
|
|
|
|
## 🚀 Filosofía
|
|
|
|
La motivación detrás de `errs` surge de nuestra experiencia en ActiveThing.com enfrentando la complejidad de sistemas reales. Go proporciona un tipo `error` simple, pero limitado. `errs` expande este modelo con:
|
|
|
|
### ✅ Clasificación Estructurada
|
|
|
|
* Define jerarquías de errores (`modulo::clase.subclase`) para evitar comparaciones de strings frágiles.
|
|
* Permite identificar errores como `auth::invalid_token` o `db::not_found.user` y aplicar lógicas según su clase o módulo.
|
|
|
|
### 🧩 Composición y Contexto
|
|
|
|
* Usa el patrón de **composición** para añadir metadatos sin alterar la clase del error.
|
|
* Agrega mensajes, códigos, causas, stack traces, fechas, trazabilidad, etc.
|
|
|
|
### 🌐 Trazabilidad en Microservicios
|
|
|
|
* Unifica el lenguaje de errores entre servicios.
|
|
* Los errores conservan su contexto a lo largo de la cadena (auth → pedidos → API Gateway).
|
|
|
|
### 🔍 Visibilidad para Operaciones
|
|
|
|
* Logs ricos con jerarquías, metadatos y stack traces.
|
|
* Salida formateada para mejorar la observabilidad.
|
|
|
|
### 🧱 Inmutabilidad y Predictibilidad
|
|
|
|
* Los errores compuestos son inmutables.
|
|
* Facilita debugging seguro y reduce efectos colaterales.
|
|
|
|
### 📊 Errores como Datos
|
|
|
|
* Los errores transportan información tipificada útil para monitoreo y lógica programática.
|
|
|
|
---
|
|
|
|
## 🛠️ Uso Básico
|
|
|
|
### 📌 Crear Errores
|
|
|
|
```go
|
|
var (
|
|
ErrNotFound = errs.CreateErr("app::not_found")
|
|
ErrInvalidInput = errs.CreateErr("app::invalid_input")
|
|
)
|
|
```
|
|
|
|
### 🧬 Subclases
|
|
|
|
```go
|
|
var (
|
|
ErrUserNotFound = errs.CreateSubclassOf(ErrNotFound, "user")
|
|
ErrProductNotFound = errs.CreateSubclassOf(ErrNotFound, "product")
|
|
)
|
|
|
|
if errs.IsSubclassOf(err, ErrNotFound) {
|
|
// Es una variante de "no encontrado"
|
|
}
|
|
```
|
|
|
|
### 🧠 Composición con Contexto
|
|
|
|
```go
|
|
err := errs.ComposeWithMessage(ErrInvalidInput, "Campo 'nombre_usuario' es obligatorio")
|
|
|
|
original := errors.New("DB desconectada")
|
|
errWithCause := errs.ComposeWithCause(errs.CreateErr("db::internal_error"), original)
|
|
|
|
complexErr := errs.Compose(
|
|
ErrUserNotFound,
|
|
errs.WithMessage("ID %d no encontrado", 12345),
|
|
errs.WithCode(404),
|
|
errs.WithDate(),
|
|
errs.WithStack(),
|
|
errs.WithRetryable(errs.RetryableCanRetry|errs.RetryableIsTemporary, 5*time.Second),
|
|
errs.WithInfo("El usuario no existe", "Verifique el ID", "https://docs/errors/user-404"),
|
|
errs.WithOrigin("user_service", "alpha"),
|
|
errs.WithObservedBy("api_gateway"),
|
|
errs.WithMeta("trace_id", "abc123xyz"),
|
|
errs.WithMetas(
|
|
errs.CreateMeta("http_status", 500, errs.MetaTypeInt),
|
|
errs.CreateMeta("is_critical", true, errs.MetaTypeBool),
|
|
),
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## 🔍 Acceso a Metadatos
|
|
|
|
```go
|
|
if ewc, ok := complexErr.(errs.ErrorWithCode); ok {
|
|
fmt.Println("Código:", ewc.Code())
|
|
}
|
|
if ewc, ok := complexErr.(errs.ErrorWithCause); ok {
|
|
fmt.Println("Causa:", ewc.Cause())
|
|
}
|
|
if ewst, ok := complexErr.(errs.ErrorWithStack); ok {
|
|
fmt.Println("Stack:", ewst.Stack())
|
|
}
|
|
if ewms, ok := complexErr.(errs.ErrorWithMetas); ok {
|
|
for _, meta := range ewms.Metas() {
|
|
fmt.Printf("%s = %v (%s)\n", meta.Name(), meta.Value(), meta.Type())
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🧾 Formato Avanzado para Logs
|
|
|
|
```go
|
|
fmt.Printf("Error básico: %v\n", complexErr)
|
|
fmt.Printf("Solo clase: %s\n", complexErr)
|
|
fmt.Printf("Detallado: %+v\n", complexErr)
|
|
fmt.Printf("Debug: %#v\n", complexErr)
|
|
```
|
|
|
|
Soporte para **salida con color** en terminales compatibles para mayor claridad.
|
|
|
|
---
|
|
|
|
## 📚 Manejo de Listas de Errores
|
|
|
|
```go
|
|
var errsList errs.ErrorList
|
|
errsList = errsList.Append(err1)
|
|
errsList = errsList.AppendNotNil(err2)
|
|
|
|
if errsList.Any() {
|
|
fmt.Printf("%d errores encontrados: %v\n", errsList.Count(), errsList.Clean())
|
|
first := errsList.First()
|
|
last := errsList.Last()
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🧩 Funcionalidades Clave
|
|
|
|
* ✅ Interfaces de Go: `error`, `fmt.Formatter`, `errors.Is`, `errors.Unwrap`
|
|
* 📐 `Err`: tipo base con jerarquía (`modulo::clase.subclase`)
|
|
* 🔗 Jerarquía de errores:
|
|
|
|
* `CreateErr(string)`
|
|
* `CreateSubclassOf(parent, name)`
|
|
* `IsSubclassOf(err, parent)`
|
|
* Métodos: `Parent()`, `Class()`, `Module()`
|
|
* 🏷️ Metadatos tipados: `int`, `bool`, `string`, etc.
|
|
* 🧠 Composición segura y declarativa
|
|
* 🔁 Soporte para errores reintentables
|
|
* 📊 Visualización enriquecida para monitoreo
|
|
|
|
---
|
|
|
|
## 🧪 Recomendado para:
|
|
|
|
* Sistemas distribuidos con múltiples servicios
|
|
* Aplicaciones con alto requerimiento de trazabilidad
|
|
* Equipos que priorizan depuración clara y mantenibilidad
|
|
|
|
---
|
|
|
|
**Repositorio oficial:** [Active-Go Core](https://github.com/active-go/core)
|
|
**Documentación:** [https://docs.active-go.com/errors](https://g.activething.com/errors)
|
|
|
|
---
|
|
|
|
> *Diseñado con propósito. Inspirado por la complejidad. Refinado en producción.*
|