errx

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Dec 15, 2024 License: MIT Imports: 7 Imported by: 19

README

errx: Advanced Error Handling for Go

errx is a flexible error handling package designed to provide structured, extensible, and developer-friendly error management in Go projects. It extends Go's built-in error interface with additional methods, supports gRPC integration, and includes utilities for detailed error tracing and contextual debugging.

Features

  • Extended Error Interface: Add structured details, validation fields, and trace information to errors.
  • gRPC Compatibility: Convert errors to/from gRPC status codes seamlessly.
  • Error Tracing: Automatically track the origin and flow of errors through the system.
  • Customizable Options: Use functional options to customize errors on creation or wrapping.
  • Rich Metadata: Attach contextual information and validation fields for debugging and logging.
  • Integration Utilities: Utilities for extracting or converting errors with functions like AsErrorX, GetCode, and GetType.

Installation

go get github.com/code19m/errx

Usage

0. Helper function for use in examples
func logError(err error) {
	if err == nil {
		return
	}
	e := errx.AsErrorX(err)

	logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{}))
	logger.Error("Error occurred",
		slog.String("err_code", e.Code()),
		slog.String("err_type", e.Type().String()),
		slog.String("err_message", e.Error()),
		slog.String("err_trace", e.Trace()),
		slog.Any("err_fields", e.Fields()),
		slog.Any("err_details", e.Details()),
	)
}
1. Creating Custom Errors
package main

import (
	"log/slog"
	"os"

	"github.com/code19m/errx"
)

func main() {
	err := errx.New("Resource not found",
		errx.WithCode("NOT_FOUND"),                     // Set error code
		errx.WithType(errx.T_NotFound),                 // Set error type
		errx.WithDetails(errx.M{"resource_id": "123"}), // Add additional details
		errx.WithFields(errx.M{"username": "invalid"}), // Add validation fields
	)

	logError(err)
}

Output

{
  "time": "2024-12-08T14:36:34.715364+05:00",
  "level": "ERROR",
  "msg": "Error occurred",
  "err_code": "NOT_FOUND",
  "err_type": "T_NotFound",
  "err_message": "[T_NotFound: NOT_FOUND] - Resource not found",
  "err_trace": "[main.go:28] main.main",
  "err_fields": {
    "username": "invalid"
  },
  "err_details": {
    "resource_id": "123"
  }
}

2. Wrapping Existing Errors
package main

import (
	"errors"
	"log/slog"
	"os"

	"github.com/code19m/errx"
)

func main() {
	baseErr := errors.New("database connection failed")
	err := errx.Wrap(
		baseErr,
		errx.WithDetails(errx.M{
			"host": "localhost",
			"port": "5432",
		}),
	)

	logError(err)
}

Output

{
  "time": "2024-12-08T14:42:44.488966+05:00",
  "level": "ERROR",
  "msg": "Error occurred",
  "err_code": "INTERNAL",
  "err_type": "T_Internal",
  "err_message": "[T_Internal: INTERNAL] - database connection failed",
  "err_trace": "[main.go:30] main.main",
  "err_fields": null,
  "err_details": {
    "host": "localhost",
    "port": "5432"
  }
}

3. Error trace information
package main

import (
	"log/slog"
	"os"

	"github.com/code19m/errx"
)

func main() {
	err := errx.Wrap(firstFunc())

	logError(err)
}

func firstFunc() error {
	return errx.Wrap(secondFunc())
}

func secondFunc() error {
	return errx.New("some error occurred")
}

Output

{
  "time": "2024-12-08T14:49:21.362634+05:00",
  "level": "ERROR",
  "msg": "Error occurred",
  "err_code": "INTERNAL",
  "err_type": "T_Internal",
  "err_message": "[T_Internal: INTERNAL] - some error occurred",
  "err_trace": "[main.go:28] main.main ➡️ [main.go:34] main.firstFunc ➡️ [main.go:38] main.secondFunc",
  "err_fields": null,
  "err_details": null
}

Error Types

The package defines several error types for categorizing errors:

Type Description
T_Internal Internal server errors
T_Validation Input validation errors
T_NotFound Resource not found errors
T_Conflict Conflicting resource errors
T_Authentication Authentication-related errors
T_Forbidden Permission-related errors

Functional Options

Option Description
WithCode Sets a machine-readable error code
WithType Sets the error type
WithPrefix Adds a prefix to trace and details
WithDetails Adds debugging details
WithFields Sets validation-related fields

Testing

Unit tests cover the package functionality. To run tests:

go test ./...

Contributing

Contributions are welcome! Feel free to open issues or submit pull requests.

License

This package is licensed under the MIT License. See the LICENSE file for details.

Documentation

Index

Constants

View Source
const (
	// DefaultCode is the default error code used when no code is provided.
	DefaultCode = "INTERNAL"

	// DefaultType is the default error type used when no type is provided.
	DefaultType = T_Internal
)

