whoops

package module
v0.7.2 Latest Latest
Warning

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

Go to latest
Published: Mar 3, 2023 License: Apache-2.0 Imports: 4 Imported by: 44

README

Whoops!

codecov Go Report Card

whoops logo

Logo inspired by the Awkward Look Monkey Puppet meme.


Whoops is a small helper Go library to help you with errors in Go. I've decided to create this library because I've noticed that I am using basically the same pattern all over and I wanted to make a library to help me save some time.

Error types

String

A simple string error that you can define as a constant.

import "github.com/vizualni/whoops"
const ErrTheAfwulThingHasHappened = whoops.String("something bad has happened")

Errorf

An error that can be formatted. It can also be defined as a constant.

Usually, you'd do something like:


func foo() error {
	// ...
	var answer = 42
	return fmt.Errorf("the answer is: %d", answer)
}

which would make it harder to use error assertion and you'd most likely end up doing a string comparison with errors to figure out which error has happened, and you'd be unable to use errors.Is to help you out.

See example below to get the idea behind the Errorf type of error.

import "github.com/vizualni/whoops"
const ErrPayloadSizeTooLarge = whoops.Errorf("payload size too big. got %d bytes")

// ...
return ErrPayloadSizeTooLarge.Format(len(payload))


// later in code you can do
var err error
if whoops.Is(err, ErrPayloadSizeTooLarge) {
	// do your thing
}

Enrich errors with custom fields

Enrich your errors with extra information. e.g. logging the query that failed without query string going into the error message making it unreadable. It uses Go 1.18 generic's feature to ensure type safety for the fields.

import "yourpackage"
import "github.com/vizualni/whoops"

const ErrFieldUser[yourpackage.User] = "user"
const ErrFieldQuery[yourpackage.Query] = "query"

func process() error {
	var (
		err error
		user yourpackage.User
		query yourpackage.Query
	)
	// ...
	return whoops.Enrich(err, ErrFieldUser.Val(user), ErrFieldQuery.Val(query))
}

// not the best example, but you get the picture
func caller() {
	err := process()
	if err != nil {
		var (
			query yourpackage.Query
			ok bool
		)
		if query, ok = ErrFieldQuery.GetFrom(err); ok {
			// log the query that failed
			log.Error("error %s with query: %s", err, query.Text())	
			// ...
		}
	}
	// ...
}

Grouping errors

If you are processing many things at once and you want to group all those errors together.

Example 1
import "github.com/vizualni/whoops"

func foo() error {
	var groupErr whoops.Group

	// processN function returns an error only (can return nil as well)
	groupErr.Add(proces1()) 
	groupErr.Add(proces2()) 
	groupErr.Add(proces3()) 

	if groupErr.Err() {
		return groupErr
	}
	// success
	return nil
}
Example 2
import "github.com/vizualni/whoops"
const (
	ErrBad1 = whoops.String("something bad has happened")
	ErrBad2 = whoops.Errorf("format this: %s")
)

// ...
var groupErr whoops.Group
groupErr.Add(ErrBad1) 
groupErr.Add(ErrBad2.Format("foobar")) 

whoops.Is(groupErr, ErrBad1) // returns true
whoops.Is(groupErr, ErrBad2) // returns true

Wrapping errors

Wrapping is very much alike like grouping but it's mostly better for only wrapping two errors together.

import "github.com/vizualni/whoops"
const (
	ErrUnableToIncrementCounter = whoops.String("unable to increment counter")
)

func incrementUserCounter(id int) error {
	// ...
	err := incrementCounterForUser(id)
	if err != nil {
	   return whoops.Wrap(err, ErrUnableToIncrementCounter)
	}
	// ...
}

Errors with stacktrace

All error types have method called Trace, which called, taked the current stack trace and creates a new error with the stack trace attached. This makes things easier for debugging.

