errors

package module
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: Feb 8, 2022 License: MIT Imports: 7 Imported by: 12

README

errors GoDoc Build Status Coverage Status Go Report Card

errors

The errors package provides an enterprise approach of error handling. Drop in replacement of standard errors package.

Motivation

Make errors helpful for quick problem localization. Reduce amount of emails to the helpdesk due to better explained error reason.

Requirements

  • Wrapping: enhance original error message with context specific one;
  • Capture the calling stack;
  • Cut calling stack related to the HTTP framework;
  • Enhance error with key/value pairs, later could be written into structurized log;
  • Enhance error with the code what can be refered in documentation. (i.g. ORA-0600 in Oracle);
  • Enhance error with severity level;
  • Support different JSON representation for server and client;
  • Possibility to mark any error as protected. It will not be presented in client's JSON.
  • Notify SRE if needed.

Installation

go get -u github.com/axkit/error

Usage Examples

Catch and Enhance Standard Go Error
func (srv *CustomerService)WriteJSON(w io.Writer, c *Customer) (int, error) {

    buf, err := json.Marshal(src)
    if err != nil {
        return 0, errors.Catch(err).Critical().Set("customer", c).StatusCode(500).Msg("internal error")
    }

    n, err := w.Write(buf)
    if err != nil {
        // Level is Tiny by default. 
        return 0, errors.Catch(err).StatusCode(500).Msg("writing to stream failed").Code("APP-0001")
    }
     
    return n, nil  
}

Catch and Enhance Already Catched Error
func AllowedProductAmount(balance, price int) (int, error) {

    res, err := Calc(balance, price)
    if err != nil {
        return 0, errors.Catch(err).SetPairs("balance", balance, "price", price).Msg("no allowed products")
    }

    return res, nil
}


func Calc(a, b int) (int, error) {

    if b == 0 {
        return 0, errors.New("divizion by zero").Critical()
    }

    return a/b, nil
}
Recatch NotFound Error Conditionally

There is a special function errors.IsNotFound() that returns true error has StatusCode = 404 or created using errors.NotFound().

func (srv *CustomerService)AcceptPayment(customerID int, paymentAmount int64) error {

    c, err := srv.repo.CustomerByID(id)
    if err != nil {
        if errors.IsNotFound(err) {
            return nil, errors.Catch(err).Medium().Msg("invalid customer")
        }
        return return nil, errors.Catch(err).Critical().Msg("internal error")
    }

    return c, nil
}

func (srv *CustomerService)CustomerByID(id int) (*Customer, error) {

    c, ok := srv.repo.CustomerByID(id)
    if !ok {
        return nil, errors.NotFound("customer not found").Set("id", id)
    }

    return c, nil
}
Write Error Responce to Client
    err := doSmth()
    
    // err is standard error  
    fmt.Println(errors.ToClientJSON(err))

    // Output:
    {"msg":"result of Error() method"}

Write Error Responce to Server Log
Send Alarm to SRE

License

MIT

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// CallerFramesFunc holds default function used by function Catch()
	// to collect call frames.
	CallerFramesFunc func(offset int) []Frame = DefaultCallerFrames

	// CallingStackMaxLen holds maximum elements in the call frames.
	CallingStackMaxLen int = 15
)
View Source
var CaptureStackStopWord string = "fasthttp"

CaptureStackStopWord holds a function name part. Stack will be captured till the function with the first CaptureStackStopWord

The stack capturing ignored if it's empty or it's appeared in the first function name.

It can be used to ignore stack above HTTP handler.

View Source
var ConsistencyFailed = func() *CatchedError {
	return cf.Raise()
}

ConsistencyFailed is a function, returns *CatchedError with predefined StatusCode=500 and Severity=Critical.

View Source
var ErrorMethodMode = Single

ErrorMethodMode holds behavior of Error() method.

View Source
var Forbidden = func() *CatchedError {
	return fbd.Raise()
}

Forbidden is a function, returns *CatchedError with predefined StatusCode=403 and Severity=Critical.

