errkit

package
v0.0.0-...-742bdff Latest Latest
Warning

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

Go to latest
Published: Jun 15, 2024 License: MIT Imports: 9 Imported by: 0

README

errkit

why errkit when we already have errorutil ?


Introduced a year ago, errorutil aimed to capture error stacks for identifying deeply nested errors. However, its approach deviates from Go's error handling paradigm. In Go, libraries like "errors", "pkg/errors", and "uber.go/multierr" avoid using the .Error() method directly. Instead, they wrap errors with helper structs that implement specific interfaces, facilitating error chain traversal and the use of helper functions like .Cause() error or .Unwrap() error or errors.Is(). Contrarily, errorutil marshals errors to strings, which is incompatible with Go's error handling paradigm. Over time, the use of errorutil has become cumbersome due to its inability to replace any error package seamlessly and its lack of support for idiomatic error propagation or traversal in Go.

errkit is a new error library that addresses the shortcomings of errorutil. It offers the following features:

  • Seamless replacement for existing error packages, requiring no syntax changes or refactoring:
    • errors package
    • pkg/errors package (now deprecated)
    • uber/multierr package
  • errkit is compatible with all known Go error handling implementations. It can parse errors from any library and works with existing error handling libraries and helper functions like Is(), As(), Cause(), and more.
  • errkit is Go idiomatic and adheres to the Go error handling paradigm.
  • errkit supports attributes for structured error information or logging using slog.Attr (optional).
  • errkit implements and categorizes errors into different kinds, as detailed below.
    • ErrKindNetworkTemporary
    • ErrKindNetworkPermanent
    • ErrKindDeadline
    • Custom kinds via ErrKind interface
  • errkit provides helper functions for structured error logging using SlogAttrs and SlogAttrGroup.
  • errkit offers helper functions to implement public or user-facing errors by using error kinds interface.

Attributes Support

errkit supports optional error wrapping with attributes slog.Attr for structured error logging, providing a more organized approach to error logging than string wrapping.

// normal way of error propogating through nested stack
err := errkit.New("i/o timeout")

// xyz.go
err := errkit.Wrap(err,"failed to connect %s",addr)

// abc.go
err := errkit.Wrap(err,"error occured when downloading %s",xyz)

with attributes support you can do following

// normal way of error propogating through nested stack
err := errkit.New("i/o timeout")

// xyz.go
err = errkit.WithAttr(err,slog.Any("resource",domain))

// abc.go
err = errkit.WithAttr(err,slog.Any("action","download"))

Note

To keep errors concise and avoid unnecessary allocations, message wrapping and attributes count have a max depth set to 3. Adding more will not panic but will be simply ignored. This is configurable using the MAX_ERR_DEPTH env variable (default 3).

Documentation

Overview

errkit implements all errors generated by nuclei and includes error definations specific to nuclei , error classification (like network,logic) etc

Index

Constants

View Source
const (
	// DelimArrow is delim used by projectdiscovery/utils to join errors
	DelimArrow = "<-"
	// DelimArrowSerialized
	DelimArrowSerialized = "\u003c-"
	// DelimSemiColon is standard delim popularly used to join errors
	DelimSemiColon = "; "
	// DelimMultiLine is delim used to join errors in multiline format
	DelimMultiLine = "\n -  "
	// MultiLinePrefix is the prefix used for multiline errors
	MultiLineErrPrefix = "the following errors occurred:"
)

Variables

