Documentation
¶
Overview ¶
Package elk provides comprehensive models and utilities for better error handling with the focus on comprehensiveness, tracability and ease of use.
Example (Error) ¶
package main import ( "encoding/json" "log" "os" "github.com/studio-b12/elk" ) var ( ErrorReadFile = elk.ErrorCode("files:failed-reading-file") ErrorParsingConfig = elk.ErrorCode("config:failed-parsing") ErrorReadingConfig = elk.ErrorCode("config:failed-reading") ) func readFile() ([]byte, error) { data, err := os.ReadFile("does/not/exist") if err != nil { return nil, elk.Wrap(ErrorReadFile, err, "failed reading file") } return data, nil } type configModel struct { BindAddress string LogLevel int } func parseConfig() (cfg configModel, err error) { data, err := readFile() if err != nil { return configModel{}, elk.Wrap(ErrorReadFile, err, "failed reading config file") } err = json.Unmarshal(data, &cfg) if err != nil { return configModel{}, elk.Wrap(ErrorParsingConfig, err, "failed parsing config data") } return cfg, nil } func main() { _, err := parseConfig() if err != nil { log.Fatalf("config parsing failed: %v", err) } }
Output:
Example (Formatting) ¶
package main import ( "errors" "fmt" "github.com/studio-b12/elk" ) func main() { err := errors.New("some normal error") fmt.Printf("%s\n", err) err = elk.Wrap(elk.CodeUnexpected, err, "Oh no!", "anyway") fmt.Printf("%s\n", err) // Print with callstack of depth 5 fmt.Printf("%+5v\n", err) // Print detailed error stack fmt.Printf("%#v\n", err) }
Output:
Example (Inner) ¶
package main import ( "errors" "fmt" "github.com/studio-b12/elk" ) type StatusError struct { elk.InnerError StatusCode int } func NewStatusError(inner error, status int) error { var s StatusError s.Inner = inner s.StatusCode = status return s } func (t StatusError) Error() string { return fmt.Sprintf("%s (%d)", t.Inner.Error(), t.StatusCode) } func main() { err := errors.New("not found") statusErr := NewStatusError(err, 404) fmt.Println(statusErr.Error()) // Because elk.InnerError implements the Error() // as well as the Unwrap() method, StatusError inherits // these methods unless they are overridden. inner := errors.Unwrap(statusErr) fmt.Println(inner == err) }
Output: not found (404) true
Index ¶
- Constants
- func As[T error](err error) (t T, ok bool)
- func IsOfType[T error](err error) bool
- func Json(err error, statusCode int) ([]byte, error)
- func JsonString(err error, statusCode int) (string, error)
- func MustJson(err error, statusCode int) []byte
- func MustJsonString(err error, statusCode int) string
- func UnwrapFull(err error) error
- type CallFrame
- type CallStack
- type Error
- func Cast(err error, fallback ...ErrorCode) Error
- func NewError(code ErrorCode, message ...string) Error
- func NewErrorf(code ErrorCode, format string, a ...any) Error
- func Wrap(code ErrorCode, err error, message ...string) Error
- func WrapCopyCode(err error, message ...string) Error
- func WrapCopyCodef(err error, format string, a ...any) Error
- func Wrapf(code ErrorCode, err error, format string, a ...any) Error
- type ErrorCode
- type ErrorResponseModel
- type HasCallStack
- type HasCode
- type HasDetails
- type HasFormat
- type HasMessage
- type InnerError
Examples ¶
Constants ¶
const (
CodeUnexpected = ErrorCode("unexpected-error")
)
Variables ¶
This section is empty.
Functions ¶
func As ¶
As applies errors.As() on the given err using the given type T as target for the unwrapping.
Refer to the documentation of errors.As() for more details: https://pkg.go.dev/errors#As
Example ¶
package main import ( "errors" "fmt" "github.com/studio-b12/elk" ) func main() { const ErrUnexpected = elk.ErrorCode("unexpected-error") type WrappedError struct { elk.InnerError } err := errors.New("some error") err = elk.Wrap(elk.CodeUnexpected, err, "Some message") err = WrappedError{InnerError: elk.InnerError{Inner: err}} Error, ok := elk.As[elk.Error](err) if ok { message := Error.Message() fmt.Println(message) } }
Output: Some message
func IsOfType ¶
IsOfType returns true when the given error is of the type of T.
If not and the error can be unwrapped, the unwrapped error will be checked until it either matches the type T or can not be further unwrapped.
Example ¶
package main import ( "errors" "fmt" "github.com/studio-b12/elk" ) func main() { type WrappedError struct { elk.InnerError } innerError := errors.New("some error") var err error = elk.Wrap(elk.CodeUnexpected, innerError, "Some message") err = WrappedError{InnerError: elk.InnerError{Inner: err}} is := elk.IsOfType[elk.Error](innerError) fmt.Println("innerError:", is) is = elk.IsOfType[elk.Error](err) fmt.Println("err:", is) }
Output: innerError: false err: true
func Json ¶
Json takes an error and marshals it into a JSON byte slice.
If err is a wrapped error, the inner error will be represented in the "error" field. Otherwise, the result of Error() on err will be represented in the "error" field. This does only apply though if exposeError is passed as true. By default, "error" will contain no information about the actual error to prevent unintended information leakage.
If the err implements HasCode, the code of the error will be represented in the "code" field of the JSON result.
If the err implements HasMessage, the JSON object will contain it as "message" field, if present.
When the JSON marshal fails, an error is returned.
Example ¶
package main import ( "errors" "fmt" "github.com/studio-b12/elk" ) type DetailedError struct { elk.InnerError details any } func (t DetailedError) Details() any { return t.details } func main() { strErr := errors.New("some error") mErr := elk.Wrap("some-error-code", strErr, "some message") json, _ := elk.Json(strErr, 0) fmt.Println(string(json)) json, _ = elk.Json(strErr, 400) fmt.Println(string(json)) json, _ = elk.Json(mErr, 0) fmt.Println(string(json)) json, _ = elk.Json(mErr, 400) fmt.Println(string(json)) dtErr := DetailedError{} dtErr.Inner = elk.NewError("some-error", "an error with details") dtErr.details = struct { Foo string Bar int }{ Foo: "foo", Bar: 123, } json, _ = elk.Json(dtErr, 500) fmt.Println(string(json)) dteErr := elk.Wrap("some-detailed-error-wrapped", dtErr, "some detailed error wrapped") json, _ = elk.Json(dteErr, 500) fmt.Println(string(json)) }
Output: { "Code": "unexpected-error" } { "Code": "unexpected-error", "Status": 400 } { "Code": "some-error-code", "Message": "some message" } { "Code": "some-error-code", "Message": "some message", "Status": 400 } { "Code": "unexpected-error", "Status": 500, "Details": { "Foo": "foo", "Bar": 123 } } { "Code": "some-detailed-error-wrapped", "Message": "some detailed error wrapped", "Status": 500, "Details": { "Foo": "foo", "Bar": 123 } }
func JsonString ¶
JsonString behaves the same as Json() but returns the result as string instead of a slice of bytes.
func MustJsonString ¶
MustJsonString is an alias for JsonString but panics when the call to Json returns an error.
func UnwrapFull ¶
UnwrapFull takes an error and unwraps it until it can not be unwrapped anymore. Then, the last error is returned.
Example ¶
package main import ( "errors" "fmt" "github.com/studio-b12/elk" ) func main() { type WrappedError struct { elk.InnerError } var err error originErr := errors.New("some error") err = elk.Wrap(elk.CodeUnexpected, originErr, "Some message") err = WrappedError{InnerError: elk.InnerError{Inner: err}} err = elk.UnwrapFull(err) fmt.Println(err == originErr) }
Output: true
Types ¶
type CallFrame ¶
CallFrame is a type alias for runtime.Frame with additional formatting functionailty used in downstream functions.
type CallStack ¶
type CallStack struct {
// contains filtered or unexported fields
}
CallStack contains the list of called runtime.Frames in the call chain with an offset from which frames are reported.
func (*CallStack) First ¶
First is shorthand for At(0) and returns the first frame in the CallStack, if available.
func (*CallStack) Frames ¶
Frames returns the offset slice of called runtime.Frame's in the recorded call stack.
type Error ¶
type Error struct { InnerError // contains filtered or unexported fields }
Error contains a wrapped inner error, an optional message, optional details objects and a CallStack from where the error has been created.
func Cast ¶
Cast takes an arbitrary error and if it is not of type Error, it will be wrapped in a new Error which is then returned. If fallback is passed, it will be used as the ErrorCode of the new Error. Otherwise, CodeUnexpected is used.
If err is a joined error created with `errors.Join`, it will be inspected for contained elk Error elements. Depending on the contents of the join, it will be treated as following:
- If the join contains exactly one elk Error, it will be returned.
- If it contains no elk Error elements, the error will be wrapped using the passed fallback ErrorCode or CodeUnexpected.
- If it contains more than one elk Error, a new wrapped Error is returned as well with the passed fallback ErrorCode or CodeUnexpected.
If err is of type Error, it is simply returned unchanged.
func NewErrorf ¶ added in v0.2.0
NewErrorf creates a new Error with the given code and message formatted according to the given format specification.
func Wrap ¶
Wrap takes an ErrorCode, error and an optional message and creates a new wrapped Error containing the passed error.
func WrapCopyCode ¶ added in v0.2.0
WrapCopyCode wraps the error with an optional message keeping the error code of the wrapped error. If the wrapped error does not have a error code, CodeUnexpected is set insetad.
func WrapCopyCodef ¶ added in v0.2.0
WrapCopyCodef wraps the error with a message formatted according to the given format specification keeping the error code of the wrapped error. If the wrapped error does not have a error code, CodeUnexpected is set instead.
func Wrapf ¶ added in v0.2.0
Wrapf takes an ErrorCode, error and a message formatted according to the given format specification and creates a new wrapped Error containing the passed error.
func (Error) CallStack ¶
CallStack returns the errors CallStack starting from where the Error has been created.
func (Error) Format ¶
Format implements custom formatting rules used with the formatting functionalities in the fmt package.
%s, %q
Prints the message of the error, if available. Otherwise, the %s format of the inner error is represented. If the inner error is nil and no message is set, the error code is printed.
%v
Prints a more detailed representation of the error. Without any flags, the error is printed in the format `<{errorCode}> {message} ({innerError})`.
By passing the `+` flag, the inner error is represented in a seperate line. Also, by using the precision parameter, you can specify the depth of the represented callstack (i.E. `%+.5v` - prints a callstack of depth 5). Otherwise, no callstack will be printed.
Bypassing the `#` flag, an even more verbose representation of the error is printed. It shows the complete chain of errors wrapped in the Error with information about message, code, initiation origin and type of the error. With the precision parameter, you can define the depth of the unwrapping. The default value is 100, if not specified.
func (Error) ToResponseModel ¶ added in v0.4.0
func (t Error) ToResponseModel(statusCode int) (model ErrorResponseModel)
ToResponseModel transforms the
type ErrorResponseModel ¶ added in v0.4.0
type ErrorResponseModel struct { Code ErrorCode // The error code Message string `json:",omitempty"` // An optional short message to further specify the error Status int `json:",omitempty"` // An optional platform- or protocol-specific status code; i.e. HTTP status code Details any `json:",omitempty"` // Optional additional detailed context for the error }
ErrorResponseModel is used to encode an Error into an API response.
type HasCallStack ¶
HasCode describes an error which has a CallStack.
type HasCode ¶
type HasCode interface { error // Code returns the inner ErrorCode of // the error. Code() ErrorCode }
HasCode describes an error which has an ErrorCode.
type HasDetails ¶ added in v0.4.0
type HasFormat ¶
type HasFormat interface { error // Formatted returns the error details // as formatted string. Formatted() string }
HasFormat describes an error with additional information which can be accessed as a formatted string.
type HasMessage ¶
HasMessage describes an error which has an additional message.
type InnerError ¶
type InnerError struct {
Inner error
}
InnerError wraps an inner error which's message is returned by calling Error() on it and which can be unwrapped using Unwrap().
InnerError is mostly used as anonymous field by other errors to "inherit" the unwrap functionality of contained errors.
func (InnerError) Error ¶
func (t InnerError) Error() string
func (InnerError) Unwrap ¶
func (t InnerError) Unwrap() error