rclone/vendor/github.com/spacemonkeygo/errors/doc.go
2020-05-12 15:56:50 +00:00

205 lines
6.4 KiB
Go

// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package errors is a flexible error support library for Go
Motivation
Go's standard library is intentionally sparse on providing error utilities, and
developers coming from other programming languages may miss some features they
took for granted [1]. This package is an attempt at providing those features in
an idiomatic Go way.
The main features this package provides (in addition to miscellaneous
utilities) are:
* Error hierarchies
* Stack traces
* Arbitrary error values
Error hierarchies
While Go has very deliberately not implemented class hierarchies, a quick
perusal of Go's net and os packages should indicate that sometimes error
hierarchies are useful. Go programmers should be familiar with the net.Error
interface (and the types that fulfill it) as well as the os helper functions
such as os.IsNotExist, os.IsPermission, etc.
Unfortunately, to implement something similar, a developer will have to
implement a struct that matches the error interface as well as any testing
methods or any more detailed interfaces they may choose to export. It's not
hard, but it is friction, and developers tend to use fmt.Errorf instead due
to ease of use, thus missing out on useful features that functions like
os.IsNotExist and friends provide.
The errors package provides reusable components for building similar
features while reducing friction as much as possible. With the errors package,
the os error handling routines can be mimicked as follows:
package osmimic
import (
"github.com/spacemonkeygo/errors"
)
var (
OSError = errors.NewClass("OS Error")
NotExist = OSError.NewClass("Not Exist")
)
func Open(path string) (*File, error) {
// actually do something here
return nil, NotExist.New("path %#v doesn't exist", path)
}
func MyMethod() error {
fh, err := Open(mypath)
if err != nil {
if NotExist.Contains(err) {
// file doesn't exist, do stuff
}
return err
}
// do stuff
}
Stack traces
It doesn't take long during Go development before you may find yourself
wondering where an error came from. In other languages, as soon as an error is
raised, a stack trace is captured and is displayed as part of the language's
error handling. Go error types are simply basic values and no such magic
happens to tell you what line or what stack an error came from.
The errors package fixes this by optionally (but by default) capturing the
stack trace as part of your error. This behavior can be turned off and on for
specific error classes and comes in two flavors. You can have the stack trace
be appended to the error's Error() message, or you can have the stack trace
be logged immediately, every time an error of that type is instantiated.
Every error and error class supports hierarchical settings, in the sense that
if a setting was not explicitly set on that error or error class, setting
resolution traverses the error class hierarchy until it finds a valid setting,
or returns the default.
See CaptureStack()/NoCaptureStack() and LogOnCreation()/NoLogOnCreation() for
how to control this feature.
Arbitrary error values
These hierarchical settings (for whether or not errors captured or logged stack
traces) were so useful, we generalized the system to allow users to extend
the errors system with their own values. A user can tag a specific error with
some value given a statically defined key, or tag a whole error class subtree.
Arbitrary error values can easily handle situtations like net.Error's
Temporary() field, where some errors are temporary and others aren't. This can
be mimicked as follows:
package netmimic
import (
"github.com/spacemonkeygo/errors"
)
var (
NetError = errors.NewClass("Net Error")
OpError = NetError.NewClass("Op Error")
tempErrorKey = errors.GenSym()
)
func SetIsTemporary() errors.ErrorOption {
return errors.SetData(tempErrorKey, true)
}
func IsTemporary(err error) bool {
v, ok := errors.GetData(err, tempErrorKey).(bool)
if !ok {
return false
}
return v
}
func NetworkOp() error {
// actually do something here
return OpError.NewWith("failed operation", SetIsTemporary())
}
func Example() error {
for {
err := NetworkOp()
if err != nil {
if IsTemporary(err) {
// probably should do exponential backoff
continue
}
return err
}
}
}
HTTP handling
Another great example of arbitrary error value functionality is the errhttp
subpackage. See the errhttp source for more examples of how to use
SetData/GetData.
The errhttp package really helped clean up our error code. Take a look to
see if it can help your error handling with HTTP stacks too.
http://godoc.org/github.com/spacemonkeygo/errors/errhttp
Exit recording
So you have stack traces, which tells you how the error was generated, but
perhaps you're interested in keeping track of how the error was handled?
Every time you call errors.Record(err), it adds the current line information
to the error's output. As an example:
func MyFunction() error {
err := Something()
if err != nil {
if IsTemporary(err) {
// manage the temporary error
return errors.Record(err)
} else {
// manage the permanent error
return errors.Record(err)
}
}
}
errors.Record will help you keep track of which error handling branch your
code took.
ErrorGroup
There's a few different types of ErrorGroup utilities in this package, but they
all work the same way. Make sure to check out the ErrorGroup example.
CatchPanic
CatchPanic helps you easily manage functions that you think might panic, and
instead return errors. CatchPanic works by taking a pointer to your named error
return value. Check out the CatchPanic example for more.
Footnotes
[1] This errors package started while porting a large Python codebase to Go.
https://www.spacemonkey.com/blog/posts/go-space-monkey
*/
package errors