Paquete errors en Go
El paquete errors proporciona funcionalidades para crear y manipular errores en Go. Este documento explora en
detalle sus características y casos de uso prácticos.
1. Conceptos Básicos
1.1 Creación de Errores
// filepath: ejemplos/errors/basic.go
package main
import (
"errors"
"fmt"
)
// Error simple
var ErrRecursoNoEncontrado = errors.New("recurso no encontrado")
// Error con formato
func newErrorConContexto(id string) error {
return fmt.Errorf("recurso %s no encontrado", id)
}
// Error personalizado
type ValidationError struct {
Campo string
Mensaje string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("error de validación en %s: %s", e.Campo, e.Mensaje)
}
1.2 Envoltura de Errores (Error Wrapping)
// filepath: ejemplos/errors/wrapping.go
package main
import (
"errors"
"fmt"
)
func procesarDatos(data []byte) error {
if len(data) == 0 {
return fmt.Errorf("datos vacíos: %w", ErrRecursoNoEncontrado)
}
return nil
}
func manejarError(err error) {
if errors.Is(err, ErrRecursoNoEncontrado) {
fmt.Println("Error de recurso no encontrado")
}
var valErr *ValidationError
if errors.As(err, &valErr) {
fmt.Printf("Error de validación: %v\n", valErr)
}
}
2. Patrones Avanzados
2.1 Cadena de Errores
// filepath: ejemplos/errors/chain.go
package main
import (
"errors"
"fmt"
)
type ErrorCode int
const (
ErrCodeDB ErrorCode = iota + 1
ErrCodeValidation
ErrCodeBusiness
)
type AppError struct {
Code ErrorCode
Message string
Err error
}
func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("[%d] %s: %v", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}
func (e *AppError) Unwrap() error {
return e.Err
}
func procesarOperacion() error {
err := operacionDB()
if err != nil {
return &AppError{
Code: ErrCodeDB,
Message: "error en base de datos",
Err: err,
}
}
return nil
}
2.2 Error Group Pattern
// filepath: ejemplos/errors/errorgroup.go
package main
import (
"context"
"errors"
"fmt"
"sync"
)
type ErrorGroup struct {
wg sync.WaitGroup
errMu sync.Mutex
err error
ctx context.Context
cancel context.CancelFunc
}
func NewErrorGroup() *ErrorGroup {
ctx, cancel := context.WithCancel(context.Background())
return &ErrorGroup{
ctx: ctx,
cancel: cancel,
}
}
func (g *ErrorGroup) Go(f func() error) {
g.wg.Add(1)
go func() {
defer g.wg.Done()
if err := f(); err != nil {
g.errMu.Lock()
if g.err == nil {
g.err = err
g.cancel()
}
g.errMu.Unlock()
}
}()
}
func (g *ErrorGroup) Wait() error {
g.wg.Wait()
g.cancel()
return g.err
}
3. Casos de Uso Prácticos
3.1 API REST con Manejo de Errores
// filepath: ejemplos/errors/api.go
package main
import (
"encoding/json"
"errors"
"net/http"
)
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
}
func handleError(w http.ResponseWriter, err error) {
var response ErrorResponse
switch {
case errors.Is(err, ErrRecursoNoEncontrado):
response = ErrorResponse{
Code: 404,
Message: "Recurso no encontrado",
Details: err.Error(),
}
case errors.As(err, &ValidationError{}):
response = ErrorResponse{
Code: 400,
Message: "Error de validación",
Details: err.Error(),
}
default:
response = ErrorResponse{
Code: 500,
Message: "Error interno del servidor",
}
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(response.Code)
json.NewEncoder(w).Encode(response)
}
3.2 Sistema de Logging con Errores
// filepath: ejemplos/errors/logging.go
package main
import (
"errors"
"log"
"runtime"
)
type ErrorLogger struct {
logger *log.Logger
}
func (el *ErrorLogger) LogError(err error) {
if err == nil {
return
}
// Obtener información del stack
pc, file, line, _ := runtime.Caller(1)
fn := runtime.FuncForPC(pc).Name()
// Log del error con contexto
el.logger.Printf("[ERROR] %s:%d %s: %v", file, line, fn, err)
// Log de la cadena de errores
for unwrapped := errors.Unwrap(err); unwrapped != nil; unwrapped = errors.Unwrap(unwrapped) {
el.logger.Printf("[CAUSA] %v", unwrapped)
}
}
4. Mejores Prácticas
- Diseño de Errores
- Usar errores descriptivos y específicos
- Incluir contexto relevante
- Mantener consistencia en el formato
- Manejo de Errores
- No ignorar errores
- Usar wrapping apropiadamente
- Implementar logging adecuado
- Consideraciones de Rendimiento
- Evitar crear errores innecesarios
- Reutilizar errores comunes
- Considerar el impacto en el stack trace