errors

package
v0.0.0-...-ab66aac Latest Latest
Warning

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

Go to latest
Published: Mar 9, 2025 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package errors provides an error type suitable for all errors returned by packages used to build services using the base/ set of packages. This error type can be used to automatically handle error logging and RPC error returns.

This package should be used to build a service specific error package and not used directly.

Services should create their own errors packages. This can be achieved for new projects using the genproject tool. Otherwise, you can copy the following and fill it in. Remember that you must use "go generate" for everything to work.

package errors

import (
    "github.com/gostdlib/base/context"
	"github.com/gostdlib/base/errors"
)

//go:generate stringer -type=Category -linecomment

// Category represents the category of the error.
type Category uint32

func (c Category) Category() string {
	return c.String()
}

const (
	// CatUnknown represents an unknown category. This should not be used.
	CatUnknown Category = Category(0) // Unknown
	// ADD YOUR OWN CATEGORIES HERE
)

//go:generate stringer -type=Type -linecomment

// Type represents the type of the error.
type Type uint16

func (t Type) Type() string {
	return t.String()
}

const (
	// TypeUnknown represents an unknown type.
	TypeUnknown Type = Type(0) // Unknown

	// ADD YOUR OWN TYPES HERE
)

// LogAttrer is an interface that can be implemented by an error to return a list of attributes
// used in logging.
type LogAttrer = errors.LogAttrer

// Error is the error type for this service. Error implements github.com/gostdlib/base/errors.E .
type Error = errors.Error

// E creates a new Error with the given parameters.
// YOU CAN REPLACE this with your own base error constructor. See github.com/gostdlib/base/errors for more info.
func E(ctx context.Context, c errors.Category, t errors.Type, msg error, options ...errors.EOption) Error {
    return errors.E(ctx, c, t, msg, options...)
}

You should include a file for your package called stdlib.go that is a copy of base/errors/stdlib/stdlib.go . This will prevent needing to import multiple "errors" packages with renaming.

This package is meant to allow extended errors that add additional attributes to our "Error" type. For example, you could create a SQLQueryErr like so:

// SQLQueryErr is an example of a custom error that can be used to wrap a SQL error for more information.
// Should be created with NewSQLQueryErr().
type SQLQueryErr struct {
	// Query is the SQL query that was being executed.
	Query string
	// Msg is the error message from the SQL query.
	Msg   error
}

// NewSQLQueryErr creates a new SQLQueryErr wrapped in Error.
func NewSQLQueryErr(q string, msg error) Error {
	return E(
		CatInternal,
		TypeUnknown,
		SQLQueryErr{
			Query: q,
			Msg:   msg,
		},
	)
}

// Error returns the error message.
func (s SQLQueryErr) Error() string {
	return s.Msg.Error()
}

// Is returns true if the target is an SQLQueryErr type regardless of the Query or Msg.
func (s SQLQueryErr) Is(target error) bool {
	if _, ok := target.(SQLQueryErr); ok {
		return true
	}
	return false
}

// Unwrap unwraps the error.
func (s SQLQueryErr) Unwrap() error {
	return s.Msg
}

// Attrs implements the Attrer.Attrs() interface.
func (s SQLQueryErr) Attrs() []slog.Attr {
	// You will notice here that I group the attributes with a category that includes the package path.
	// This is to prevent attribute name collisions with other packages.
	return []slog.Attr{slog.Group("package/path.SQLQueryErr", "Query", s.Query)}
}

Now a program can create a compatible error that will detail our additional attributes for logging.

// Example of creating a SQLQueryErr
err := errors.NewSQLQueryErr("SELECT * FROM users", errors.New("SQL Error"))

In the case you want to have a more detailed top level error message than Error provides, it is simple to provide this extra data in the error message. Simply replace the `E` constructor in your `errors` package with custom one:

// Args are arguments for creating our Error type.
type Args struct {
	Category Category
	Type type
	Msg error

	ExtraField string
}

// Extended is an example of an the extended Error type containing the extra field.
// This extra field will ge promoted to the top level of the log message.
type Extended struct {
	ExtraField string
}

// Error returns the error message.
func (s Extended) Error() string {
	return s.Msg.Error()
}

// Unwrap unwraps the error.
func (s Extended) Unwrap() error {
	return s.Msg
}

// Attrs implements the Attrer.Attrs() interface.
func (s Extended) Attrs() []slog.Attr {
	// Notice that unlike in the SQLQueryErr, we are not grouping the attributes.
	// This will cause the attributes to be at the top level of the log message.
	// This is generally only done in places like this where we are extending the base error.
	return []slog.Attr{
		slog.Any("ExtraField", s.ExtraField),
	}
}

// E creates a new Error with the given parameters.
func E(ctx context.Context, args Args) E {
	return errors.E(ctx, s.Category, s.Type, Extended{ExtraField: s.ExtraField, Msg: s.Msg})
}

Our E constructor now returns an Extended type that includes the exta field we wanted and can be used to wrap other errors. This pattern can easily be extended to include more fields as needed if all errors require these additional fields.

