harvest Go errors with ease

Overview

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

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, 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.

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.

For library development guide, read the docs.

Prerequisites

Golang 1.6+

Authors

  • Billy Peake - Initial work

Attributions

Logo made by Freepik from www.flaticon.com
Issues
  • Reworked API with inspiration from errgroup.Group

    Reworked API with inspiration from errgroup.Group

    Based on this comment thread: https://www.reddit.com/r/golang/comments/mwzmo6/pears_a_package_for_easily_handling_panic/

    I quite like the API for errgroup.Group, but decided against making this a drop-in replacement. Some methods and types have been renamed to be more erganomic and the options have been changed to more readable.

    The docs have also been updated to directly discuss how this lib differs from errgroup.Group and what it offers as an improvement.

    enhancement 
    opened by peake100 0
Owner
Billy Peake
Ex Hollywood assistant editor turned programmer.
Billy Peake
Common juju errors and functions to annotate errors. Based on juju/errgo

errors import "github.com/juju/errors" The juju/errors provides an easy way to annotate errors without losing the original error context. The exporte

Juju 1.3k May 12, 2022
Linter for errors.Is and errors.As

erris erris is a program for checking that errors are compared or type asserted using go1.13 errors.Is and errors.As functions. Install go get -u gith

Roman Budnikov 51 Mar 3, 2022
An errors package optimized for the pkg/errors package

errors An errors package optimized for the pkg/errors package Use Download and install go get github.com/dobyte/errors API // New Wrapping for errors.

Fuxiao 3 Mar 2, 2022
The Emperor takes care of all errors personally

The Emperor takes care of all errors personally. Go's philosophy encourages to gracefully handle errors whenever possible, but some times recovering f

Emperror 248 Apr 17, 2022
eris provides a better way to handle, trace, and log errors in Go 🎆

eris Package eris provides a better way to handle, trace, and log errors in Go. go get github.com/rotisserie/eris Why you'll want to switch to eris Us

null 1k May 15, 2022
A drop-in replacement for Go errors, with some added sugar! Unwrap user-friendly messages, HTTP status code, easy wrapping with multiple error types.

Errors Errors package is a drop-in replacement of the built-in Go errors package with no external dependencies. It lets you create errors of 11 differ

Kamaleshwar 41 May 10, 2022
A Go (golang) package for representing a list of errors as a single error.

go-multierror go-multierror is a package for Go that provides a mechanism for representing a list of error values as a single error. This allows a fun

HashiCorp 1.6k May 18, 2022
Golang errors with stack trace and source fragments.

Golang Errors with Stack Trace and Source Fragments Tired of uninformative error output? Probably this will be more convenient: Example package main

null 710 May 13, 2022
Hierarchical errors reporting done right in Golang

Hierarchical errors made right Hate seeing error: exit status 128 in the output of programs without actual explanation what is going wrong? Or, maybe,

reconquest 74 Nov 9, 2021
Go tool to wrap and fix errors with the new %w verb directive

errwrap Wrap and fix Go errors with the new %w verb directive. This tool analyzes fmt.Errorf() calls and reports calls that contain a verb directive t

Fatih Arslan 352 May 10, 2022
Golang errors with stacktrace and context

merry Add context to errors, including automatic stack capture, cause chains, HTTP status code, user messages, and arbitrary values. The package is la

Russ Egan 248 Apr 5, 2022
SupErr -- Go stdlib errors with super powers

superr SupErr -- Go stdlib errors with super powers. Pronounced super with a French accent :D Build a stack of errors compatible with Go stdlib and er

Golang libraries for everyone 22 Mar 2, 2022
Package semerr helps to work with errors in Golang.

semerr Package semerr helps to work with errors in Golang. Const error An error that can be defined as const. var errMutable error = errors.New("mutab

Maxim Krivchun 3 Mar 27, 2022
A Nostress Errors Package For Golang

A Nostress Errors Package For Golang

null 0 Nov 2, 2021
Simple Go library for typed errors creation

go-typed-errors Why this repo was created? Main reason was to create additional methods for better error typing support. Why not to use errors.As and

Bogdan 2 Nov 5, 2021
Simple Go library for typed const errors creation

go-errors Why this repo was created? Main reason was to create additional methods for better error typing support. Features Errors as constants errors

Bogdan 2 Nov 5, 2021
This project is for parsing Artifactory logs for errors

hello-frog About this plugin This plugin is a template and a functioning example for a basic JFrog CLI plugin. This README shows the expected structur

Tatarao 0 Nov 30, 2021
Go package for errors with chained stack traces

errstack: Go errors with chained stack traces errstack is a Go package for creating errors with stack traces. It is heavily inspired by github.com/pkg

Greg Ward 0 Nov 27, 2021
A Go package providing errors with a stack trace Read-only

Errors with a stack trace A Go package providing errors with a stack trace. Features: Based of github.com/pkg/errors with similar API, addressing many

null 0 Jan 7, 2022