Documentation
¶
Overview ¶
Package xerr provides different error functionalities, like an error enriched with stack trace, and a MultiError.
Index ¶
- func AllowFrame(_, _ string) bool
- func Errorf(format string, args ...interface{}) error
- func New(msg string) error
- func NoDomainFunctionName(fnName string) string
- func OnlyFunctionName(fnName string) string
- func SetFrameFnNameProcessor(fn FrameFnNameProcessor)
- func SetSkipFrame(fn SkipFrame)
- func ShortFunctionName(fnName string) string
- func Wrap(err error, msg string) error
- func Wrapf(err error, format string, args ...interface{}) error
- type FrameFnNameProcessor
- type MultiError
- func (mErr *MultiError) Add(errs ...error) *MultiError
- func (mErr *MultiError) AddOnce(errs ...error) *MultiError
- func (mErr *MultiError) As(target interface{}) bool
- func (mErr *MultiError) ErrOrNil() error
- func (mErr *MultiError) Error() string
- func (mErr *MultiError) Errors() []error
- func (mErr *MultiError) Format(f fmt.State, verb rune)
- func (mErr *MultiError) Is(target error) bool
- func (mErr *MultiError) Reset()
- func (mErr *MultiError) Unwrap() error
- type SkipFrame
- type SkipFrameChain
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AllowFrame ¶
AllowFrame is a SkipFrame which whitelists any given frame. It can be used as the default/first SkipFrame in a chained responsibility configuration.
Example:
xerr.SetSkipFrame(SkipFoo(SkipBar(xerr.AllowFrame)))
func Errorf ¶
Errorf formats according to a format specifier and returns the string as a value that satisfies error. Errorf also records the stack trace at the point it was called.
Example ¶
package main
import (
"fmt"
"github.com/actforgood/xerr"
)
func main() {
err := xerr.Errorf("something went bad with %s", "this example")
fmt.Println(err.Error())
// Example output:
// something went bad with this example
fmt.Printf("%+v\n", err)
// Example output:
// something went bad with this example
// github.com/actforgood/xerr_test.ExampleErrorf
// /Users/bogdan/work/go/xerr/example_test.go:44
// testing.runExample
// /usr/local/go/src/testing/run_example.go:63
// testing.runExamples
// /usr/local/go/src/testing/example.go:44
// testing.(*M).Run
// /usr/local/go/src/testing/testing.go:1418
// main.main
// _testmain.go:83
// runtime.main
// /usr/local/go/src/runtime/proc.go:225
// runtime.goexit
// /usr/local/go/src/runtime/asm_amd64.s:1371
}
func New ¶
New returns an error with the supplied message. New also records the stack trace at the point it was called.
Example ¶
package main
import (
"fmt"
"github.com/actforgood/xerr"
)
func main() {
err := xerr.New("something went bad")
fmt.Println(err.Error())
// Example output:
// something went bad
fmt.Printf("%+v\n", err)
// Example output:
// something went bad
// github.com/actforgood/xerr_test.ExampleNew
// /Users/bogdan/work/go/xerr/example_test.go:15
// testing.runExample
// /usr/local/go/src/testing/run_example.go:63
// testing.runExamples
// /usr/local/go/src/testing/example.go:44
// testing.(*M).Run
// /usr/local/go/src/testing/testing.go:1418
// main.main
// _testmain.go:79
// runtime.main
// /usr/local/go/src/runtime/proc.go:225
// runtime.goexit
// /usr/local/go/src/runtime/asm_amd64.s:1371
}
func NoDomainFunctionName ¶
NoDomainFunctionName is a FrameFnNameProcessor which removes the first part (which is usually a domain) from fully qualified package name. Example: "github.com/actforgood/xerr_test.TestX" => "actforgood/xerr_test.TestX" .
func OnlyFunctionName ¶
OnlyFunctionName is a FrameFnNameProcessor which returns only the function name, removing the package part. Example: "github.com/actforgood/xerr_test.TestX" => "TestX" .
func SetFrameFnNameProcessor ¶
func SetFrameFnNameProcessor(fn FrameFnNameProcessor)
SetFrameFnNameProcessor configures the function this package uses in order to manipulate the function name from a stack trace frame. You will call it usually somewhere in the bootstrap process of your application. For example:
// myapp/bootstrap.go
func init() {
xerr.SetFrameFnNameProcessor(xerr.ShortFunctionName)
}
func SetSkipFrame ¶
func SetSkipFrame(fn SkipFrame)
SetSkipFrame configures the function this package uses in order to include/exclude frames from a stack trace of an error. You will call it usually somewhere in the bootstrap process of your application. For example:
// myapp/bootstrap.go
func init() {
xerr.SetSkipFrame(SkipFoo(SkipBar(xerr.AllowFrame)))
}
func ShortFunctionName ¶
ShortFunctionName is a FrameFnNameProcessor which returns only the <package.funcName>, removing the fully qualified package name parts. Example: "github.com/actforgood/xerr_test.TestX" => "xerr_test.TestX" .
func Wrap ¶
Wrap returns an error annotating err with a stack trace at the point Wrap is called, and the supplied message. If err is nil, Wrap returns nil. If err is another stack trace aware error, the final stack trace will consists of original error's stack trace + 1 trace of current Wrap call.
Example (WithStackError) ¶
package main
import (
"fmt"
"github.com/actforgood/xerr"
)
// DoSomeOtherOperation simulates a function which (may) return an error.
func DoSomeOtherOperation() error {
return xerr.New("op err")
}
func main() {
err := DoSomeOtherOperation()
if err != nil {
err = xerr.Wrap(err, "could not perform operation")
}
fmt.Println(err.Error())
// Example output:
// could not perform operation: op err
fmt.Printf("%+v\n", err)
// Example output:
// could not perform operation: op err
// github.com/actforgood/xerr_test.ExampleWrap_withStackError
// /Users/bogdan/work/go/xerr/example_test.go:83
// github.com/actforgood/xerr_test.DoSomeOtherOperation
// /Users/bogdan/work/go/xerr/example_test.go:48
// github.com/actforgood/xerr_test.ExampleWrap_withStackError
// /Users/bogdan/work/go/xerr/example_test.go:81
// testing.runExample
// /usr/local/go/src/testing/run_example.go:63
// testing.runExamples
// /usr/local/go/src/testing/example.go:44
// testing.(*M).Run
// /usr/local/go/src/testing/testing.go:1418
// main.main
// _testmain.go:79
// runtime.main
// /usr/local/go/src/runtime/proc.go:225
// runtime.goexit
// /usr/local/go/src/runtime/asm_amd64.s:1371
}
Example (WithStandardError) ¶
package main
import (
"errors"
"fmt"
"github.com/actforgood/xerr"
)
// DoSomeOperation simulates a function which (may) return an error.
func DoSomeOperation() error {
return errors.New("op err")
}
func main() {
err := DoSomeOperation()
if err != nil {
err = xerr.Wrap(err, "could not perform operation")
}
fmt.Println(err.Error())
// Example output:
// could not perform operation: op err
fmt.Printf("%+v\n", err)
// Example output:
// could not perform operation: op err
// github.com/actforgood/xerr_test.ExampleWrap_withStandardError
// /Users/bogdan/work/go/xerr/example_test.go:54
// testing.runExample
// /usr/local/go/src/testing/run_example.go:63
// testing.runExamples
// /usr/local/go/src/testing/example.go:44
// testing.(*M).Run
// /usr/local/go/src/testing/testing.go:1418
// main.main
// _testmain.go:81
// runtime.main
// /usr/local/go/src/runtime/proc.go:225
// runtime.goexit
// /usr/local/go/src/runtime/asm_amd64.s:1371
}
func Wrapf ¶
Wrapf returns an error annotating err with a stack trace at the point Wrap is called, and the message formatted according to a format specifier. If err is nil, Wrap returns nil. If err is another stack trace aware error, the final stack trace will consists of original error's stack trace + 1 trace of current Wrapf call.
Types ¶
type FrameFnNameProcessor ¶
FrameFnNameProcessor is an alias for a function that can manipulate the function name from a stack trace frame. You can apply customizations upon function name output this way.
type MultiError ¶
type MultiError struct {
// contains filtered or unexported fields
}
MultiError holds a pool of errors. Its APIs are concurrent safe if you initialize it with NewMultiError().
func NewMultiError ¶
func NewMultiError() *MultiError
NewMultiError instantiates a new MultiError object. Use it to initialize from start your MultiError variable if you use it in a concurrent context, or you need to pass it as parameter to a function. Otherwise just declare the variable's type and get effective instance returned by Add() / AddOnce() APIs, to avoid unnecessary allocation if those APIs end up never being called.
func (*MultiError) Add ¶
func (mErr *MultiError) Add(errs ...error) *MultiError
Add appends the given error(s) in MultiError. It returns the MultiError, eventually initialized.
Example (Parallel) ¶
package main
import (
"fmt"
"os"
"sync"
"github.com/actforgood/xerr"
)
func main() {
files := []string{
"/this/file/does/not/exist/1",
"/this/file/does/not/exist/2",
"/this/file/does/not/exist/3",
}
multiErr := xerr.NewMultiError() // we need instance already initialized.
var wg sync.WaitGroup
for _, file := range files {
wg.Add(1)
go func(filePath string, mErr *xerr.MultiError, waitGr *sync.WaitGroup) {
defer waitGr.Done()
if _, err := os.Open(filePath); err != nil {
_ = mErr.Add(err) // we can dismiss returned value as multiErr is already initialized.
}
// else do something with that file ...
}(file, multiErr, &wg)
}
wg.Wait()
returnErr := multiErr.ErrOrNil()
fmt.Println(returnErr)
// Example of unordered output:
// error #1
// open /this/file/does/not/exist/3: no such file or directory
// error #2
// open /this/file/does/not/exist/1: no such file or directory
// error #3
// open /this/file/does/not/exist/2: no such file or directory
}
Example (Sequential) ¶
package main
import (
"fmt"
"os"
"github.com/actforgood/xerr"
)
func main() {
files := []string{
"/this/file/does/not/exist/1",
"/this/file/does/not/exist/2",
"/this/file/does/not/exist/3",
}
var multiErr *xerr.MultiError // save allocation if Add is never called.
for _, file := range files {
if _, err := os.Open(file); err != nil {
multiErr = multiErr.Add(err) // store allocated multiErr.
}
// else do something with that file ...
}
returnErr := multiErr.ErrOrNil()
fmt.Println(returnErr)
// Example of output: note - on Windows the message is slightly different (The system cannot find the path specified)
// error #1
// open /this/file/does/not/exist/1: no such file or directory
// error #2
// open /this/file/does/not/exist/2: no such file or directory
// error #3
// open /this/file/does/not/exist/3: no such file or directory
}
func (*MultiError) AddOnce ¶
func (mErr *MultiError) AddOnce(errs ...error) *MultiError
AddOnce stores the given error(s) in MultiError, only if they do not exist already. Comparison is accomplished with Is() API. It returns the MultiError, eventually initialized.
Example ¶
package main
import (
"fmt"
"io"
"os"
"github.com/actforgood/xerr"
)
func main() {
// different errors we want to add to MultiError
err1 := os.ErrNotExist
err2 := io.ErrUnexpectedEOF
err3 := os.ErrNotExist
var multiErr *xerr.MultiError
multiErr = multiErr.AddOnce(err1)
multiErr = multiErr.AddOnce(err2)
multiErr = multiErr.AddOnce(err3) // err3 is the same with err1, so it should be ignored
returnErr := multiErr.ErrOrNil()
fmt.Println(returnErr)
}
Output: error #1 file does not exist error #2 unexpected EOF
func (*MultiError) As ¶
func (mErr *MultiError) As(target interface{}) bool
As implements standard error As() API, comparing the first error from stored ones.
func (*MultiError) ErrOrNil ¶
func (mErr *MultiError) ErrOrNil() error
ErrOrNil returns nil if MultiError does not have any stored errors, or the single error it stores, or self if has more more than 1 error.
func (*MultiError) Error ¶
func (mErr *MultiError) Error() string
Error returns the error's message. Implements std error interface. Returns all stored errors' messages, new line separated.
func (*MultiError) Errors ¶
func (mErr *MultiError) Errors() []error
Errors returns a copy of stored errors.
Example ¶
package main
import (
"errors"
"fmt"
"github.com/actforgood/xerr"
)
func main() {
var multiErr = xerr.NewMultiError()
_ = multiErr.Add(errors.New("1st error"))
_ = multiErr.Add(errors.New("2nd error"))
for _, err := range multiErr.Errors() {
fmt.Println(err)
}
}
Output: 1st error 2nd error
func (*MultiError) Format ¶
func (mErr *MultiError) Format(f fmt.State, verb rune)
Format implements fmt.Formatter. It relies upon individual error's Format() API if applicable, otherwise Error() 's outcome is taken into account.
func (*MultiError) Is ¶
func (mErr *MultiError) Is(target error) bool
Is implements standard error Is() API, comparing the first error from stored ones.
Example ¶
package main
import (
"errors"
"fmt"
"io"
"os"
"github.com/actforgood/xerr"
)
func main() {
var multiErr = xerr.NewMultiError()
_ = multiErr.Add(io.ErrUnexpectedEOF)
someErrWithStack := xerr.New("stack err")
_ = multiErr.Add(someErrWithStack)
fmt.Println(errors.Is(multiErr, io.ErrUnexpectedEOF))
fmt.Println(errors.Is(multiErr, someErrWithStack))
fmt.Println(errors.Is(multiErr, os.ErrClosed))
}
Output: true true false
func (*MultiError) Unwrap ¶
func (mErr *MultiError) Unwrap() error
Unwrap returns original error (can be nil). It implements standard error Is()/As() APIs. Returns recursively first error from stored errors.
type SkipFrame ¶
SkipFrame is alias for a function that decides whether a frame should be included in the stack trace or not.
func SkipFrameGoRootSrcPath ¶
SkipFrameGoRootSrcPath is a chained function which blacklists frames with files starting with "GOROOT/src" path.
type SkipFrameChain ¶
SkipFrameChain is a alias for a chained SkipFrame.