oops

package module
v1.4.1 Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2025 License: MIT Imports: 7 Imported by: 0

README

Oops, Errors

Go Reference

Error handling for adding attributes, error codes, and source location to errors when logging them.

Inspired by https://github.com/samber/oops with a minimal API.

Works with log/slog through a LogValue that decorates the logged error attribute with the attributes and source location to any oops.Error.

Example

func (s *Service)process(ctx context.Context, id string) error {
    now := time.Now()
    if id == "" {
        return oops.New("process failed: bad input").Code(400)
    }

    results, err := s.Run(ctx, id)
    if err != nil {
        if errors.Is(err, context.DeadlineExceeded) {
            return oops.Errorf("process failed: deadline exceeded %w", err).With("duration", time.Since(now))
        }
        return oops.Wrap(err).With("id", id, "method", "Run")
    }

    return results, nil
}

func main() {
    ctx := context.Background()
    logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
    s := &Service{}

    _, err := s.process(ctx, "oops")
    if err != nil {
         if oops.Code(err) == 400 {
             logger.Warn("process input rejected", "error", err)
         } else {
             logger.Error("process failed", "error", err)
         }
    }
}
{
   "time":"2025-02-01T21:59:54.959869-07:00",
   "level":"ERROR",
   "msg":"process failed",
   "error":{
      "err":"process not found",
      "id":"oops",
      "method":"Run",
      "source":[
         {
            "file":"#####/github.com/jesse0michael/oops/README.md",
            "function":"github.com/jesse0michael/oops.process",
            "line":30
         }
      ]
   }
}

This example highlights some of the different ways to create and decorate errors by dropping in the oops package. Create a new error with oops.New or wrap an existing error with oops.Wrap or use oops.Errorf to format an error message.

Add attributes to the error with the With method. The With method can take any number of key-value pairs or slog.Attr's.

Set error codes with the Code method to indicate specific error types or status codes. You can retrieve the error code using the oops.Code() function, which returns the explicitly set code or looks for a "code" attribute set as an integer.

Wrapping or formatting an oops.Error with %w will append the source location with the new error and preserve attributes of any prior error.

Includes a slog Handler that will preserve the specified error field used with the oops.Error as the string representation of the inner error, and add the error decoration to the slog key oops.

logger := slog.New(oops.NewOopsHandler(slog.NewJSONHandler(os.Stdout, nil)))
{
   "time":"2025-02-01T21:59:54.959869-07:00",
   "level":"ERROR",
   "msg":"process failed",
   "error": "process not found",
   "oops":{
      "id":"oops",
      "method":"Run",
      "source":[
         {
            "file":"#####/github.com/jesse0michael/oops/README.md",
            "function":"github.com/jesse0michael/oops.process",
            "line":30
         }
      ]
   }
}

[!NOTE]
runtime source location can be disabled with oops.EnableSource(false).

The slog handler attribute field can be overridden with oops.SetAttrField("field").

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Code added in v1.4.0

func Code(err error) int

Code returns the oops error code, if the error is an oops error. if the error code is not set, it looks for a "code" attribute set to an int.

func EnableSource added in v1.2.0

func EnableSource(enabled bool)

EnableSource enables or disables source tracking. default is true.

func NewOopsHandler added in v1.2.0

func NewOopsHandler(handler slog.Handler) slog.Handler

NewOopsHandler creates a handler that logs oops errors with additional context.

func SetAttrField added in v1.2.0

func SetAttrField(field string)

SetAttrField overrides the field used to oops error attributes. default is "oops".

func Wrap

func Wrap(err error, args ...any) error

Wrap wraps an error as an oops error. supports supports [string, any]... pairs or slog.Attr values.

Example
l := logger()

err := errors.New("oops")
err = Wrap(err, "id", "wrap")

fmt.Println(err)
l.Error("error", "err", err)
Output:

oops
level=ERROR msg=error err.err=oops err.id=wrap err.source="[{Function:github.com/jesse0michael/oops.ExampleWrap File:oops_test.go Line:29}]"
Example (Nil)
l := logger()

err := Wrap(nil)

