source: https://commandcenter.blogspot.com/2017/12/error-handling-in-upspin.html
by Rob Pike
Panic like a Gopher
Sentinel errors
Typed errors
Behaviour errors
func (c *Client) Get(url string) (resp *Response, err error)
It is idiomatic in Go to use the error interface type as the return type for any error that is going to be returned from a function or method.
(..)Errors are just values and can be programmed in different ways to suit different situations"
func (c *Client) Get(url string) (resp *Response, err error)
//http://golang.org/src/pkg/bufio/bufio.go
var (
ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
ErrBufferFull = errors.New("bufio: buffer full")
ErrNegativeCount = errors.New("bufio: negative count")
)
func (m *meteredExpirator) collectMetric() {
// ...
err := m.underlying.Expire(ctx, maxTime)
switch err {
case nil:
m.expirationSuccessRemoved.Inc(1)
case ErrNothingToExpire:
m.expirationSuccessEmpty.Inc(1)
default:
m.expirationFailed.Inc(1)
}
// ...
}
props
cons
// https://golang.org/pkg/os/#PathError
// PathError records an error and the operation and file path that caused it.
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
Sometimes the caller needs extra context in order to make a more informed error handling decision.
err := something() switch e := err.(type) { case nil: // success case *os.PathError: log.Printf("while executing something, got err: %v", e) if e.Op == "mkdir" { // drop custom metric } else { // drop custom metric }
default:
// unknown error
}
props
Interactions with the world outside your process, like network activity, require that the caller investigate the nature of the error to decide if it is reasonable to retry the operation."
// DNSError represents a DNS lookup error.
type DNSError struct {
Err string // description of the error
Name string // name looked for
Server string // server used
IsTimeout bool // if true, timed out; not all timeouts set this
IsTemporary bool // if true, error is temporary; not all errors set this
}
func (e *DNSError) Error() string {
// ...
}
// Temporary reports whether the DNS error is known to be temporary.
// This is not always known; a DNS lookup may fail due to a temporary
// error and return a DNSError for which Temporary returns false.
func (e *DNSError) Temporary() bool { return e.IsTimeout || e.IsTemporary }
err := c.processItem(key.(string)) switch { case err == nil: c.queue.Forget(key) case IsTemporaryError(err): c.log.Errorf("Error processing %q (will retry): %v", key, err) c.queue.AddRateLimited(key) default: c.log.Errorf("Error processing %q (giving up): %v", key, err) c.queue.Forget(key) }
// IsTemporaryError returns true if error implements following interface:
// type temporary interface {
// Temporary() bool
// }
//
// and Temporary() method return true. Otherwise false will be returned.
func IsTemporaryError(err error) bool {
type temporary interface {
Temporary() bool
}
te, ok := err.(temporary)
return ok && te.Temporary()
}
err := svc.instanceInserter.Insert(...)
switch {
case err == nil:
// 202 Accepted
case IsActiveOperationInProgressError(err):
// provisioning in progress: 202 Accepted
case IsAlreadyExistsError(err):
// all filed are the same: 200 OK
// same ID different fields: 409 Conflict
default:
// 400 Bad Request
return fmt.Errorf("cannot schedule instance to provision: %s", err.Error())
}
// IsNotFoundError checks if given error is NotFound error
func IsNotFoundError(err error) bool {
nfe, ok := err.(interface {
NotFound() bool
})
return ok && nfe.NotFound()
}
// IsAlreadyExistsError checks if given error is AlreadyExist error
func IsAlreadyExistsError(err error) bool {
aee, ok := err.(interface {
AlreadyExists() bool
})
return ok && aee.AlreadyExists()
}
func (c *Controller) processItem(key string) error {
obj, exists, err := c.informer.Informer().GetIndexer().GetByKey(key)
if err != nil {
return err
}
if !exists {
if err := c.reRemover.Remove(internal.RemoteEnvironmentName(key)); err != nil {
return err
}
return nil
}
replaced, err := c.reUpserter.Upsert(dm)
if err != nil {
return err
}
return nil
}
if err := processItem(key); err != nil { log.Errorf("Error processing %q: %v", key, err) return }
{
"level": "error",
"log": {
"message": "Error processing 'ns/re1': no
reachable servers",
}
}
// https://github.com/pkg/errors err := c.reRemover.Remove(internal.RemoteEnvironmentName(key)) if err != nil { return errors.Wrapf(err, "while removing RE with name %q from storage", key) }
{ "level": "error", "log": { "message": "Error processing 'ns/re1': while removing remote environment with name 're1' from storage: while connecting to database: no reachable server", } }