errors

package
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 23, 2025 License: MIT Imports: 4 Imported by: 0

README

ServiceLib Error Handling

This package provides a comprehensive error handling system for the application. It includes error codes, HTTP status mapping, contextual information, and utilities for creating, wrapping, and serializing errors.

For detailed information about the error handling design, including design principles, error type hierarchy, relationships between packages, and best practices, see Error Handling Design.

Package Structure

The error handling package is organized into several sub-packages:

  • core: Core error handling functionality (error codes, base error type, utility functions)
  • domain: Domain-specific error types (validation errors, business rule errors, not found errors)
  • infra: Infrastructure-related error types (database errors, network errors, external service errors)
  • app: Application-level error types (configuration errors, authentication errors, authorization errors)
  • http: HTTP-related error utilities
  • log: Logging integration
  • metrics: Metrics integration
  • trace: Tracing integration
  • utils: Utility functions for error handling

Features

  • Clear Error Type Hierarchy:

    • BaseError: The foundation for all error types
    • DomainError: Domain-specific errors (validation, business rules, not found)
    • InfrastructureError: Infrastructure-related errors (database, network, external services)
    • ApplicationError: Application-level errors (configuration, authentication, authorization)
  • Consistent Error Creation and Wrapping:

    • Factory functions for creating different error types
    • Wrap functions for adding context to errors
    • Support for error cause chains
  • Error Codes and HTTP Status Mapping:

    • Predefined error codes for common error scenarios
    • Automatic mapping of error codes to HTTP status codes
  • Contextual Information:

    • Operation name
    • Source location (file and line)
    • Additional details as key-value pairs
    • Error cause chain
  • Error Categorization and Type Checking:

    • Type-specific checking functions (IsValidationError, IsDatabaseError, etc.)
    • Support for standard errors.Is and errors.As functions
  • JSON Serialization:

    • Convert errors to JSON for API responses
    • Include all contextual information in JSON output
  • Integration with Logging, Metrics, and Tracing:

    • Log errors with all contextual information
    • Record error metrics with error type and code
    • Add error information to traces

Installation

go get github.com/abitofhelp/servicelib/errors

Usage

Basic Error Creation
package main

import (
    "fmt"
    "github.com/abitofhelp/servicelib/errors"
)

func main() {
    // Create a simple error
    err := errors.New(errors.InvalidInputCode, "Invalid input provided")
    fmt.Println(err) // Output: Invalid input provided

    // Create a domain error
    err = errors.NewDomainError(errors.NotFoundCode, "User not found", nil)
    fmt.Println(err) // Output: User not found

    // Create a validation error
    err = errors.NewValidationError("Field 'name' is required", "name", nil)
    fmt.Println(err) // Output: Field 'name' is required

    // Create a business rule error
    err = errors.NewBusinessRuleError("User must be at least 18 years old", "MinimumAge", nil)
    fmt.Println(err) // Output: User must be at least 18 years old

    // Create a not found error
    err = errors.NewNotFoundError("User", "123", nil)
    fmt.Println(err) // Output: User with ID 123 not found

    // Create an infrastructure error
    err = errors.NewDatabaseError("Failed to query database", "SELECT", "users", nil)
    fmt.Println(err) // Output: Failed to query database

    // Create an application error
    err = errors.NewConfigurationError("Invalid configuration value", "MAX_CONNECTIONS", "abc", nil)
    fmt.Println(err) // Output: Invalid configuration value
}
Error Wrapping
package main

import (
    "database/sql"
    "fmt"
    "github.com/abitofhelp/servicelib/errors"
)