if err != nil {
	fmt.Println(err)
}
l.Error("error", "err", err)
Output:

level=ERROR msg=error err=<nil>
Example (OopsError)
l := logger()

oopsErr := New("oops").With("id", "new", "component", "example")
err := Wrap(oopsErr, "id", "wrap")

fmt.Println(err)
l.Error("error", "err", err)
Output:

oops
level=ERROR msg=error err.err=oops err.component=example err.id=wrap err.source="[{Function:github.com/jesse0michael/oops.ExampleWrap_oopsError File:oops_test.go Line:42} {Function:github.com/jesse0michael/oops.ExampleWrap_oopsError File:oops_test.go Line:43}]"

Types

type Error

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

func Errorf

func Errorf(format string, args ...any) *Error

Errorf formats a string and returns a new oops error.

Example
l := logger()

err := Errorf("failure: %s, %w", "Errorf", errors.New("oops"))

fmt.Println(err)
l.Error("error", "err", err)
Output:

failure: Errorf, oops
level=ERROR msg=error err.err="failure: Errorf, oops" err.source="[{Function:github.com/jesse0michael/oops.ExampleErrorf File:oops_test.go Line:56}]"
Example (WrapOopsError)
l := logger()

err := New("oops").With("id", "new", "component", "example")
err = Errorf("failure: %s, %w", "Errorf", err).With("id", "wrap")

fmt.Println(err)
l.Error("error", "err", err)
Output:

failure: Errorf, oops
level=ERROR msg=error err.err="failure: Errorf, oops" err.component=example err.id=wrap err.source="[{Function:github.com/jesse0michael/oops.ExampleErrorf_wrapOopsError File:oops_test.go Line:70} {Function:github.com/jesse0michael/oops.ExampleErrorf_wrapOopsError File:oops_test.go Line:69}]"

func New

func New(msg string) *Error

New returns a new oops error.

Example
l := logger()

err := New("oops").With("id", "new")

fmt.Println(err)
l.Error("error", "err", err)
Output:

oops
level=ERROR msg=error err.err=oops err.id=new err.source="[{Function:github.com/jesse0michael/oops.ExampleNew File:oops_test.go Line:15}]"
Example (NonOopsWrappedError)
// This example shows how the LogValue won't be hit for a wrapped error :(
l := logger()

var err error
err = New("oops").With("id", "new", "component", "example")
err = &wrappedError{underlying: err}

fmt.Println(err)
l.Error("error", "err", err)
Output:

oops
level=ERROR msg=error err=oops

func (*Error) Code added in v1.4.0

func (e *Error) Code(code int) *Error

Code sets the error code.

Example
l := logger()

err := New("oops").Code(404)

fmt.Println(err)
l.Error("error", "err", err)
fmt.Println(Code(err))
Output:

oops
level=ERROR msg=error err.err=oops err.source="[{Function:github.com/jesse0michael/oops.ExampleError_Code File:oops_test.go Line:97}]"
404

func (*Error) Error

func (e *Error) Error() string

Error returns undecorated error.

func (*Error) LogAttrs added in v1.2.0

func (e *Error) LogAttrs() []slog.Attr

func (*Error) LogValue

func (e *Error) LogValue() slog.Value

func (*Error) Unwrap added in v1.4.1

func (e *Error) Unwrap() error

Unwrap allows errors.As and errors.Is to find the inner error.

func (*Error) With

func (e *Error) With(args ...any) *Error

With collects key-value pairs to return with the error. supports supports [string, any]... pairs or slog.Attr values.

type Handler added in v1.2.0

type Handler struct {
	slog.Handler
}

Handler logs oops errors with additional context.

func (Handler) Handle added in v1.2.0

func (h Handler) Handle(ctx context.Context, record slog.Record) error

Handle implements slog.Handler.

func (Handler) WithAttrs added in v1.2.0

func (h Handler) WithAttrs(attrs []slog.Attr) slog.Handler

func (Handler) WithGroup added in v1.2.0

func (h Handler) WithGroup(name string) slog.Handler

type Source

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

Jump to

Keyboard shortcuts

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