Note: This package returns concrete types. While our constructors return our concrete type, functions or methods returning the value should always return the error interface and never our custom "Error" concrete type.

There is a sub-directory called example/ that shows an errors package for a service which can be the base for your package.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func As

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

As finds the first error in err's chain that matches target, and if so, sets target to that error value and returns true.

func Is

func Is(err, target error) bool

Is reports whether any error in err's chain matches target.

func Join

func Join(err ...error) error

Join returns an error that wraps the given errors. Any nil error values are discarded. Join returns nil if every value in errs is nil. The error formats as the concatenation of the strings obtained by calling the Error method of each element of errs, with a newline between each string. A non-nil error returned by Join implements the Unwrap() []error method.

func New

func New(text string) error

New returns an error that formats as the given text.

func Unwrap

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 Category

type Category interface {
	Category() string
}

Category is the category of the error.

type EOption

type EOption func(eOpts) eOpts

EOption is an optional argument for E().

func WithCallNum

func WithCallNum(i int) EOption

WithCallNum is used if you need to set the runtime.CallNum() in order to get the correct filename and line. This can happen if you create a call wrapper around E(), because you would then need to look up one more stack frame for every wrapper. This defaults to 1 which sets to the frame of the caller of E().

func WithStackTrace

func WithStackTrace() EOption

WithStackTrace will add a stack trace to the error. This is useful for debugging in certain rare cases. This is not recommended for general use as it can cause performance issues when errors are created frequently.

func WithSuppressTraceErr

func WithSuppressTraceErr() EOption

WithSuppressTraceErr will prevent the trace as being recorded with an error status. The trace will still receive the error message. This is useful for errors that are retried and you only want to get a status of error if the error is not resolved.

type Error

type Error struct {
	// Category is the category of the error. Should always be provided.
	Category Category
	// Type is the type of the error. This is a subcategory of the Category.
	// It is not always provided.
	Type Type
	// Msg is the message of the error.
	Msg error
	// MsgOveride is the message that should be used in place of the error message. This can happen
	// if the error message is sensitive or contains PII.
	MsgOverride string

	// Filename is the file that the error was created in. This is automatically
	// filled in by the E().
	Filename string
	// Line is the line that the error was created on. This is automatically
	// filled in by the E().
	Line int
	// ErrTime is the time that the error was created. This is automatically filled
	// in by E(). This is in UTC.
	ErrTime time.Time
	// StackTrace is the stack trace of the error. This is automatically filled
	// in by E() if WithStackTrace() is used.
	StackTrace string
}

Error is the basic error type that is used to represent an error. Users should create their own "errors" package for their service with an E() method that creates a type that returns Error with their information. Any type that implements Error should also be JSON serializable for logging output. Error represents an error that has a category and a type. Created with E().

func E

func E(ctx context.Context, c Category, t Type, msg error, options ...EOption) Error

E creates a new Error with the given parameters. If the message is already an Error, it will be returned instead.

func (Error) Error

func (e Error) Error() string

Error implements the error interface.

func (Error) Is

func (e Error) Is(target error) bool

Is implements the errors.Is() interface. An Error is equal to another Error if the category and type are the same.

func (Error) Log

func (e Error) Log(ctx context.Context, callID, customerID string, req any)

Log logs the error with the given callID and customerID for easy lookup. Also logs the request that caused the error. The request is expected to be JSON serializable. If the req is a proto, this will used protojson to marshal it.

func (Error) LogAttrs

func (e Error) LogAttrs(ctx context.Context) []slog.Attr

LogAttrs implements the LogAttrer.LogAttrs() interface.

func (Error) TraceAttrs

func (e Error) TraceAttrs(ctx context.Context, prepend string, attrs span.Attributes) span.Attributes

TraceAttrs converts the error to a list of trace attributes consumable by the OpenTelemetry trace package. This does not include attributes on the .Msg field. These are added to the attrs passed in and returned.

func (Error) Unwrap

func (e Error) Unwrap() error

Unwrap unwraps the error.

type LogAttrer

type LogAttrer interface {
	// LogAttrs returns a []slog.Attr that will be used in logging.
	LogAttrs(ctx context.Context) []slog.Attr
}

LogAttrer is an interface that can be implemented by an error to return a list of attributes used in logging.

type TraceAttrer

type TraceAttrer interface {
	TraceAttrs(ctx context.Context, prepend string, attrs span.Attributes) span.Attributes
}

TraceAttrer is an interface that can be implemented by an error to return a list of attributes used in tracing. Keys should be prepended by the given string. This is used by Error to add the package name of the error type attribute key to prevent collisions.

type Type

type Type interface {
	Type() string
}

Type is the type of the error.

Directories

Path Synopsis
Package errors provides the standard library's errors package with additional functionality.
Package errors provides the standard library's errors package with additional functionality.
proto
Package example is a reverse proxy.
Package example is a reverse proxy.

Jump to

Keyboard shortcuts

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