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 ¶
- func As(err error, target interface{}) bool
- func Is(err, target error) bool
- func Join(err ...error) error
- func New(text string) error
- func Unwrap(err error) error
- type Category
- type EOption
- type Error
- func (e Error) Error() string
- func (e Error) Is(target error) bool
- func (e Error) Log(ctx context.Context, callID, customerID string, req any)
- func (e Error) LogAttrs(ctx context.Context) []slog.Attr
- func (e Error) TraceAttrs(ctx context.Context, prepend string, attrs span.Attributes) span.Attributes
- func (e Error) Unwrap() error
- type LogAttrer
- type TraceAttrer
- type Type
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func As ¶
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 Join ¶
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.
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 ¶
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 ¶
E creates a new Error with the given parameters. If the message is already an Error, it will be returned instead.
func (Error) Is ¶
Is implements the errors.Is() interface. An Error is equal to another Error if the category and type are the same.
func (Error) Log ¶
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) 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.
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.