View Source
var (
	// MaxErrorDepth is the maximum depth of errors to be unwrapped or maintained
	// all errors beyond this depth will be ignored
	MaxErrorDepth = env.GetEnvOrDefault("MAX_ERROR_DEPTH", 3)
	// ErrorSeperator is the seperator used to join errors
	ErrorSeperator = env.GetEnvOrDefault("ERROR_SEPERATOR", "; ")
)
View Source
var (
	// ErrClassNetwork indicates an error related to network operations
	// these may be resolved by retrying the operation with exponential backoff
	// ex: Timeout awaiting headers, i/o timeout etc
	ErrKindNetworkTemporary = NewPrimitiveErrKind("network-temporary-error", "temporary network error", isNetworkTemporaryErr)
	// ErrKindNetworkPermanent indicates a permanent error related to network operations
	// these may not be resolved by retrying and need manual intervention
	// ex: no address found for host
	ErrKindNetworkPermanent = NewPrimitiveErrKind("network-permanent-error", "permanent network error", isNetworkPermanentErr)
	// ErrKindDeadline indicates a timeout error in logical operations
	// these are custom deadlines set by nuclei itself to prevent infinite hangs
	// and in most cases are server side issues (ex: server connects but does not respond at all)
	// a manual intervention is required
	ErrKindDeadline = NewPrimitiveErrKind("deadline-error", "deadline error", isDeadlineErr)
	// ErrKindUnknown indicates an unknown error class
	// that has not been implemented yet this is used as fallback when converting a slog Item
	ErrKindUnknown = NewPrimitiveErrKind("unknown-error", "unknown error", nil)
)
View Source
var (
	// DefaultErrorKinds is the default error kinds used in classification
	// if one intends to add more default error kinds it must be done in init() function
	// of that package to avoid race conditions
	DefaultErrorKinds = []ErrKind{
		ErrKindNetworkTemporary,
		ErrKindNetworkPermanent,
		ErrKindDeadline,
	}
)

Functions

func Append

func Append(errs ...error) error

Append appends given errors and returns a new error it ignores all nil errors

func As

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

Proxy to StdLib errors.As

func Cause

func Cause(err error) error

Cause returns the original error that caused this error

func Combine

func Combine(errs ...error) error

Combine combines multiple errors into a single error

func Errors

func Errors(err error) []error

Errors returns all underlying errors there were appended or joined

func GetAttr

func GetAttr(err error) []slog.Attr

GetAttr returns all attributes of given error if it has any

func GetAttrValue

func GetAttrValue(err error, key string) slog.Value

GetAttrValue returns the value of the attribute with given key

func Is

func Is(err error, target ...error) bool

Proxy to StdLib errors.Is

func IsDeadlineErr

func IsDeadlineErr(err error) bool

IsDeadlineErr checks if given error is a deadline error

func IsKind

func IsKind(err error, match ...ErrKind) bool

IsKind checks if given error is equal to one of the given errkind if error did not already have a kind, it tries to parse it using default error kinds and given kinds

func IsNetworkPermanentErr

func IsNetworkPermanentErr(err error) bool

IsNetworkPermanentErr checks if given error is a permanent network error

func IsNetworkTemporaryErr

func IsNetworkTemporaryErr(err error) bool

IsNetworkTemporaryErr checks if given error is a temporary network error

func Join

func Join(errs ...error) error

Join joins given errors and returns a new error it ignores all nil errors Note: unlike Other libraries, Join does not use `\n` so it is equivalent to wrapping/Appending errors

func ToSlogAttrGroup

func ToSlogAttrGroup(err error) slog.Attr

ToSlogAttrGroup returns a slog attribute group for the given error it is in format of:

{
	"data": {
		"kind": "<error-kind>",
		"cause": "<cause>",
		"errors": [
			<errs>...
		]
	}
}

func ToSlogAttrs

func ToSlogAttrs(err error) []slog.Attr

ToSlogAttrs returns slog attributes for the given error it is in format of:

{
	"kind": "<error-kind>",
	"cause": "<cause>",
	"errors": [
		<errs>...
	]
}

func WithAttr

func WithAttr(err error, attrs ...slog.Attr) error

WithAttr wraps error with given attributes

err = errkit.WithAttr(err,slog.Any("resource",domain))

func WithMessage

func WithMessage(err error, message string) error

WithMessage

func WithMessagef

func WithMessagef(err error, format string, args ...interface{}) error

WithMessagef

func Wrap

func Wrap(err error, message string) error

Wrap wraps the given error with the message

func Wrapf

func Wrapf(err error, format string, args ...interface{}) error

Wrapf wraps the given error with the message

Types

type CauseError

type CauseError interface {
	// Cause return the original error that caused this without any wrapping
	Cause() error
}

