irr

package module
v0.0.0-...-c70ba75 Latest Latest
Warning

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

Go to latest
Published: Mar 9, 2024 License: MIT Imports: 7 Imported by: 32

README

irr

Build Status codecov

Irr is an error library based on the handling stack. It supports error wrapping, handling-stack tracing, and error stack traversal.

Table of Contents

Overview

irr provides

  • Error wrapping
  • Error link traversal
  • Optional stack tracing

Concept

Irr is more concerned with the error handling-stack than the usual call stack.

The handling-stack is somewhat similar to the function call stack, but it is more reflective of the relationship between the flow before and after the exception handling than the function call relationship. Therefore, it is more reflective of the actual handling of the logical function library.

For example, when function A calls function B on line $l_a$, and an error is generated on line $l_b$ of function B. The usual error tracing, gives a tuple of $<A,l_a>$ $<B, l_b>$

In fact, the exception errB returned by B is often not handled in $l_a$, but is distributed to some subsequent logic, or even to other sub-functions. Therefore, the function call stack can only focus on the generation of exceptions, but in many cases, we need to focus on the whole logic chain from the generation of exceptions to their final processing.

Handling-stack is usually done via wrap. In irr handling, the advantages of stack trace and error wrap are combined, so that there is trace information for both exceptions and handling sessions. This makes it easy to trace the call chain.

To avoid redundant information and unnecessary performance overhead, irr does not advocate exporting the call stack in non-error situations, and therefore only provides methods related to error handling. In addition, irr advocates that developers should handle exceptions clearly, so it provides methods to skip the stack frame, or just warp without outputting the stack, to serve developers with better error handling practices.

Usage

Import package
import (
    "github.com/khicago/irr"
)
Basic Usage

Create an error

err := irr.Error("this is a new error")
errWithParam :=  irr.Error("this is a new error with integer %d", 1)

if you print them, you will got

fmt.Println(err)
fmt.Println(errWithParam)
// Output:
// this is a new error
// this is a new error with integer 1

Or you can easilly wrap an error

wrappedErr := irr.Wrap(err, "some wrap information")
wrappedErrWithParam := irr.Wrap(err, "some wrap information with integer %d", 1)

fmt.Println(wrappedErr)
fmt.Println(wrappedErrWithParam)
// when err := fmt.Errorf("default err message"), the outputs will be
// Output:
// some wrap information; default err message
// some wrap information with integer 1; default err message

and you can define the output format by yourself

fmt.Println(wrappedErr.ToString(false, " ==> "))
fmt.Println(wrappedErrWithParam.ToString(false, " ==> "))
// Output:
// some wrap information ==> default err message
// some wrap information with integer 1 ==> default err message
Work with errors' stack trace

Create an error with stack trace

err := irr.Trace("this is a new error")
errWithParam :=  irr.Trace("this is a new error with integer %d", 1)

By default, the trace info will not be print by Error() method ToString method can be used to print trace info

fmt.Println(err.ToString(true, ""))
fmt.Println(errWithParam.ToString(true, ""))
// this is a new error your_function@/.../your_code.go:line
// this is a new error with integer 1 your_function@/.../your_code.go:line

You can also easilly wrap an error with stack trace

wrappedErr := irr.Track(err, "some wrap information")
wrappedErrWithParam := irr.Track(err, "some wrap information with integer %d", 1)

The result can be exported in the same way, and you can set the splitor of each stack.

fmt.Println(wrappedErr.ToString(true, " && "))
// some wrap information your_outer_function@/.../your_outer_code.go:line && this is a new error your_function@/.../your_code.go:line
fmt.Println(wrappedErrWithParam.ToString(true, "\n"))
// some wrap information with integer 1 your_outer_function@/.../your_outer_code.go:line
// this is a new error your_function@/.../your_code.go:line
Unwrap

Irr provides several methods for users to query several key points in the error chain.

IRR interface {
    Root() error
    Source() error
    Unwrap() error
}

The Unwrap() method, which returns the error directly wrapped by the current irr. It supports the Unwrap interface, so irr is fully compatible with the wrap logic of the errors library. For example, you can use errors.Is to determine if the error type expected.