View Source
var InternalError = func() *CatchedError {
	return ie.Raise()
}

ValidationFailed is a function, returns *CatchedError with predefined StatusCode=400 and Severity=Tiny.

View Source
var InvalidRequestBody = func(s string) *CatchedError {
	return irr.Raise().msg(s)
}
View Source
var LastNonPairedValue interface{} = "missed value"

LastNonPairedValue holds value to be assigned by SetPairs if amount of parameters is odd.

View Source
var NotFound = func(msg string) *CatchedError {
	return nf.Raise().msg(msg)
}
View Source
var RootLevelFields = []string{"reason"}

RootLevelFields holds a name of context fields which will be generated placed on the root level in JSON together with standard error attribute such as msg, code, statusCode. All other context fields will be located under root level attribute "ctx".

View Source
var Unauthorized = func() *CatchedError {
	return ue.Raise()
}

Unauthorized is a function, returns *CatchedError with predefined StatusCode=401 and Severity=Medium.

View Source
var UnprocessableEntity = func(s string) *CatchedError {
	return ue.Raise().msg(s)
}
View Source
var ValidationFailed = func(msg string) *CatchedError {
	return vf.Raise().msg(msg)
}

ValidationFailed is a function, returns *CatchedError with predefined StatusCode=400 and Severity=Tiny.

Functions

func As added in v0.0.2

func As(err error, target interface{}) bool

func Is added in v0.0.2

func Is(err, target error) bool

func IsNotFound added in v0.0.2

func IsNotFound(err error) bool

IsNotFound returns true if err is *CatchedError and StatusCode is 404. If err wraps another errors, last one is taken for decision.

func ToClientJSON added in v0.0.2

func ToClientJSON(err error) []byte

ToClientJSON returns error formatted as JSON addressed to HTTP response.

{
	"msg" : "validation failed",
	"code" : "ORA-0600",
	"severity" : "critical",
	"statusCode" : 400
}

func ToJSON added in v0.0.2

func ToJSON(err error, flags FormattingFlag) []byte

ToJSON formats error as JSON with flags.

func ToServerJSON added in v0.0.2

func ToServerJSON(err error) []byte

ToServerJSON returns error formatted as JSON addressed to server logs.

func Unwrap added in v0.0.2

func Unwrap(err error) error

Unwrap returns the result of calling the Unwrap method on err, if err's type contains an Unwrap method returning error. Otherwise, Unwrap returns nil.

Types

type Alarmer added in v0.0.2

type Alarmer interface {
	Alarm(*CatchedError)
}

Alarmer wraps a single method Alarm that receives CatchedError and implement real-time notification logic.

The type CapturedError has method Alarm that recieves Alarmer as a parameter.

type CatchedError

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

CatchedError holds original error, all intermediate error wraps and the call stack.

func Catch

func Catch(err error) *CatchedError

Catch wraps an error with capturing stack at the point of calling. Assigns severity level Tiny.

If err is already CapturedError, the stack does not capture again. It's assumed it was done before. The attributes Severity and StatusCode inherits but can be changed later.

Returns nil if err is nil.

func CatchCustom

func CatchCustom(err error, stackcapture func() []Frame) *CatchedError

CatchCustom wraps an error with custom stack capturer.

func New

func New(msg string) *CatchedError

New returns *CatchedError with stack at the point of calling and severity level Tiny. The function is used if there is no original error what can be wrapped.

Example
package main

import (
	"fmt"
	"strconv"

	"github.com/axkit/errors"
)

func main() {

	var err error
	var ErrValidationFailed = errors.New("value validation failed").StatusCode(400)

	if _, err = strconv.Atoi("5g"); err != nil {
		err = errors.Wrap(err, ErrValidationFailed.Set("value", "5g"))
	}

	//err := errors.Raise(ErrAuthServiceIsNotAvailable).StatusCode(500).Critical()
	fmt.Println(err.Error())
}
Output:

func NewCritical

