meep

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

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

Go to latest
Published: Jul 29, 2017 License: Apache-2.0 Imports: 7 Imported by: 0

README

More Expressive Error Patterns Build Status

consider the following!

Error[ErrMyApplicationStuck]: Subsystem="blamethis"; TaskNum=42;
	Caused by: Error[ErrNoSpoons]:
		Stack trace:
			·> /build/path/polydawn/meep/autodescriber_test.go:71: meep.TestAutodescribePlusTraceableCause
			·> /usr/local/go/src/testing/testing.go:447: testing.tRunner
			·> /usr/local/go/src/runtime/asm_amd64.s:2232: runtime.goexit

That's the output of meep errors...

... where the errors were types:

type ErrMyApplicationStuck struct {
	meep.TraitCausable
	meep.TraitAutodescribing
	Subsystem string
	TaskNum   int
}
type ErrNoSpoons struct {
	meep.TraitTraceable
	meep.TraitAutodescribing
}

... and the error site was:

err := meep.New(
	&ErrMyApplicationStuck{Subsystem:"blamethis", TaskNum: 42},
	meep.Cause(&ErrNoSpoons{}),
)

i dunno if it's entirely obvious why i'm excited, but... this is

  • A) typed errors with
  • B) near zero keyboard mashing and
  • C) automatically gorgeous output including
  • D) stack traces that
  • E) survive channels if you want to send the error value to other goroutines and
  • F) you can put as many other fields in the structs as you want and they're still tersely prettyprinted.

PLUS, a whole bunch of easily mixed-in behaviors like capturing a chain of "cause"s to an error -- but only if you decide your type needs that!

Typed errors are pretty much universally acknowledged to beat the pants off fmt.Errorf("unmangably handwavey") stringy errors. Now start using them, because it's easy, and you can have stacks and all these other bonuses too!

read more

Documentation

Overview

More Expressive Error Patterns.

Embed `meep` types in your errors structures to effortlessly get fancy behaviors. Stacks, automatic message generation from your fields, common handling: declaring useful error types is now *much* easier.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DamageControl

func DamageControl(handle func(error))

Use `DamageControl` for a safe, terse default mechanism to gather panics before they crash your program.

Damage control can be used like this:

defer DamageControl(func(e error) {
	errCh <- e
})

Using damage control in this way at the top of all your goroutines is advised if there's the slightest possibility of panics arising. Typically, pushing the error into a channel handled by the spawning goroutine is a good response.