The Root() method will return the final error, and if an error wrapped an error with irr, which is another wrapped error that supports Unwrap interface (such as an error created by %w), it will continue to search until it finds the first error that cannot be unwrapped.

The Source() method returns only the first error that is generally not an irr wrap, which is usually the one we are most concerned about.

In practice, we focus on Source, because it is usually the error returned by some underlying call. The root, on the other hand, can be used to generate error tags.

Logging shortcuts

Irr interface supports some logging shortcuts, by injecting logger, you can easily warp errors in the final processing exit and enter error, warning, fatal level logs. When using these log shortcuts, trace information will be printed by default.

IRR interface {
    ...
    LogWarn(logger interface{ Warn(args ...interface{}) }) IRR
    LogError(logger interface{ Error(args ...interface{}) }) IRR
    LogFatal(logger interface{ Fatal(args ...interface{}) }) IRR
    ...
}
Traverse, and it's panic handling

Irr provides two traverse method

IRR interface {
    ...
    TraverseToSource(fn func(err error, isSource bool) error) (err error)
    TraverseToRoot(fn func(err error) error) (err error)
    ...
}

Author

kinghand@foxmail.com

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrUntypedExecutionFailure = errors.New("!!!panic")

Functions

func CatchFailure

func CatchFailure(set func(err error))

CatchFailure is used to catch and handle panics within a function, preventing them from causing the program to crash while unifying the encapsulation of non-error information. It is declared at the beginning of a function with the defer keyword, ensuring that any panic during function execution can be caught. This function takes a callback function as a parameter, which is called when a panic occurs to handle the recovered error.

Usage example:

// A sample function that may cause panic

func riskyOperation() (err error) {
    // Defer calling CatchFailure at the start of riskyOperation
    // to ensure any subsequent panics can be caught and handled
    defer irr.CatchFailure(func(e error) {
        // Convert the recovered panic into a regular error so the function can return it
        // err can be set as a side effect, or the caught e can be handled directly (e.g., logging)
        // If the panic parameter is nil, e will be nil
        // If the panic is triggered with an error, the corresponding err will be passed directly
        // If the panic is another value, ErrUntypedExecutionFailure will be passed in, with the panic value attached to the error message
        err = e
    })

    // Trigger an out-of-bounds error that will cause a panic
    _ = make([]int, 0)[1]
    // Due to the panic above, the following code will not execute
    fmt.Println("This line of code will not be executed.")

    // If there is no panic, the function will return a nil error
    return nil
}

// Calling riskyOperation elsewhere, handling errors returned by it

func main() {
    if err := riskyOperation(); err != nil {
        fmt.Printf("Caught error: %v\n", err)
    } else {
        fmt.Println("Operation successful, no errors occurred")
    }
}

Note: CatchFailure should only be used to deal with panics caused by unforeseen situations, while regular error handling should be done using the error.

Types

type BasicIrr

type BasicIrr struct {
	Code  int64      `json:"code"`
	Tags  []string   `json:"tags"`
	Msg   string     `json:"msg"`
	Trace *traceInfo `json:"trace"`
	// contains filtered or unexported fields
}

func (*BasicIrr) ClosestCode

func (ir *BasicIrr) ClosestCode() (val int64)

ClosestCode the implementation of ITraverseCoder[int64]

func (*BasicIrr) Error

func (ir *BasicIrr) Error() string

Error the implementation of error

func (*BasicIrr) GetCode

func (ir *BasicIrr) GetCode() (val int64)

GetCode the implementation of ICoder[int64]

func (*BasicIrr) GetCodeStr

func (ir *BasicIrr) GetCodeStr() string

GetCodeStr Determines how the code is written to the message, so that this method can input an empty string to avoid outputting the code in the message

func (*BasicIrr) GetTag

func (ir *BasicIrr) GetTag(key string) (val []string)

GetTag the implementation of ITagger

func (*BasicIrr) GetTraceInfo

func (ir *BasicIrr) GetTraceInfo() *traceInfo

func (*BasicIrr) LogError

func (ir *BasicIrr) LogError(logger IErrorLogger) IRR

LogError the implementation of ILogCaller

func (*BasicIrr) LogFatal