func NewCritical(msg string) *CatchedError

NewCritical returns CatchedError with stack at the point of calling and severity level Critical.

func NewMedium

func NewMedium(msg string) *CatchedError

NewMedium returns *CatchedError with stack at the point of calling and severity level Medium.

func NewTiny added in v0.0.2

func NewTiny(msg string) *CatchedError

NewTiny is synonym to func New.

func Raise added in v0.0.2

func Raise(ce *CatchedError) *CatchedError

Raise returns explicitly defined CatchedError. Function captures stack at the point of calling.

func Wrap added in v0.0.6

func Wrap(err error, ce *CatchedError) *CatchedError

func (*CatchedError) Alarm added in v0.0.2

func (ce *CatchedError) Alarm(a Alarmer)

Alarm send error to Alarmer. Intended usage is real-time SRE notification if critical error.

func (*CatchedError) AllMessages added in v0.0.2

func (ce *CatchedError) AllMessages(exceptProtected bool) []string

AllMessages returns all error text messages including top (last) message. The last message is in the beginning of slice.

func (*CatchedError) Capture added in v0.0.2

func (ce *CatchedError) Capture() *CatchedError

Capture captures stack frames. Recommended to use when raised predefined errors.

var ErrInvalidCustomerID = errors.New("invalid customer id")

if c, ok := customers[id]; ok {
	  return ErrInvalidCustomerID.Capture()
}

func (*CatchedError) Code

func (ce *CatchedError) Code(code string) *CatchedError

Code sets business code of.

func (*CatchedError) Critical

func (ce *CatchedError) Critical() *CatchedError

Critical sets severity level to Critical.

func (CatchedError) Error

func (ce CatchedError) Error() string

CatchedError implements golang standard Error interface. Returns string, taking into account setting ErrorMethodMode.

If ErrorMethodMode == Multi, results build using LIFO principle.

func (*CatchedError) Fields

func (ce *CatchedError) Fields() map[string]interface{}

Fields returns all key/value pairs associated with error.

func (*CatchedError) Frames

func (ce *CatchedError) Frames() []Frame

Frames returns callstack frames.

func (*CatchedError) Get

func (ce *CatchedError) Get(key string) (interface{}, bool)

Get returns value by key.

func (*CatchedError) GetCode added in v0.0.2

func (ce *CatchedError) GetCode() string

func (*CatchedError) GetDefault

func (ce *CatchedError) GetDefault(key string, def interface{}) interface{}

GetDefault returns value by key. Returns def if not found.

func (*CatchedError) GetSeverity added in v0.0.2

func (ce *CatchedError) GetSeverity() SeverityLevel

func (*CatchedError) IsNotFound

func (ce *CatchedError) IsNotFound() bool

IsNotFound returns true if StatusCode is 404.

func (*CatchedError) Last

func (ce *CatchedError) Last() WrappedError

Last returns the last wrap of error .

func (*CatchedError) Len

func (ce *CatchedError) Len() int

Len returns amount of errors wrapped + 1 or zero if nil.

func (*CatchedError) Medium

func (ce *CatchedError) Medium() *CatchedError

Medium sets severity level to Medium. It's ignored if current level Critical.

func (*CatchedError) Msg

func (ce *CatchedError) Msg(s string) error

Msg sets or replaces latest error's text message. If message different previous error pushed to error stack.

func (*CatchedError) Protect

func (ce *CatchedError) Protect() *CatchedError

Protect marks error as protected. An error with protection will not be visible to the user.

func (*CatchedError) Raise added in v0.2.3

func (ce *CatchedError) Raise() *CatchedError

Capture captures stack frames. Recommended to use when raised predefined errors.

var ErrInvalidCustomerID = errors.New("invalid customer id")

if c, ok := customers[id]; ok {
	  return ErrInvalidCustomerID.Capture()
}

func (*CatchedError) Set

func (ce *CatchedError) Set(key string, val interface{}) *CatchedError

Set associates a single key with value.

