metaerr

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2023 License: MIT Imports: 5 Imported by: 0

README

MetaErr

GoDoc Go Report Card codecov

Metaerr is a golang package to create or wrap errors with custom metadata and location.

Why

I used github.com/pkg/errors before, and the stack traces were extensive (like Java) and not very useful. Then, I came across the Fault library, which was amazing, but the way I wanted to use it clashed with some of the opinions embedded in the library.

This is why I decided to create this simple library. It utilizes the same "stack trace" model as Fault, in the sense that you will see the stack pertaining to the locations of error creation, and not the stack trace that led to the error's creation.

The next feature it offers is the ability to add any number of key-value metadata entries to each error, including wrapped errors. This is useful if you want to attach metadata at the time of error creation and then leverage that metadata during resolution. A common use case is having a generic HTTP error handler for an API that can use the metadata to determine the HTTP status or construct an error payload to send to the user. Another use case would be logging and alerting. If you convert the metadata into fields in a JSON logger, you could have different alerting rules for logged ERRORS based on the metadata; for example, errors with the metadata tag containing "security" could trigger an immediate alert.

Install

go get -u github.com/quantumcycle/metaerr

Usage

Metaerr can be used with the Go standard errors package, and they are also compatible with error wrapping introduced in Go 1.13.

To create an new MetaErr from a string, use

err := metaerr.New("failure")

To create a new MetaErr by wrapping an existing error, use

err := metaerr.Wrap(err, "failure")

The next step, once you have a Metaerr, is to add metadata to it. You need to create a function that matches the metaerr.ErrorMetadata signature. For your convenience, you can use metaerr.StringMeta, but you can also create your own. Ultimately, all metadata entries are stored as strings.

//Create an metadata called ErrorCode
var ErrorCode = metaerr.StringMeta("error_code")

func main() {
	rootCause := metaerr.New("failure")
	err := metaerr.Wrap(rootCause, "cannot fetch content").Meta(ErrorCode("x01"))
	fmt.Printf("%+v", err)
}

will print (... will be your project location)

cannot fetch content [error_code=x01]
        at .../quantumcycle/metaerr/cmd/main.go:13
failure
        at .../quantumcycle/metaerr/cmd/main.go:12
Getting the err message, location, and metadata

In the example above, we use the Printf formatting to display the error, metadata and location all in one gulp. You can however use the provided helper function to get the individual parts

err := metaerr.New("failure")
err.Error() //returns failure
err.Location() //returns .../mysource/mypackage/file.go:22

// will print error_code:x01
meta := metaerr.GetMeta(err, false)
for k, values := range meta {
  for _, val := range values {
    fmt.Println(k + ":" + val)
  }
}

Options

You can provide options to modify the errors during creation. Currently, there is a single option called WithLocationSkip. By default, when creating an error, Metaerr will skip 2 call stack frames to determine the error's creation location. This works well when you call Metaerr directly at the place where the error is created in your codebase. However, there is a use case where you might want to create an error factory function for common scenarios to initialize the error with some standard metadata. In this case, if you use the standard metaerr.New function, the reported location will be the line where metaerr.New is called, which may be within your error factory function. You probably don't want to have all your locations pointing to the same line. To address this, you can use the metaerr.WithLocationSkip option to add additional call stack skips to determine the location. Here is an example:

package main

import (
	"fmt"

	"github.com/quantumcycle/metaerr"
)

var Tag = metaerr.StringMeta("tag")

func CreateDatabaseError(reason string) error {
	return metaerr.New(reason, metaerr.WithLocationSkip(1)).Meta(Tag("database"))
}

func main() {
	dbErr := CreateDatabaseError("no such table [User]")
	fmt.Printf("%+v", dbErr)
}

which will output

no such table [User] [tag=database]
        at /home/matdurand/sources/github/quantumcycle/metaerr/cmd/main.go:16

Without the WithLocationSkip option, the reported location would be line 12, inside the CreateDatabaseError function. Having all our errors pointing to this specific line would ne useless.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetMeta

func GetMeta(err error, nested bool) map[string][]string

func StringMeta

func StringMeta(name string) func(val string) ErrorMetadata

func StringerMeta added in v0.2.0

func StringerMeta[T fmt.Stringer](name string) func(val T) ErrorMetadata

Types

type Error

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

func New

func New(reason string, opt ...Option) Error

func Wrap

func Wrap(err error, msg string, opt ...Option) *Error

func (Error) Error

func (e Error) Error() string

func (Error) Format

func (e Error) Format(s fmt.State, verb rune)

func (Error) Location

func (e Error) Location() string

func (Error) Meta

func (e Error) Meta(metas ...ErrorMetadata) Error

func (Error) Unwrap

func (e Error) Unwrap() error

type ErrorMetadata

type ErrorMetadata = func(err Error) []MetaValue

type MetaValue

type MetaValue struct {
	Name   string
	Values []string
}

type Option

type Option func(*Error)

func WithLocationSkip

func WithLocationSkip(additionalCallerSkip int) Option

Jump to

Keyboard shortcuts

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