import "github.com/vizualni/whoops"
const (
	ErrFoo = whoops.String("foo")
	ErrBar = whoops.Errorf("hello: %s")
)

func bar() error {
	// ...
	if err != nil {
	   return whoops.Trace(err)
	}
	// or
	return ErrFoo.Trace()
	// or
	return ErrBar.Format("Alice").Trace()
	// or
	return Group{ErrFoo, ErrBar.Format("Bob")}.Trace()
	// or
	return Wrap(ErrFoo, ErrBar.Format("Bob")).Trace()
	// ...
}

if err != nil { return err }

The idea is not to get rid of err != nil but rather to eliminate the need to write it all the time.

Assert

import "github.com/vizualni/whoops"

err := functionCall()
whoops.Assert(err) // panics if err is nil and the value that was panicked is the (wrapped) error

Must

import "github.com/vizualni/whoops"
import "json"

var myMap map[string]any
// ...
var bytes []byte
bytes = whoops.Must(json.Marshal(myMap))

Must1, Must2 && Must3

Try

import "github.com/vizualni/whoops"
import "json"

// ...
err := whoops.Try(func(){
	bytes := whoops.Must(json.Marshal(myMap))
	bytesWritten := whoops.Must(file.Write(bytes))
})
if err != nil {
	return err
}

TryVal

import "github.com/vizualni/whoops"
import "json"

// ...
n, err := whoops.TryVal(func() int{
	bytes := whoops.Must(json.Marshal(myMap))
	bytesWritten := whoops.Must(file.Write(bytes))
	return bytesWritten
})
if err != nil {
	return err
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func As

func As(err error, target any) bool

func Assert

func Assert(err error)

func Compose

func Compose(err error, funcs ...Compositer) error

func DeferWrap

func DeferWrap(wrapWith error) func()

func Enrich

func Enrich(err error, fields ...enricher) error

func FormatStacktrace

func FormatStacktrace(err error) (res []string)

func Is

func Is(err, target error) bool

func Must

func Must[T any](val T, err error) T

func Must1

func Must1(err error)

func Must2

func Must2[T any](val T, err error) T

func Must3

func Must3[T1 any, T2 any](val1 T1, val2 T2, err error) (T1, T2)

func Trace

func Trace(err error) error

func Try

func Try(fnc func()) (err error)

func TryVal

func TryVal[T any](fnc func() T) (t T, err error)

func Unwrap

func Unwrap(err error) error

func Wrap

func Wrap(err, wrap error) wrapper

func WrapS

func WrapS(err error, msg string, args ...any) wraps

Types

type Compositer

type Compositer func(error) error

func ComposeCustomMessage

func ComposeCustomMessage(fnc func(error) string) Compositer

func ComposeEnrich

func ComposeEnrich(fields ...enricher) Compositer

type Errorf

type Errorf string

func (Errorf) Error

func (e Errorf) Error() string

func (Errorf) Format

func (e Errorf) Format(args ...any) formattedErrorf

func (Errorf) Is

func (e Errorf) Is(err error) bool

type Field

type Field[T any] string // adding T for type safety

func (Field[T]) GetFrom

func (f Field[T]) GetFrom(err error) (val T, found bool)

func (Field[T]) Val

func (f Field[T]) Val(v T) wrappedValue[T]

type Group

type Group []error

func (*Group) Add

func (g *Group) Add(errs ...error)

func (Group) Err

func (g Group) Err() bool

func (Group) Error

func (g Group) Error() string

func (Group) Return

func (g Group) Return() error

func (Group) Trace

func (s Group) Trace() error

func (Group) Unwrap

func (g Group) Unwrap() error

func (Group) WrapS

func (g Group) WrapS(msg string, args ...any) wraps

type String

type String string

func (String) Error

func (s String) Error() string

func (String) Trace

func (s String) Trace() error

func (String) WrapS

func (s String) WrapS(msg string, args ...any) wraps

Jump to

Keyboard shortcuts

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