Variables

This section is empty.

Functions

func FromGRPCError

func FromGRPCError(err error, opts ...OptionFunc) error

FromGRPCError converts a gRPC error into a custom error (ErrorX).

It is intended for use in gRPC client side after making gRPC calls to convert gRPC status errors to ErrorX instances. If the error is nil, no action is taken, so it is safe to call this function with a nil error.

If the provided error does not contain an ErrorX detail, a default ErrorX instance is created. Optional modifications can be applied via OptionFunc.

***NOTE***: Don't confuse this function with ToGRPCError, which is intended for use in gRPC server side.

func GetCode

func GetCode(err error) string

GetCode returns the error code if the error implements the ErrorX interface. Otherwise, it returns the default code.

func New

func New(msg string, opts ...OptionFunc) error

New creates a new ErrorX with the given message and options.

func ToGRPCError

func ToGRPCError(err error, opts ...OptionFunc) error

ToGRPCError converts a custom error (ErrorX) into a gRPC-compatible error.

It is intended for use in gRPC server handlers/interceptors to convert ErrorX instances to gRPC status errors. If the error is nil, no action is taken, so it is safe to call this function with a nil error.

If the provided error does not implement the ErrorX interface, it is wrapped into a default ErrorX instance.

Optional modifications can be applied via OptionFunc.

***NOTE***: Don't confuse this function with FromGRPCError, which is intended for use in gRPC client side.

func Wrap

func Wrap(err error, opts ...OptionFunc) error

Wrap wraps an error in an errorX instance with the given options.

This function serves as a convenience wrapper around New, enriching the error with additional information and a trace.

It is designed to be used in the middle layers of an application.

Types

type ErrorX

type ErrorX interface {

	// Error returns a human-readable description of the error.
	// It implements the standard error interface.
	Error() string

	// Code returns a machine-readable error code.
	// This is intended for use in application logic.
	Code() string

	// Type returns the type of the error.
	// Useful for categorizing errors during error handling.
	Type() Type

	// Trace returns the error's trace information.
	// This can help identify the error's origin in the system.
	Trace() string

	// Fields provides information about input validation errors.
	// Example: {"field_name": "error_message/validation_rule"}
	// Not to be confused with Details, which is used for debugging.
	Fields() M

	// Details provides additional debugging information about the error.
	// This is intended for logging and troubleshooting purposes.
	Details() M

	// Is methos implements the standard errors.Is function.
	// It reports whether any error in the error's tree matches the target.
	Is(target error) bool
}

ErrorX represents a main interface of this package. It extends the built-in error interface with additional methods to provide structured error information and facilitate debugging.

func AsErrorX

func AsErrorX(err error) ErrorX

AsErrorX returns the error as an ErrorX instance.

If the error does not implement the ErrorX interface, it converts it to an ErrorX with default values.

This function is useful when you want to work with ErrorX instances.

***NOTE***: Make sure that error is not nil before calling this function.

type M

type M map[string]string

M is a shorthand for a map of string key-value pairs.

type OptionFunc

type OptionFunc func(*errorX)

OptionFunc is a function that modifies an errorX.

func WithCode

func WithCode(code string) OptionFunc

WithCode sets the error code. If this option is not used, the default code is "INTERNAL".

func WithDetails

func WithDetails(details M) OptionFunc

WithDetails adds additional contextual information (metadata) to the error. If a key already exists, the new value is appended to the existing value, with the new value appearing first, separated by a "|" character.

func WithFields

func WithFields(fields M) OptionFunc

WithFields sets specific validation related fields. Unlike WithDetails, this method does not append but completely overwrites the existing fields.

Example of fields: {"username": "too short", "email": "invalid format"}

func WithPrefix

func WithPrefix(prefix string) OptionFunc

WithPrefix adds a prefix to the trace and all keys in the error's details, specifically designed for error propagation between microservices, particularly in gRPC communication.

The trace is changed in the format ">>> prefix >>> %s". The details keys are changed in the format "prefix.%s".

func WithType

func WithType(t Type) OptionFunc

WithType sets the error type. If this option is not used, the default type is T_Internal.

type Type

type Type uint8

Type defines the different categories of errors that can be represented by an ErrorX.

const (
	// Internal errors indicate unexpected issues within the application.
	T_Internal Type = iota

	// Validation errors occur when user input does not meet expected criteria.
	T_Validation

	// NotFound errors are returned when a requested resource cannot be located.
	T_NotFound

	// Conflict errors occur when a resource already exists.
	T_Conflict

	// Authentication errors occur when a user is not authorized to access a resource.
	T_Authentication

	// Forbidden errors occur when a user is not allowed to access a resource.
	T_Forbidden
)

func GetType

func GetType(err error) Type

GetType returns the error type if the error implements the ErrorX interface. Otherwise, it returns the default type.

func (Type) String

func (t Type) String() string

String returns a string representation of the error type.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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