An open API service indexing awesome lists of open source software.

https://github.com/peake100/pears-go

harvest Go errors with ease
https://github.com/peake100/pears-go

error-handling errors go golang panic-handler

Last synced: 5 months ago
JSON representation

harvest Go errors with ease

Awesome Lists containing this project

README

          

Pears





Harvest Go Errors with Ease



click to see build pipeline
click to see build pipeline
click to see build pipeline



click to see report card



Repo
Go Reference

Introduction
------------

Pears helps reduce the boilerplate and ensure correctness for common error-handling
scenarios:

- Panic recovery

- Abort and error collection from concurrent workers.

Demo
----

**Catch a Panic**

```go
package main

import (
"errors"
"fmt"
"github.com/peake100/pears-go/pkg/pears"
"io"
)

func main() {
// We can use CatchPanic to catch ay panics that occur in an operation:
err := pears.CatchPanic(func() (innerErr error) {
// We are going to throw an io.EOF.
panic(io.EOF)
})

// Our error will report that it is from a recovered panic.
fmt.Println("Error:", err)

// We can test whether this error is a the result of a panic by using errors.As.
panicErr := pears.PanicError{}
if errors.As(err, &panicErr) {
fmt.Println("error is recovered panic")
// do something if this was a panic
}

// PanicError implements xerrors.Wrapper, so we can use errors.Is and errors.As
// to get at any inner errors.
if errors.Is(err, io.EOF) {
fmt.Println("error is io.EOF")
}

// Output:
// Error: panic recovered: EOF
// error is recovered panic
// error is io.EOF
}
```

**Gather Errors From Multiple Workers**

pears offers a ``Group`` type which takes some inspirations from
[https://pkg.go.dev/golang.org/x/sync/errgroup](errgroup.Group), with some key
differences:

- All errors are collected, not just the first. Each is wrapped in an OpError and
then collected into a GroupErrors. These types offer a number of ways to inspect
and resolve errors in concurrent situations.

- Launched operations can be named using GoNamed for more robust error inspection and
handling.

- A context is required, and is passed to all child functions, allowing for higher
readability of where a context comes from.

- Group must be created with a constructor function: NewGroup.

```go
package main

import (
"context"
"errors"
"fmt"
"github.com/peake100/pears-go/pkg/pears"
"io"
"time"
)

func main() {
group := pears.NewGroup(
context.Background(), // this context will be used as the parent to al
// operation contexts
)

for i := 0; i < 10; i++ {
// Each routine will be identified as 'worker [workerNum]'. We do not need to
// use the 'go' keyword here. op will be launched as a routine, but some
// internal bookkeeping needs to occur before the op can be launched.
workerNum := i
group.GoNamed(fmt.Sprint("worker", workerNum), func(ctx context.Context) error {
// We'll use a timer to stand in for some long-running worker.
timer := time.NewTimer(5 * time.Second)
select {
case <-ctx.Done():
fmt.Printf("operation %v received abort request\n", workerNum)
return ctx.Err()
case <-timer.C:
fmt.Printf("operation %v completed successfully\n", workerNum)
return nil
}
})
}

// Lastly we'll launch a routine that returns an error, which will cancel the
// contexts of every op launched above.
group.GoNamed("faulty operation", func(ctx context.Context) error {
// This faulty operation will return an io.EOF
return io.EOF
})

// Now we join the group, which blocks until all routines launched above return.
// If any operations returned an error, we will get one here.
err := group.Wait()

// report our error.
fmt.Println("\nERROR:", err)

// errors.Is() and errors.As() can inspect what caused our operations to fail.
// Because pears.BatchMatchFirst is our error-matching mode, only the FIRST
// encountered error will pass errors.Is() or errors.As().
//
// For us that should be io.EOF.
if errors.Is(err, io.EOF) {
fmt.Println("error is io.EOF")
}

// Even though the other operations returned context.Canceled, we will NOT
// pass the following check since it was not the FIRST error returned. This is nice
// for checking against an error that started a cascade.
//
// If our match mode had been set to pears.BatchMatchAny, this check would also
// pass
if errors.Is(err, context.Canceled) {
fmt.Println("error is context.Cancelled")
}

// We can extract a pears.OpError to get more information about the first error.
opErr := pears.OpError{}
if !errors.As(err, &opErr) {
panic("expected opErr")
}

fmt.Println("batch failure caused by operation:", opErr.OpName)

// We can also extract a GroupErrors to inspect all of our errors more closely:
groupErrs := pears.GroupErrors{}
if !errors.As(err, &groupErrs) {
panic("expected BatchErrors")
}

// Let's inspect ALL of the errors we got back. We'll see that the context
// cancellation errors were returned, but because of our Batch error matching mode,
// are being kept from surfacing through errors.Is() and errors.As().
fmt.Println("\nALL ERRORS:")
for _, thisErr := range groupErrs.Errs {
fmt.Println(thisErr)
}

// Unordered Output:
//
// operation 9 received abort request
// operation 8 received abort request
// operation 1 received abort request
// operation 3 received abort request
// operation 6 received abort request
// operation 4 received abort request
// operation 0 received abort request
// operation 7 received abort request
// operation 5 received abort request
// operation 2 received abort request
//
// ERROR: 11 errors returned. first: error during 'faulty operation': EOF
// error is io.EOF
// batch failure caused by operation: faulty operation
//
// ALL ERRORS:
// error during 'faulty operation': EOF
// error during 'worker1': context canceled
// error during 'worker5': context canceled
// error during 'worker7': context canceled
// error during 'worker0': context canceled
// error during 'worker4': context canceled
// error during 'worker6': context canceled
// error during 'worker8': context canceled
// error during 'worker3': context canceled
// error during 'worker2': context canceled
// error during 'worker9': context canceled
}
```

Goals
-----

- Expose simple APIs for dealing with common error-handling situations.

- Support error inspection through errors.Is and errors.As,

Non-Goals
---------

- Creating complex error frameworks. Pears does not want to re-invent the wheel and
seeks only to reduce the boilerplate of leveraging Go's built-in error system.

- Solving niche problems. This package seeks to help only the most-broad error cases.
Features like HTTP or gRPC error-code and serialization systems are beyond the scope
of this package.

## Getting Started
For API documentation:
[read the docs](https://pkg.go.dev/github.com/peake100/pears-go?readme=expanded#section-documentation).

For library development guide,
[read the docs](https://illuscio-dev.github.io/islelib-go/).

### Prerequisites

Golang 1.6+

## Authors

* **Billy Peake** - *Initial work*

## Attributions

Logo made by Freepik from www.flaticon.com