func (ir *BasicIrr) LogFatal(logger IFatalLogger) IRR

LogFatal the implementation of ILogCaller

func (*BasicIrr) LogWarn

func (ir *BasicIrr) LogWarn(logger IWarnLogger) IRR

LogWarn the implementation of ILogCaller

func (*BasicIrr) Root

func (ir *BasicIrr) Root() error

Root the implementation of ITraverseError

func (*BasicIrr) SetCode

func (ir *BasicIrr) SetCode(val int64) IRR

SetCode the implementation of ICoder[int64]

func (*BasicIrr) SetTag

func (ir *BasicIrr) SetTag(key, val string)

SetTag the implementation of ITagger

func (*BasicIrr) Source

func (ir *BasicIrr) Source() (err error)

Source the implementation of ITraverseIrr

func (*BasicIrr) ToString

func (ir *BasicIrr) ToString(printTrace bool, split string) string

ToString consecutive equal codes will be printed only once during the traceback process

func (*BasicIrr) TraverseCode

func (ir *BasicIrr) TraverseCode(fn func(err error, code int64) error) (err error)

TraverseCode the implementation of ITraverseCoder[int64]

func (*BasicIrr) TraverseToRoot

func (ir *BasicIrr) TraverseToRoot(fn func(err error) error) (err error)

TraverseToRoot the implementation of ITraverseError

func (*BasicIrr) TraverseToSource

func (ir *BasicIrr) TraverseToSource(fn func(err error, isSource bool) error) (err error)

TraverseToSource the implementation of ITraverseIrr

func (*BasicIrr) Unwrap

func (ir *BasicIrr) Unwrap() error

Unwrap the implementation of IUnwrap

type ICodeGetter

type ICodeGetter[TCode any] interface {
	GetCode() (val TCode)
	GetCodeStr() string
}

type ICoder

type ICoder[TCode any] interface {
	ICodeGetter[TCode]
	SetCode(val TCode) IRR
}

type IErrorLogger

type IErrorLogger interface{ Error(args ...any) }

type IFatalLogger

type IFatalLogger interface{ Fatal(args ...any) }

type ILogCaller

type ILogCaller interface {
	LogWarn(logger IWarnLogger) IRR
	LogError(logger IErrorLogger) IRR
	LogFatal(logger IFatalLogger) IRR
}

type IRR

type IRR interface {
	ITraverseIrr

	error
	ITraverseError
	IUnwrap

	ICoder[int64]
	ITraverseCoder[int64]

	ITagger
	ILogCaller

	ToString(printTrace bool, split string) string
	GetTraceInfo() *traceInfo
}

func Error

func Error(formatOrMsg string, args ...any) IRR

Error creates a new IRR error object with a formatted message. formatOrMsg is a string that accepts printf style format specifiers. args are variadic parameters that represent the arguments for the formatting string.

Example
package main

import (
	"fmt"

	"github.com/khicago/irr"
)

func main() {
	err := irr.Error("this is a new error")
	errWithParam := irr.Error("this is a new error with integer %d", 1)

	fmt.Println(err)
	fmt.Println(errWithParam)
}
Output:

this is a new error
this is a new error with integer 1

func ErrorC

func ErrorC[T int64](code T, formatOrMsg string, args ...any) IRR

ErrorC creates a new IRR error object with an error code and a formatted message. code is an int64 type error code used to identify and classify errors. formatOrMsg is a string that accepts printf style format specifiers to generate the error message. args are variadic parameters that represent the arguments for the formatting string. It returns an IRR error object set with the specific error code.

Usage example:

// Define a sample error code const ErrCodeInvalidInput int64 = 1001

// ValidateInput checks the input string and returns an error if it is empty

func ValidateInput(input string) error {
    if input == "" {
        // Create an error object with a specific error code and formatted message using ErrorC
        return irr.ErrorC(ErrCodeInvalidInput, "validation failed: %s", "input cannot be empty")
    }
    // Other input validation logic...
    return nil
}

Note: ErrorC is typically used when you wish to categorize errors or define specific status codes for easier error handling and response later on.

func Trace

func Trace(formatOrMsg string, args ...any) IRR