CauseError is implemented by errors that have a cause

type ComparableError

type ComparableError interface {
	// Is checks if current error contains given error
	Is(err error) bool
}

ComparableError is implemented by errors that can be compared

type ErrKind

type ErrKind interface {
	// Is checks if current error kind is same as given error kind
	Is(ErrKind) bool
	// IsParent checks if current error kind is parent of given error kind
	// this allows heirarchical classification of errors and app specific handling
	IsParent(ErrKind) bool
	// RepresentsError checks if given error is of this kind
	Represents(*ErrorX) bool
	// Description returns predefined description of the error kind
	// this can be used to show user friendly error messages in case of error
	Description() string
	// String returns the string representation of the error kind
	String() string
}

ErrKind is an interface that represents a kind of error

func CombineErrKinds

func CombineErrKinds(kind ...ErrKind) ErrKind

CombineErrKinds combines multiple error kinds into a single error kind this is not recommended but available if needed It is currently used in ErrorX while printing the error It is recommended to implement a hierarchical error kind instead of using this outside of errkit

func GetAllErrorKinds

func GetAllErrorKinds(err error, defs ...ErrKind) []ErrKind

GetAllErrorKinds returns all error kinds from the error this should not be used unless very good reason to do so

func GetErrorKind

func GetErrorKind(err error, defs ...ErrKind) ErrKind

GetErrorKind returns the first error kind from the error extra error kinds can be passed as optional arguments

func NewPrimitiveErrKind

func NewPrimitiveErrKind(id string, info string, represents func(*ErrorX) bool) ErrKind

NewPrimitiveErrKind creates a new primitive error kind

type ErrorX

type ErrorX struct {
	// contains filtered or unexported fields
}

ErrorX is a custom error type that can handle all known types of errors wrapping and joining strategies including custom ones and it supports error class which can be shown to client/users in more meaningful way

func FromError

func FromError(err error) *ErrorX

FromError parses a given error to understand the error class and optionally adds given message for more info

func New

func New(format string, args ...interface{}) *ErrorX

New creates a new error with the given message

func (*ErrorX) Attrs

func (e *ErrorX) Attrs() []slog.Attr

Attrs returns all attributes associated with the error

func (*ErrorX) Build

func (e *ErrorX) Build() error

Build returns the object as error interface

func (*ErrorX) Cause

func (e *ErrorX) Cause() error

Cause return the original error that caused this without any wrapping

func (*ErrorX) Error

func (e *ErrorX) Error() string

Error returns the error string

func (*ErrorX) Errors

func (e *ErrorX) Errors() []error

Errors returns all errors parsed by the error

func (*ErrorX) Is

func (e *ErrorX) Is(err error) bool

Is checks if current error contains given error

func (*ErrorX) Kind

func (e *ErrorX) Kind() ErrKind

Kind returns the errorkind associated with this error if any

func (ErrorX) MarshalJSON

func (e ErrorX) MarshalJSON() ([]byte, error)

func (*ErrorX) Msgf

func (e *ErrorX) Msgf(format string, args ...interface{})

Msgf adds a message to the error

func (*ErrorX) ResetKind

func (e *ErrorX) ResetKind() *ErrorX

func (*ErrorX) SetAttr

func (e *ErrorX) SetAttr(s ...slog.Attr) *ErrorX

SetAttr sets additional attributes to a given error it only adds unique attributes and ignores duplicates Note: only key is checked for uniqueness

func (*ErrorX) SetKind

func (e *ErrorX) SetKind(kind ErrKind) *ErrorX

SetClass sets the class of the error if underlying error class was already set, then it is given preference when generating final error msg

func (*ErrorX) Unwrap

func (e *ErrorX) Unwrap() []error

Unwrap returns the underlying error

type JoinedError

type JoinedError interface {
	// Unwrap returns the underlying error
	Unwrap() []error
}

JoinedError is implemented by errors that are joined by Join

type WrappedError

type WrappedError interface {
	// Unwrap returns the underlying error
	Unwrap() error
}

WrappedError is implemented by errors that are wrapped

Jump to

Keyboard shortcuts

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