parent
b1f2a8ccd1
commit
8f20e8523d
File diff suppressed because it is too large
Load Diff
@ -1,79 +0,0 @@
|
||||
// =============================================================================
|
||||
// Active-GO Framework
|
||||
// Copyright (c) 2025 ActiveThing (https://activething.com)
|
||||
// Author: Juan V. Navarro juanvnl@activething.com
|
||||
// =============================================================================
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// =============================================================================
|
||||
// Date Create 18/05/2025
|
||||
// =============================================================================
|
||||
|
||||
package errs
|
||||
|
||||
/*
|
||||
// Basic ANSI color codes
|
||||
const (
|
||||
ColorBlack = "\033[30m"
|
||||
ColorRed = "\033[31m"
|
||||
ColorGreen = "\033[32m"
|
||||
ColorYellow = "\033[33m"
|
||||
ColorBlue = "\033[34m"
|
||||
ColorMagenta = "\033[35m"
|
||||
ColorCyan = "\033[36m"
|
||||
ColorWhite = "\033[37m"
|
||||
|
||||
ColorBrightBlack = "\033[90m"
|
||||
ColorBrightRed = "\033[91m"
|
||||
ColorBrightGreen = "\033[92m"
|
||||
ColorBrightYellow = "\033[93m"
|
||||
ColorBrightBlue = "\033[94m"
|
||||
ColorBrightMagenta= "\033[95m"
|
||||
ColorBrightCyan = "\033[96m"
|
||||
ColorBrightWhite = "\033[97m"
|
||||
|
||||
BgBlack = "\033[40m"
|
||||
BgRed = "\033[41m"
|
||||
BgGreen = "\033[42m"
|
||||
BgYellow = "\033[43m"
|
||||
BgBlue = "\033[44m"
|
||||
BgMagenta = "\033[45m"
|
||||
BgCyan = "\033[46m"
|
||||
BgWhite = "\033[47m"
|
||||
|
||||
BgBrightBlack = "\033[100m"
|
||||
BgBrightRed = "\033[101m"
|
||||
BgBrightGreen = "\033[102m"
|
||||
BgBrightYellow = "\033[103m"
|
||||
BgBrightBlue = "\033[104m"
|
||||
BgBrightMagenta = "\033[105m"
|
||||
BgBrightCyan = "\033[106m"
|
||||
BgBrightWhite = "\033[107m"
|
||||
|
||||
TextBold = "\033[1m" // Bold text
|
||||
TextFaint = "\033[2m" // Faint text (dim)
|
||||
TextUnderline = "\033[4m" // Underlined text
|
||||
TextReverse = "\033[7m" // Swap foreground and background colors
|
||||
|
||||
ColorReset = "\033[0m" // Reset all attributes
|
||||
|
||||
)
|
||||
|
||||
*/
|
||||
@ -0,0 +1,181 @@
|
||||
# **errs: Gestión de Errores Estructurada para Go (Parte de Active-Go Core)**
|
||||
|
||||
errs es un módulo fundamental dentro de la librería core de Active-Go, diseñado para transformar la forma en que manejamos los errores en aplicaciones Go, especialmente en entornos distribuidos y arquitecturas de microservicios. Va más allá de la simplicidad del tipo error nativo de Go, ofreciendo una estructura enriquecida que facilita la **clasificación, composición y contextualización** de los errores.
|
||||
|
||||
## **Filosofía**
|
||||
|
||||
La filosofía central de errs se basa en la experiencia de desarrollar y operar sistemas complejos en ActiveThing.com. Nace de la necesidad de superar las limitaciones del error básico de Go para obtener:
|
||||
|
||||
* **Errores Estructurados y Clasificados:** Evita la frágil comparación de cadenas. Permite definir **jerarquías de errores** (clases y subclases) para una identificación precisa y un manejo más granular. Esto significa que podemos identificar un error como, por ejemplo, servicio::tipo\_negocio.subtipo\_específico y tomar decisiones lógicas basadas en su clasificación, no solo en su mensaje.
|
||||
* **Composición Flexible y Contexto Enriquecido:** Un error rara vez ocurre de forma aislada. errs facilita la adición de **información contextual relevante** a los errores existentes sin modificar su tipo subyacente. A través del patrón de **"compositor"**, un error puede ser "envuelto" con metadatos cruciales como códigos, causas, información de usuario, detalles de reintentos, rastreos de pila, etc. Esto construye una **historia completa del error** a medida que se propaga por el sistema.
|
||||
* **Trazabilidad Unificada en Entornos Distribuidos:** En microservicios, los errores viajan. errs proporciona un **lenguaje común para los errores** a través de distintos servicios. Un error que se origina en un servicio de autenticación y se propaga a un servicio de pedidos, y luego a un API Gateway, puede llevar consigo su contexto original y añadir metadatos de cada punto que atraviesa. Esto es vital para la depuración y el monitoreo en cascada.
|
||||
* **Visibilidad Enriquecida para la Operación:** Simplifica la depuración y el diagnóstico en sistemas complejos. errs ofrece capacidades de formato avanzadas, incluyendo la visualización de jerarquías de errores, metadatos y rastreos de pila, lo que convierte un simple log en una herramienta de diagnóstico potente.
|
||||
* **Inmutabilidad y Predictibilidad:** Los errores, una vez creados o compuestos, son inmutables. Esto contribuye a un comportamiento predecible y reduce los efectos secundarios inesperados en el manejo de errores.
|
||||
* **Errores como Datos:** Al adjuntar **metadatos tipificados** a los errores, errs promueve la idea de que los errores son objetos de datos con información valiosa que puede ser utilizada programáticamente por la lógica de la aplicación, los sistemas de registro o las herramientas de monitoreo, en lugar de ser solo mensajes opacos.
|
||||
|
||||
## **Modo de Uso**
|
||||
|
||||
errs introduce el tipo **Err** como la base para errores estructurados. Los errores pueden crearse, clasificarse y enriquecerse con diversos metadatos utilizando las funciones y tipos proporcionados.
|
||||
|
||||
### **Creación de Errores Básicos**
|
||||
|
||||
Puedes crear un error base usando CreateErr. Los errores pueden tener una estructura de módulo y clase, por ejemplo, modulo::clase:
|
||||
|
||||
import "core/errs"
|
||||
|
||||
var (
|
||||
ErrNotFound \= errs.CreateErr("app::not\_found")
|
||||
ErrInvalidInput \= errs.CreateErr("app::invalid\_input")
|
||||
)
|
||||
|
||||
### **Creación de Subclases de Errores**
|
||||
|
||||
Para crear jerarquías de errores, puedes definir subclases. Esto es útil para errores más específicos dentro de una categoría general:
|
||||
|
||||
import "core/errs"
|
||||
|
||||
var (
|
||||
ErrUserNotFound \= errs.CreateSubclassOf(ErrNotFound, "user")
|
||||
ErrProductNotFound \= errs.CreateSubclassOf(ErrNotFound, "product")
|
||||
)
|
||||
|
||||
// Puedes verificar si un error es subclase de otro
|
||||
if errs.IsSubclassOf(err, ErrNotFound) {
|
||||
// Es un error de tipo "no encontrado", ya sea de usuario o producto.
|
||||
}
|
||||
|
||||
### **Composición de Errores con Metadatos**
|
||||
|
||||
El patrón clave de errs es la **composición de errores**. Utiliza las funciones WithX o ComposeWithX para añadir información relevante sin cambiar el tipo base del error. Esto permite construir un error con todos los detalles necesarios:
|
||||
|
||||
import (
|
||||
"core/errs"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Añadir un mensaje detallado al error
|
||||
err := errs.ComposeWithMessage(ErrInvalidInput, "El campo '%s' es obligatorio", "nombre\_usuario")
|
||||
|
||||
// Añadir una causa subyacente (el error original que provocó este)
|
||||
originalDBErr := errors.New("conexión a la base de datos perdida")
|
||||
errWithCause := errs.ComposeWithCause(errs.CreateErr("db::internal\_error"), originalDBErr)
|
||||
|
||||
// Composición encadenada para construir un error con múltiples capas de contexto
|
||||
complexErr := errs.Compose(
|
||||
ErrUserNotFound,
|
||||
errs.WithMessage("Usuario con ID %d no encontrado en el sistema principal", 12345),
|
||||
errs.WithCode(404), // Código de aplicación o HTTP
|
||||
errs.WithDate(), // Fecha y hora del error
|
||||
errs.WithStack(), // Rastreo de pila en el punto de composición (requiere core/excs)
|
||||
errs.WithRetryable(errs.RetryableCanRetry|errs.RetryableIsTemporary, 5\*time.Second), // Marcado como reintentable
|
||||
errs.WithInfo("Este error indica que el usuario no existe", "Verifique el ID de usuario o el servicio de autenticación", "https://docs.active-go.com/errors/user-404"),
|
||||
errs.WithOrigin("user\_service", "instance\_alpha"), // Origen del error
|
||||
errs.WithObservedBy("api\_gateway"), // Dónde fue observado o modificado
|
||||
// Puedes añadir metadatos genéricos:
|
||||
errs.WithMeta("trace\_id", "abc123xyz"),
|
||||
errs.WithMetas(
|
||||
errs.CreateMeta("http\_status", 500, errs.MetaTypeInt),
|
||||
errs.CreateMeta("is\_critical", true, errs.MetaTypeBool),
|
||||
),
|
||||
)
|
||||
|
||||
### **Acceso a Metadatos**
|
||||
|
||||
Puedes acceder a los metadatos de un error mediante la conversión de tipo o interfaces, lo que permite una lógica de manejo de errores más sofisticada:
|
||||
|
||||
if ewc, ok := complexErr.(errs.ErrorWithCode); ok {
|
||||
fmt.Printf("Código de error: %d\\n", ewc.Code())
|
||||
}
|
||||
|
||||
if ewc, ok := complexErr.(errs.ErrorWithCause); ok {
|
||||
fmt.Printf("Causa: %v\\n", ewc.Cause())
|
||||
}
|
||||
|
||||
if ewst, ok := complexErr.(errs.ErrorWithStack); ok {
|
||||
fmt.Printf("Stack: %v\\n", ewst.Stack()) // Acceso al stack trace
|
||||
}
|
||||
|
||||
// Acceso a metadatos genéricos
|
||||
if ewm, ok := complexErr.(errs.ErrorWithMeta); ok {
|
||||
if ewm.Name() \== "trace\_id" {
|
||||
fmt.Printf("Trace ID: %v\\n", ewm.Value())
|
||||
}
|
||||
}
|
||||
|
||||
// Acceso a múltiples metadatos genéricos
|
||||
if ewms, ok := complexErr.(errs.ErrorWithMetas); ok {
|
||||
for \_, meta := range ewms.Metas() {
|
||||
fmt.Printf("Meta: %s \= %v (Tipo: %s)\\n", meta.Name(), meta.Value(), meta.Type().String())
|
||||
}
|
||||
}
|
||||
|
||||
### **Formato de Errores para Depuración y Logs**
|
||||
|
||||
errs mejora significativamente la capacidad de formato de errores de Go. Implementa fmt.Formatter, permitiendo usar verbos especiales para controlar la cantidad de detalle en la salida:
|
||||
|
||||
fmt.Printf("Error básico: %v\\n", complexErr) // Muestra jerarquía y stack principal
|
||||
fmt.Printf("Error estructurado: %s\\n", complexErr) // Muestra solo la clase del error (string del Err base)
|
||||
fmt.Printf("Error detallado: %+v\\n", complexErr) // Muestra jerarquía completa, todos los metadatos y stack completo (recomendado para logs)
|
||||
fmt.Printf("Error para depuración: %\#v\\n", complexErr) // Muestra metadatos del error actual y stack principal (útil para depuración)
|
||||
|
||||
Las opciones de formato se controlan mediante el tipo FormatShow y los modificadores de fmt.State, permitiendo incluso salida con **colores** en terminales compatibles para mejorar la legibilidad.
|
||||
|
||||
### **Manejo de Listas de Errores**
|
||||
|
||||
ErrorList es una colección útil para manejar múltiples errores, común en escenarios donde operaciones paralelas o validaciones acumulan fallos:
|
||||
|
||||
var errors errs.ErrorList
|
||||
errors \= errors.Append(someValidationErr)
|
||||
errors \= errors.AppendNotNil(anotherAPIError)
|
||||
|
||||
if errors.Any() { // Verifica si hay al menos un error no nulo
|
||||
fmt.Printf("Se encontraron %d errores: %v\\n", errors.Count(), errors.Clean()) // Count() y Clean()
|
||||
firstErr := errors.First() // Accede al primer error no nulo
|
||||
lastErr := errors.Last() // Accede al último error no nulo
|
||||
// ... otras operaciones con la lista
|
||||
}
|
||||
|
||||
## **Funcionalidades Clave**
|
||||
|
||||
* **Implementación de Interfaces Estándar de Go:** errs se integra perfectamente con el ecosistema Go al implementar interfaces clave como error, fmt.Formatter, errors.Is, y errors.Unwrap.
|
||||
* **Err Tipo Base:** Un tipo de cadena (string) que representa un error estructurado, permitiendo la definición de módulos y clases de errores (e.g., modulo::clase o modulo::clase.subclase).
|
||||
* **Jerarquía de Errores:**
|
||||
* CreateErr(value string): Crea un nuevo error con una clase y opcionalmente un módulo.
|
||||
* CreateSubclassOf(parent Err, subclass string): Permite crear una subclase de un error existente, estableciendo una relación jerárquica (e.g., app::not\_found.user).
|
||||
* IsSubclassOf(err, parent error): Verifica si un error es una subclase de otro, facilitando el manejo polimórfico de errores.
|
||||
* Parent() (Err, bool): Devuelve el error padre en la jerarquía, si existe.
|
||||
* Class(): Devuelve el nombre de la clase del error.
|
||||
* Module(): Devuelve el nombre del módulo del error.
|
||||
* **Composición de Errores (Composer):**
|
||||
* Un patrón de diseño que permite añadir características o metadatos a un error existente sin modificar su tipo original.
|
||||
* Funciones WithX y ComposeWithX para "envolver" un error con información adicional de forma aditiva.
|
||||
* **Tipos de Composición de Errores:** errs provee una variedad de tipos que implementan error y añaden contexto específico:
|
||||
* ErrorWithCause: Adjunta una causa subyacente al error (similar a errors.Unwrap).
|
||||
* ErrorWithCauses: Permite adjuntar múltiples causas a un error (implementa el patrón interface{ Unwrap() \[\]error } de Go 1.20+).
|
||||
* ErrorWithCode: Añade un código numérico al error (ej. código de negocio, HTTP).
|
||||
* ErrorWithDate: Incluye la fecha y hora de creación del error.
|
||||
* ErrorWithInfo: Proporciona una descripción, ayuda y un enlace a documentación para el error.
|
||||
* ErrorWithMessage: Permite añadir un mensaje descriptivo más detallado al error, incluso con formato de cadena.
|
||||
* ErrorWithMeta / ErrorWithMetas: Adjunta metadatos genéricos de clave-valor (Meta) al error. ErrorWithMeta usa MetaTypeAny por defecto, mientras que ErrorWithMetas permite especificar tipos más concretos si se crean objetos Meta directamente.
|
||||
* ErrorObservedBy: Registra qué componente o servicio observó el error. Crucial para la trazabilidad en microservicios.
|
||||
* ErrorWithOrigin: Indica el servicio y opcionalmente la instancia donde el error se originó.
|
||||
* ErrorWithRetryable: Marca un error como reintentable, especificando si es temporal, si es idempotente y el tiempo de reintento sugerido.
|
||||
* ErrorWithSeverity: Asigna un nivel de severidad al error (e.g., Info, Warn, Error, Fatal).
|
||||
* ErrorWithStack: Captura el rastreo de pila en el punto de composición/creación del error. **Depende del paquete core/excs**.
|
||||
* **ErrorList:** Una colección útil para manejar múltiples errores. Proporciona métodos como Any(), Len(), Count(), First(), Last(), Append(), AppendNotNil(), Reset(), y Clean() para una gestión conveniente.
|
||||
* **MetadataProvider:** Una interfaz implementada por los errores compuestos que permite extraer los metadatos asociados de forma programática, controlando la selección con MetaSelector.
|
||||
* **Formato Personalizado (fmt.Formatter):**
|
||||
* Implementa la interfaz fmt.Formatter para un formato de error avanzado y configurable.
|
||||
* Soporte para verbos de formato (%v, %+v, %\#v) para controlar la profundidad y el tipo de información mostrada (jerarquía, metadatos, stack traces).
|
||||
* Opciones de formato configurables (FormatOptions), incluyendo la posibilidad de habilitar **colores ANSI** en la salida de la consola para una mejor legibilidad.
|
||||
* **Validación Estricta:** Utiliza expresiones regulares para validar los nombres de los errores y los metadatos, asegurando que sigan un formato consistente en todo el sistema y previniendo errores estáticos.
|
||||
* **Manejo de Errores Estándar:** ToError(err any) convierte una variedad de tipos (incluyendo nil, error, \[\]error) en un tipo error estándar.
|
||||
* **Errores Predefinidos:** Proporciona errores comunes como E.Multiple, E.InvalidNilError, E.InvalidClass, etc., para errores internos del propio módulo errs.
|
||||
* **Manejo de Panics:** PanicErr y CreatePanicErr proporcionan una forma estructurada de convertir un panic recuperado en un error manejable, adjuntando la causa original.
|
||||
* **Errores de Parámetros:** Una serie de errores específicos (ParamErr, CreateParamNilErr, CreateParamEmptyErr, CreateParamRange) para indicar problemas con los parámetros de las funciones, estandarizando estos errores comunes de entrada.
|
||||
|
||||
### **Instalación**
|
||||
|
||||
errs es parte de la librería core de Active-Go. Para utilizarlo, deberás incluir la librería Active-Go Core en las dependencias de tu proyecto. Consulta la documentación principal de Active-Go Core para obtener instrucciones detalladas sobre cómo añadirla a tu proyecto Go.
|
||||
Loading…
Reference in new issue