Trace creates an error object with stack trace and a formatted message. formatOrMsg is a string that accepts printf style format specifiers. args are variadic parameters that represent the arguments for the formatting string. It defaults to skipping one call frame, usually the place where Trace is called.

Example
package main

import (
	"fmt"

	"github.com/khicago/irr"
)

func main() {
	err := irr.Trace("this is a new error")
	errWithParam := irr.Trace("this is a new error with integer %d", 1)

	fmt.Println(err.ToString(true, ""))
	fmt.Println(errWithParam.ToString(true, ""))

	wrappedErr := irr.Track(err, "some wrap information")
	wrappedErrWithParam := irr.Track(err, "some wrap information with integer %d", 1)

	fmt.Println(wrappedErr.ToString(true, " && "))
	fmt.Println(wrappedErrWithParam.ToString(true, "\n"))
}
Output:

func TraceSkip

func TraceSkip(skip int, formatOrMsg string, args ...any) IRR

TraceSkip creates an error object with stack trace and formatted message, skipping a certain number of stack frames. skip indicates the number of call frames to skip in the stack trace. formatOrMsg is a string that accepts printf style format specifiers. args are variadic parameters that represent the arguments for the formatting string.

func Track

func Track(innerErr error, formatOrMsg string, args ...any) IRR

Track creates an error object with a stack trace and wraps an inner error. innerErr is the error being wrapped. formatOrMsg is a string that accepts printf style format specifiers. args are variadic parameters that represent the arguments for the formatting string. It defaults to skipping one call frame, starting the trace where Track is called.

func TrackSkip

func TrackSkip(skip int, innerErr error, formatOrMsg string, args ...any) IRR

TrackSkip creates an error object with a stack trace and wraps an inner error, skipping a specified number of stack frames. skip indicates the number of call frames to skip in the stack trace. innerErr is the error being wrapped. formatOrMsg is a string that accepts printf style format specifiers. args are variadic parameters that represent the arguments for the formatting string.

func Wrap

func Wrap(innerErr error, formatOrMsg string, args ...any) IRR

Wrap wraps an existing error object with a given message and an inner error. innerErr is the error being wrapped. formatOrMsg is a string that accepts printf style format specifiers. args are variadic parameters that represent the arguments for the formatting string.

Example
package main

import (
	"fmt"

	"github.com/khicago/irr"
)

func main() {
	err := fmt.Errorf("default err message")
	wrappedErr := irr.Wrap(err, "some wrap information")
	wrappedErrWithParam := irr.Wrap(err, "some wrap information with integer %d", 1)

	fmt.Println(wrappedErr)
	fmt.Println(wrappedErrWithParam)
}
Output:

some wrap information, default err message
some wrap information with integer 1, default err message

type ITagger

type ITagger interface {
	SetTag(key, value string)
	GetTag(key string) (values []string)
}

type ITraverseCoder

type ITraverseCoder[TCode any] interface {
	ClosestCode() TCode
	TraverseCode(fn func(err error, code TCode) error) (err error)
	ICodeGetter[TCode]
}

type ITraverseError

type ITraverseError interface {
	Root() error
	TraverseToRoot(fn func(err error) error) (err error)
}

type ITraverseIrr

type ITraverseIrr interface {
	Source() error
	TraverseToSource(fn func(err error, isSource bool) error) (err error)
}

type IUnwrap

type IUnwrap interface {
	Unwrap() error
}

type IWarnLogger

type IWarnLogger interface{ Warn(args ...any) }

type Spawner

type Spawner interface {
	Error(formatOrMsg string, args ...interface{}) IRR
	Wrap(innerErr error, formatOrMsg string, args ...interface{}) IRR
	TraceSkip(skip int, formatOrMsg string, args ...interface{}) IRR
	Trace(formatOrMsg string, args ...interface{}) IRR
	TrackSkip(skip int, innerErr error, formatOrMsg string, args ...interface{}) IRR
	Track(innerErr error, formatOrMsg string, args ...interface{}) IRR
}

Directories

Path Synopsis
Package irc provides a customized error code system extending the IRR library.
Package irc provides a customized error code system extending the IRR library.

Jump to

Keyboard shortcuts

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