func (*CatchedError) SetPairs

func (ce *CatchedError) SetPairs(kvpairs ...interface{}) *CatchedError

SetPairs associates multiple key/value pairs. SetPairs("id", 10, "name", "John") if amount of parameters is odd, SetPairs("id", 10, "name") uses LastNonPairedValue as the last value.

func (*CatchedError) SetStrs

func (ce *CatchedError) SetStrs(key string, strs ...string) *CatchedError

SetStrs accociates with the key multiple strings.

func (*CatchedError) SetVals

func (ce *CatchedError) SetVals(key string, vals ...interface{}) *CatchedError

SetVals accociates with the key multiple interfaces.

func (*CatchedError) Severity

func (ce *CatchedError) Severity(level SeverityLevel) *CatchedError

Severity overwrites error's severity level.

func (*CatchedError) StatusCode

func (ce *CatchedError) StatusCode(code int) *CatchedError

StatusCode sets HTTP response code, recommended to be assigned. StatusCode 404 is used by

func (*CatchedError) WrappedErrors added in v0.0.2

func (ce *CatchedError) WrappedErrors() []WrappedError

WrappedErrors returns all errors holding by CatchedError.

func (*CatchedError) WrappedMessages added in v0.0.2

func (ce *CatchedError) WrappedMessages(exceptProtected bool) []string

WrappedMessages returns error messages of wrapped errors except last message.

type FormattingFlag added in v0.0.2

type FormattingFlag uint8
const (

	// AddStack - add stack in the JSON.
	AddStack FormattingFlag = 1 << iota

	// AddProtected - add protected errors in the JSON.
	AddProtected

	// AddProtected - add key/value pairs.
	AddFields

	// AddWrappedErrors - add to the output previous errors.
	AddWrappedErrors
)

type Frame

type Frame struct {
	Function string `json:"function"`
	File     string `json:"file"`
	Line     int    `json:"line"`
}

Frame describes content of a single stack frame stored with error.

func DefaultCallerFrames added in v0.0.2

func DefaultCallerFrames(offset int) []Frame

DefaultCallerFrames returns default implementation of call frames collector.

type Mode

type Mode int

Mode describes allowed methods of response returned Error().

const (
	// Multi return all error messages separated by ": ".
	Multi Mode = 0

	// Single return message of last error in the stack.
	Single Mode = 1
)

type SeverityLevel

type SeverityLevel int

SeverityLevel describes error severity levels.

const (
	// Tiny classifies as expected, managed errors that do not require administrator attention.
	// It's not recommended to write a call stack to the journal file.
	//
	// Example: error related to validation of entered form fields.
	Tiny SeverityLevel = iota

	// Medium classifies an regular error. A call stack is written to the log.
	Medium

	// Critical classifies a significant error, requiring immediate attention.
	// An error occurrence fact shall be passed to the administrator in all possible ways.
	// A call stack is written to the log.
	Critical
)

func (SeverityLevel) MarshalJSON

func (sl SeverityLevel) MarshalJSON() ([]byte, error)

MarshalJSON implements json/Marshaller interface.

func (SeverityLevel) String

func (sl SeverityLevel) String() string

String returns severity level string representation.

type WrappedError

type WrappedError struct {

	// Message holds final error's Message.
	Message string `json:"message"`

	// Severity) holds severity level.
	Severity SeverityLevel `json:"severity"`

	// StatusCode holds HTTP status code, what is recommended to assign to HTTP response
	// if value is specified (above zero).
	StatusCode int `json:"-"`

	// Code holds application error code.
	Code string `json:"code,omitempty"`

	// Protected
	Protected bool `json:"-"`
	// contains filtered or unexported fields
}

WrappedError is a structure defining wrapped error. It's public to be able customize logging and failed HTTP responses.

func (WrappedError) Err

func (we WrappedError) Err() error

Err returns original error.

func (WrappedError) Error

func (we WrappedError) Error() string

Error implements standard Error interface.

Jump to

Keyboard shortcuts

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