func main() {
    // Wrap a standard error
    originalErr := sql.ErrNoRows
    wrappedErr := errors.Wrap(originalErr, errors.NotFoundCode, "Failed to find user")
    fmt.Println(wrappedErr) // Output: Failed to find user: sql: no rows in result set

    // Wrap with an operation
    wrappedWithOp := errors.WrapWithOperation(originalErr, errors.NotFoundCode, "Failed to find user", "GetUserByID")
    fmt.Println(wrappedWithOp) // Output: GetUserByID: Failed to find user: sql: no rows in result set

    // Wrap with details
    details := map[string]interface{}{
        "user_id": "123",
        "table":   "users",
    }
    wrappedWithDetails := errors.WrapWithDetails(originalErr, errors.NotFoundCode, "Failed to find user", details)
    fmt.Println(wrappedWithDetails) // Output: Failed to find user: sql: no rows in result set

    // Unwrap to get the original error
    unwrapped := errors.Unwrap(wrappedErr)
    fmt.Println(unwrapped) // Output: sql: no rows in result set

    // Check if an error is of a specific type
    if errors.Is(wrappedErr, sql.ErrNoRows) {
        fmt.Println("The original error was sql.ErrNoRows")
    }
}
Error Type Checking
package main

import (
    "fmt"
    "github.com/abitofhelp/servicelib/errors"
)

func main() {
    // Create errors of different types
    notFoundErr := errors.NewNotFoundError("User", "123", nil)
    validationErr := errors.NewValidationError("Email is invalid", "email", nil)
    dbErr := errors.NewDatabaseError("Failed to query database", "SELECT", "users", nil)
    authErr := errors.NewAuthenticationError("Invalid credentials", "john.doe", nil)

    // Check error types
    fmt.Printf("Is not found error: %v\n", errors.IsNotFoundError(notFoundErr))
    fmt.Printf("Is validation error: %v\n", errors.IsValidationError(validationErr))
    fmt.Printf("Is database error: %v\n", errors.IsDatabaseError(dbErr))
    fmt.Printf("Is authentication error: %v\n", errors.IsAuthenticationError(authErr))

    // Use in error handling
    handleError(notFoundErr)
    handleError(validationErr)
    handleError(dbErr)
    handleError(authErr)
}

func handleError(err error) {
    switch {
    case errors.IsNotFoundError(err):
        fmt.Println("Handle not found error:", err)
    case errors.IsValidationError(err):
        fmt.Println("Handle validation error:", err)
    case errors.IsDatabaseError(err):
        fmt.Println("Handle database error:", err)
    case errors.IsAuthenticationError(err):
        fmt.Println("Handle authentication error:", err)
    default:
        fmt.Println("Handle generic error:", err)
    }
}
HTTP Status Mapping
package main

import (
    "fmt"
    "github.com/abitofhelp/servicelib/errors"
    "net/http"
)

func main() {
    // Create errors of different types
    notFoundErr := errors.NewNotFoundError("User", "123", nil)
    validationErr := errors.NewValidationError("Email is invalid", "email", nil)
    dbErr := errors.NewDatabaseError("Failed to query database", "SELECT", "users", nil)
    authErr := errors.NewAuthenticationError("Invalid credentials", "john.doe", nil)

    // Get HTTP status codes
    fmt.Printf("Not found error HTTP status: %d\n", errors.GetHTTPStatus(notFoundErr))
    fmt.Printf("Validation error HTTP status: %d\n", errors.GetHTTPStatus(validationErr))
    fmt.Printf("Database error HTTP status: %d\n", errors.GetHTTPStatus(dbErr))
    fmt.Printf("Authentication error HTTP status: %d\n", errors.GetHTTPStatus(authErr))

    // Use in HTTP handler
    handleHTTPError(notFoundErr, http.ResponseWriter(nil))
}

func handleHTTPError(err error, w http.ResponseWriter) {
    status := errors.GetHTTPStatus(err)
    if status == 0 {
        status = http.StatusInternalServerError
    }

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    w.Write([]byte(errors.ToJSON(err)))
}
JSON Serialization
package main

import (
    "fmt"
    "github.com/abitofhelp/servicelib/errors"
)