`DamageControl` uses `recover()` internally. Note that this means you must defer `DamageControl` itself (you cannot defer another func which calls `DamageControl`; `recover` doesn't work like that).

The error type given to the `handle` function will always be `*ErrUnderspecified`. If you have different, more specific error handling paths and types in mind, you should express those by writing your own recovers.

func Meep

func Meep(err error, opts ...Opts) error

func New

func New(err error, opts ...Opts) error

func RecoverPanics

func RecoverPanics(fn func()) (e error)

Invokes your function, captures any panics, and returns them.

This is simply shorthand for writing your own defers/recovers.

Note that RecoverPanics returns `error` rather than `interface{}`. Any values panicked which do not match the `error` interface will be wrapped in the `ErrUntypedPanic` type, with the original value in the `Cause` field.

func Try

func Try(fn func(), plan TryPlan)

Invokes your function, captures any panics, and routes them through the TryPlan.

This may be superior to calling a function that returns an error and calling `TryPlan.MustHandle` yourself, because any panics that *do* occur will retain a stack including their original panic location until the TryPlan evaluates, meaning you can capture it properly with any error with the `meep.TraitTraceable` property.

Example
meep.Try(func() {
	panic(meep.New(&meep.AllTraits{}))
}, meep.TryPlan{
	{ByType: &meep.ErrInvalidParam{},
		Handler: meep.TryHandlerMapto(&meep.ErrProgrammer{})},
	{ByVal: io.EOF,
		Handler: meep.TryHandlerDiscard},
	{CatchAny: true,
		Handler: func(error) {
			fmt.Println("caught wildcard")
		}},
})
Output:

	caught wildcard

func TryHandlerDiscard

func TryHandlerDiscard(_ error)

Types

type AllTraits

Bundles all the behaviors for quick and easy use!

type ErrInvalidParam

type ErrInvalidParam struct {
	TraitAutodescribing
	TraitTraceable
	Param  string
	Reason string
}

type ErrNotYetImplemented

type ErrNotYetImplemented struct {
	TraitAutodescribing
	TraitTraceable
}

type ErrProgrammer

type ErrProgrammer struct {
	Msg string
	TraitAutodescribing
	TraitTraceable
	TraitCausable
}

type ErrUnderspecified

type ErrUnderspecified struct {
	TraitAutodescribing
	TraitTraceable
	TraitCausable
}

A default type for grabbag, underspecified errors; it is the type used by `DamageControl` to wrap recovered panics.

type ErrUntypedPanic

type ErrUntypedPanic struct {
	TraitAutodescribing
	TraitTraceable
	Cause interface{}
}

A wrapper for non-error types raised from a panic.

The `Try` system will coerce all non-error types to this automatically.

type Opts

type Opts struct {
	// contains filtered or unexported fields
}

func Cause

func Cause(x error) Opts

Use `Cause` to tell `Meep()` that it should attach another error as a cause to the error it's initializating.

Usage:

meep.Meep(
	&ErrSomethingCausable{},
	meep.Cause(fmt.Errorf("the root cause")),
)

func NoStack

func NoStack() Opts

Use `NoStack` to tell `Meep()` that it should skip gathering a stack trace for this error, even if it has `TraitTraceable`.

Usage:

meep.Meep(
	&ErrUsuallyHasAStacktrace{},
	meep.NoStack(), // skip stacks this time.
)

type Stack

type Stack struct {
	Frames []StackFrame
}

func CaptureStack

func CaptureStack() *Stack

Captures a trace of the current stack.

You probably won't want to use this directly; instead, use a `TraitTraceable` like this:

//type ErrXYZ struct { TraitTraceable }
err := meep.New(&ErrXYZ{})
// `err` now automatically has a stack capture!

There's nothing more magical here than `runtime.Callers`; just some additional types and prettyprinting which are absent from stdlib `runtime` because of stdlib's necessary avoidance of invoking complex features in that (zerodep!) package.

type StackFrame

type StackFrame uintptr

func (StackFrame) String

func (pc StackFrame) String() string

`String` returns a human readable form of the frame.

The string includes the path to the file and the linenumber associated with the frame, formatted to match the `file:lineno: ` convention (so your IDE, if it supports that convention, may let you click-to-jump); following the source location info, the function name is suffixed.

func (StackFrame) Where

func (pc StackFrame) Where() (file string, line int, fn string)

type TraitAutodescribing

type TraitAutodescribing struct {
	// contains filtered or unexported fields
}

Errors that generate their messages automatically from their fields!

func (*TraitAutodescribing) Error

func (m *TraitAutodescribing) Error() string

Implements `error`.

If you're using other mixins, you may want to override this again; if you're just using `Autodescriber`, it'll do fine.

func (*TraitAutodescribing) ErrorMessage

func (m *TraitAutodescribing) ErrorMessage() string

type TraitCausable

type TraitCausable struct {
	Cause error
}

Errors with other errors as their cause!

type TraitTraceable

type TraitTraceable struct {
	Stack Stack
}

Errors with stacks!

func (TraitTraceable) IsStackSet

func (m TraitTraceable) IsStackSet() bool

func (TraitTraceable) StackString

func (m TraitTraceable) StackString() string

Return the stack of the error formatted as a human readable string: one frame per line. Each line lists the source file, line number, and the name of the function.

func (TraitTraceable) WriteStack

func (m TraitTraceable) WriteStack(w io.Writer)

Same job as StackString; use StackString for convenience, use this for performance.

type TryHandler

type TryHandler func(error)

func TryHandlerMapto

func TryHandlerMapto(toTmpl interface{}) TryHandler

type TryPlan

type TryPlan []TryRoute

TryPlan is a declarative error handling plan.

You can dispatch errors according to several patterns, since both the golang stdlib and many libraries have seen fit to use a variety of different patterns, and they can't easily be distinguished by type alone (e.g. should we typeswitch, or do we need to do actual val/ptr compare):

TryRoute{ByType: exampleVal error,       Handler: fn}
TryRoute{ByVal:  ptrOrVal error,         Handler: fn}
TryRoute{ByFunc: func(error) bool,       Handler: fn}
TryRoute{CatchAny: true,                 Handler: fn}

The `By*` fields are used to check whether an error should be handled by that route; then the handler is called when a route matches. Errors are checked against routes in the order they're listed in your TryPlan.

Use `ByType` as much as you can. Meep's typed error helpers should make typed errors your default coding style.

Use `ByVal` where you have to; `io.EOF` is one you must check by value.

Use `ByFunc` as a last resort -- but if you really have to do something complicated, go for it.

If you want to catch *everything*, set the CatchAny flag. If using CatchAny, be sure to add it last -- since it matches any error, routes after it will never be called.

func (TryPlan) Handle

func (tp TryPlan) Handle(e error) error

Checks the TryPlan for handlers that match the error, and invokes the first one that does.

The return value is nil if we found and called a handler, or the original error if there was no matching handler.

If the error parameter was nil, no handler will be called, and the result will always be nil.

func (TryPlan) MustHandle

func (tp TryPlan) MustHandle(e error)

Like `Handle(e)`, but will panic if the (non-nil) error is not handled.

type TryRoute

type TryRoute struct {
	ByType   interface{}
	ByVal    interface{}
	ByFunc   func(error) bool
	CatchAny bool

	Handler TryHandler
}

A single route, used to compose a TryPlan.

Typical usage is to set a Handler function, and then treat the other fields as if it's a 'union' (that is, only set one of them), like this:

TryRoute{ByVal: io.EOF, Handler: func(e error) { fmt.Println(e) }}

func (TryRoute) Matches

func (tr TryRoute) Matches(e error) bool

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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