func main() {
    // Create an error with details
    details := map[string]interface{}{
        "user_id": "123",
        "action":  "create_order",
        "status":  "failed",
    }
    err := errors.WrapWithDetails(
        errors.New(errors.ValidationErrorCode, "Invalid input"),
        errors.ValidationErrorCode,
        "Failed to process request",
        details,
    )

    // Convert the error to JSON
    jsonStr := errors.ToJSON(err)
    fmt.Println(jsonStr)

    // Output:
    // {
    //   "message": "Failed to process request: Invalid input",
    //   "code": "VALIDATION_ERROR",
    //   "details": {
    //     "user_id": "123",
    //     "action": "create_order",
    //     "status": "failed"
    //   },
    //   "source": "main.go",
    //   "line": 15
    // }
}
Integration with Logging
package main

import (
    "context"
    "github.com/abitofhelp/servicelib/errors"
    "log"
)

func main() {
    // Create a context
    ctx := context.Background()

    // Create an error
    err := errors.NewDatabaseError("Failed to query database", "SELECT", "users", nil)

    // Log the error with context
    logError(ctx, err)
}

func logError(ctx context.Context, err error) {
    // Get error details
    code := ""
    if e, ok := err.(interface{ GetCode() errors.ErrorCode }); ok {
        code = string(e.GetCode())
    }

    // Log the error with context
    log.Printf(
        "Error occurred: code=%s, message=%s, http_status=%d",
        code,
        err.Error(),
        errors.GetHTTPStatus(err),
    )
}

Best Practices

1. Use the Appropriate Error Type

Choose the most specific error type for your situation:

// For domain validation errors
err := errors.NewValidationError("Email is invalid", "email", nil)

// For not found errors
err := errors.NewNotFoundError("User", "123", nil)

// For database errors
err := errors.NewDatabaseError("Failed to query database", "SELECT", "users", nil)

// For authentication errors
err := errors.NewAuthenticationError("Invalid credentials", "john.doe", nil)
2. Add Context to Errors

Wrap errors with additional context:

// Wrap with operation name
err = errors.WrapWithOperation(err, errors.DatabaseErrorCode, "Database query failed", "GetUserByID")

// Wrap with details
details := map[string]interface{}{
    "user_id": "123",
    "query":   "SELECT * FROM users WHERE id = ?",
}
err = errors.WrapWithDetails(err, errors.DatabaseErrorCode, "Database query failed", details)
3. Check Error Types

Use the type checking functions to handle different error types:

switch {
case errors.IsNotFoundError(err):
    // Handle not found error
case errors.IsValidationError(err):
    // Handle validation error
case errors.IsDatabaseError(err):
    // Handle database error
default:
    // Handle other errors
}

### 4. Map Errors to HTTP Status Codes

Use the GetHTTPStatus function to map errors to HTTP status codes:

```go
func handleHTTPError(w http.ResponseWriter, err error) {
    status := errors.GetHTTPStatus(err)
    if status == 0 {
        status = http.StatusInternalServerError
    }

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    w.Write([]byte(errors.ToJSON(err)))
}
5. Provide Detailed Error Messages

Include detailed error messages that help identify the issue:

// Instead of this:
err := errors.New(errors.ValidationErrorCode, "Invalid input")

// Do this:
err := errors.NewValidationError("Email must be a valid email address", "email", nil)
6. Use Standard Error Codes

Use the standard error codes defined in the package:

// Standard error codes
errors.NotFoundCode
errors.InvalidInputCode
errors.DatabaseErrorCode
errors.InternalErrorCode
errors.UnauthorizedCode
errors.ForbiddenCode
errors.ValidationErrorCode
errors.BusinessRuleViolationCode
7. Include Source Information

The error handling system automatically includes source file and line information, which helps with debugging:

// The error will include the source file and line number
err := errors.New(errors.InternalErrorCode, "Something went wrong")

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Overview

Package errors provides a comprehensive error handling system for the application. It includes error codes, HTTP status mapping, contextual information, and utilities for creating, wrapping, and serializing errors.

Index

Constants

View Source
const (
	NotFoundCode              = core.NotFoundCode
	InvalidInputCode          = core.InvalidInputCode
	DatabaseErrorCode         = core.DatabaseErrorCode
	InternalErrorCode         = core.InternalErrorCode
	TimeoutCode               = core.TimeoutCode
	CanceledCode              = core.CanceledCode
	AlreadyExistsCode         = core.AlreadyExistsCode
	UnauthorizedCode          = core.UnauthorizedCode
	ForbiddenCode             = core.ForbiddenCode
	ValidationErrorCode       = core.ValidationErrorCode
	BusinessRuleViolationCode = core.BusinessRuleViolationCode
	ExternalServiceErrorCode  = core.ExternalServiceErrorCode
	NetworkErrorCode          = core.NetworkErrorCode
	ConfigurationErrorCode    = core.ConfigurationErrorCode
	ResourceExhaustedCode     = core.ResourceExhaustedCode
	DataCorruptionCode        = core.DataCorruptionCode
	ConcurrencyErrorCode      = core.ConcurrencyErrorCode
)

Error code constants

Variables

View Source
var (
	ErrNotFound      = core.NewBaseError(NotFoundCode, "resource not found", nil)
	ErrInvalidInput  = core.NewBaseError(InvalidInputCode, "invalid input", nil)
	ErrInternal      = core.NewBaseError(InternalErrorCode, "internal error", nil)
	ErrUnauthorized  = core.NewBaseError(UnauthorizedCode, "unauthorized", nil)
	ErrForbidden     = core.NewBaseError(ForbiddenCode, "forbidden", nil)
	ErrTimeout       = core.NewBaseError(TimeoutCode, "operation timed out", nil)
	ErrCanceled      = core.NewBaseError(CanceledCode, "operation canceled", nil)
	ErrAlreadyExists = core.NewBaseError(AlreadyExistsCode, "resource already exists", nil)
)

Standard errors

Functions

func As

func As(err error, target interface{}) bool

func GetHTTPStatus

func GetHTTPStatus(err error) int

GetHTTPStatus returns the HTTP status code for an error.

func Is

func Is(err, target error) bool

Error checking functions

func IsApplicationError

func IsApplicationError(err error) bool

func IsAuthenticationError added in v1.4.0

func IsAuthenticationError(err error) bool

func IsAuthorizationError added in v1.4.0

func IsAuthorizationError(err error) bool

func IsBusinessRuleError added in v1.4.0

func IsBusinessRuleError(err error) bool

func IsCancelled

func IsCancelled(err error) bool

IsCancelled checks if an error is a cancellation error.

func IsConfigurationError added in v1.4.0

func IsConfigurationError(err error) bool

func IsDatabaseError added in v1.4.0

func IsDatabaseError(err error) bool

func IsDomainError added in v1.4.0

func IsDomainError(err error) bool

Error type checking functions

func IsExternalServiceError added in v1.4.0

func IsExternalServiceError(err error) bool

func IsInfrastructureError added in v1.4.0

func IsInfrastructureError(err error) bool

func IsNetworkError added in v1.4.0

func IsNetworkError(err error) bool

func IsNotFoundError

func IsNotFoundError(err error) bool

func IsTimeout

func IsTimeout(err error) bool

IsTimeout checks if an error is a timeout error.

func IsValidationError

func IsValidationError(err error) bool

func New

func New(code ErrorCode, message string) error

New creates a new BaseError.

func ToJSON

func ToJSON(err error) string

ToJSON converts an error to a JSON string.

func Unwrap

func Unwrap(err error) error

func Wrap

func Wrap(err error, code ErrorCode, message string) error

Wrap wraps an error with additional context.

func WrapWithDetails added in v1.4.0

func WrapWithDetails(err error, code ErrorCode, message string, details map[string]interface{}) error

WrapWithDetails wraps an error with details.

func WrapWithOperation

func WrapWithOperation(err error, code ErrorCode, message string, operation string) error

WrapWithOperation wraps an error with an operation.

Types

type ApplicationError

type ApplicationError = app.ApplicationError

Error type aliases

func NewApplicationError

func NewApplicationError(code ErrorCode, message string, cause error) *ApplicationError

Application error creation functions

type AuthenticationError added in v1.4.0

type AuthenticationError = app.AuthenticationError

Error type aliases

func NewAuthenticationError added in v1.4.0

func NewAuthenticationError(message string, username string, cause error) *AuthenticationError

type AuthorizationError added in v1.4.0

type AuthorizationError = app.AuthorizationError

Error type aliases

func NewAuthorizationError added in v1.4.0

func NewAuthorizationError(message string, username string, resource string, action string, cause error) *AuthorizationError

type BaseError added in v1.4.0

type BaseError = core.BaseError

Error type aliases

type BusinessRuleError added in v1.4.0

type BusinessRuleError = domain.BusinessRuleError

Error type aliases

func NewBusinessRuleError added in v1.4.0

func NewBusinessRuleError(message string, rule string, cause error) *BusinessRuleError

type ConfigurationError added in v1.4.0

type ConfigurationError = app.ConfigurationError

Error type aliases

func NewConfigurationError added in v1.4.0

func NewConfigurationError(message string, configKey string, configValue string, cause error) *ConfigurationError

type DatabaseError added in v1.4.0

type DatabaseError = infra.DatabaseError

Error type aliases

func NewDatabaseError added in v1.4.0

func NewDatabaseError(message string, operation string, table string, cause error) *DatabaseError

type DomainError

type DomainError = domain.DomainError

Error type aliases

func NewDomainError

func NewDomainError(code ErrorCode, message string, cause error) *DomainError

Domain error creation functions

type ErrorCode

type ErrorCode = core.ErrorCode

ErrorCode is an alias for core.ErrorCode

type ExternalServiceError added in v1.4.0

type ExternalServiceError = infra.ExternalServiceError

Error type aliases

func NewExternalServiceError added in v1.4.0

func NewExternalServiceError(message string, serviceName string, endpoint string, cause error) *ExternalServiceError

type InfrastructureError added in v1.4.0

type InfrastructureError = infra.InfrastructureError

Error type aliases

func NewInfrastructureError added in v1.4.0

func NewInfrastructureError(code ErrorCode, message string, cause error) *InfrastructureError

Infrastructure error creation functions

type NetworkError added in v1.4.0

type NetworkError = infra.NetworkError

Error type aliases

func NewNetworkError added in v1.4.0

func NewNetworkError(message string, host string, port string, cause error) *NetworkError

type NotFoundError

type NotFoundError = domain.NotFoundError

Error type aliases

func NewNotFoundError

func NewNotFoundError(resourceType string, resourceID string, cause error) *NotFoundError

type ValidationError

type ValidationError = domain.ValidationError

Error type aliases

func NewValidationError

func NewValidationError(message string, field string, cause error) *ValidationError

type ValidationErrors

type ValidationErrors = domain.ValidationErrors

Error type aliases

func NewValidationErrors

func NewValidationErrors(message string, errors ...*ValidationError) *ValidationErrors

Directories

Path Synopsis
Package app provides application-level error types for the application.
Package app provides application-level error types for the application.
Package core provides the core error handling functionality for the application.
Package core provides the core error handling functionality for the application.
Package domain provides domain-specific error types for the application.
Package domain provides domain-specific error types for the application.
Package http provides HTTP-related error utilities for the application.
Package http provides HTTP-related error utilities for the application.
Package infra provides infrastructure-related error types for the application.
Package infra provides infrastructure-related error types for the application.
Package log provides logging integration for the error handling system.
Package log provides logging integration for the error handling system.
Package metrics provides metrics integration for the error handling system.
Package metrics provides metrics integration for the error handling system.
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.
Package trace provides tracing integration for the error handling system.
Package trace provides tracing integration for the error handling system.
Package types provides specific error types for the errors package.
Package types provides specific error types for the errors package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL