oops

package module
v1.19.3 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2025 License: MIT Imports: 18 Imported by: 257

README ΒΆ

Oops - Structured Error Handling for Go

Transform your Go error handling from "oops!" to "aha!" moments

tag Go Version GoDoc Build Status Go report Coverage Contributors License

Oops is a comprehensive Go error handling library that provides structured error management with rich contextual information. It's designed as a drop-in replacement for Go's built-in error, adding powerful features like stack traces, source code fragments, structured attributes, and developer-friendly debugging hints.

🎯 Key Features:

  • πŸ”§ Drop-in Replacement: Seamlessly replaces standard Go error handling
  • πŸ“Š Rich Context: Add structured attributes, user info
  • πŸ› Debug-Friendly: Out-of-the-box stacktraces and source code fragments
  • πŸ”— Error Chaining: Wrap and compose errors with additional context
  • πŸ›‘οΈ Panic Recovery: Built-in panic handling with error conversion
  • βœ… Assertions: One-line assertion helpers for validation
  • ⚑ Performance: Zero dependencies, lightweight and fast
  • πŸ“ Logger Integration: Works with all major Go logging libraries
  • βœ‚οΈ Separation of Concerns: Error handling and logging are separate jobs
  • 🍳 Easy Integration: No large refactoring required

[!WARNING]
Important: This is NOT a logging library. oops should complements your existing logging toolchain (zap, zerolog, logrus, slog, go-sentry...).

πŸ₯· Start hacking oops with this playground.

Table of content

Context rich error handling

In a few minutes, your logs will look like this:

πŸš€ Install

go get github.com/samber/oops

This library is v1 and follows SemVer strictly.

No breaking changes will be made to APIs before v2.0.0.

This library has no dependencies outside the Go standard library.

πŸ’‘ Quick start

This library provides a simple error builder for composing structured errors, with contextual attributes and stack trace.

Since oops.OopsError implements the error interface, you will be able to compose and wrap native errors with oops.OopsError.

import "github.com/samber/oops"

func main() {
    // Simple error with context
    err := oops.
        In("user-service").
        Tags("database", "postgres").
        Code("network_failure").
        User("user-123", "email", "foo@bar.com").
        With("path", "/hello/world").
        Errorf("failed to fetch user: %s", "connection timeout")
    
    // Error wrapping
    if err != nil {
        return oops.
            Trace("req-123").
            With("product_id", "456").
            Wrapf(err, "user operation failed")
    }
}

🧠 Spec

GoDoc: https://godoc.org/github.com/samber/oops

Error builder
Method Description
.New(message string) error Formats returns oops.OopsError object that satisfies error
.Errorf(format string, args ...any) error Formats an error and returns oops.OopsError object that satisfies error
.Wrap(err error) error Wraps an error into an oops.OopsError object that satisfies error
.Wrapf(err error, format string, args ...any) error Wraps an error into an oops.OopsError object that satisfies error and formats an error message
.Recover(cb func()) error Handle panic and returns oops.OopsError object that satisfies error.
.Recoverf(cb func(), format string, args ...any) error Handle panic and returns oops.OopsError object that satisfies error and formats an error message.
.Assert(condition bool) OopsErrorBuilder Panics if condition is false. Assertions can be chained.
.Assertf(condition bool, format string, args ...any) OopsErrorBuilder Panics if condition is false and formats an error message. Assertions can be chained.
.Join(err1 error, err2 error, ...) error Join returns an error that wraps the given errors.
Examples
// with error wrapping
err0 := oops.
    In("repository").
    Tags("database", "sql").
    Wrapf(sql.Exec(query), "could not fetch user")  // Wrapf returns nil when sql.Exec() is nil

// with panic recovery
err1 := oops.
    In("repository").
    Tags("database", "sql").
    Recover(func () {
        panic("caramba!")
    })

// with assertion
err2 := oops.
    In("repository").
    Tags("database", "sql").
    Recover(func () {
        // ...
        oops.Assertf(time.Now().Weekday() == 1, "This code should run on Monday only.")
        // ...
    })

// oops.New
err3 := oops.
    In("repository").
    Tags("database", "sql").
    New("an error message")

// oops.Errorf
err4 := oops.
    In("repository").
    Tags("database", "sql").
    Errorf("an error message: %d", 42)
Context

The library provides an error builder. Each method can be used standalone (eg: oops.With(...)) or from a previous builder instance (eg: oops.In("iam").User("user-42")).

The oops.OopsError builder must finish with either .Errorf(...), .Wrap(...), .Wrapf(...), .Join(...), .Recover(...) or .Recoverf(...).

Builder method Getter Description
.With(string, any) err.Context() map[string]any Supply a list of attributes key+value. Values of type func() any {} are accepted and evaluated lazily.
.WithContext(context.Context, ...any) err.Context() map[string]any Supply a list of values declared in context. Values of type func() any {} are accepted and evaluated lazily.
.Code(string) err.Code() string Set a code or slug that describes the error. Error messages are intended to be read by humans, but such code is expected to be read by machines and be transported over different services
.Public(string) err.Public() string Set a message that is safe to show to an end user
.Time(time.Time) err.Time() time.Time Set the error time (default: time.Now())
.Since(time.Time) err.Duration() time.Duration Set the error duration
.Duration(time.Duration) err.Duration() time.Duration Set the error duration
.In(string) err.Domain() string Set the feature category or domain
.Tags(...string) err.Tags() []string Add multiple tags, describing the feature returning an error
err.HasTag(string) bool Check whether the error contains provided tag
.Trace(string) err.Trace() string Add a transaction id, trace id, correlation id... (default: ULID)
.Span(string) err.Span() string Add a span representing a unit of work or operation... (default: ULID)
.Hint(string) err.Hint() string Set a hint for faster debugging
.Owner(string) err.Owner() (string) Set the name/email of the colleague/team responsible for handling this error. Useful for alerting purpose
.User(string, any...) err.User() (string, map[string]any) Supply user id and a chain of key/value
.Tenant(string, any...) err.Tenant() (string, map[string]any) Supply tenant id and a chain of key/value
.Request(*http.Request, bool) err.Request() *http.Request Supply http request
.Response(*http.Response, bool) err.Response() *http.Response Supply http response
.FromContext(context.Context) Reuse an existing OopsErrorBuilder transported in a Go context
Examples
// simple error with public facing message
err0 := oops.
    Public("Could not fetch user.").
    Errorf("sql: bad connection")

// simple error with stacktrace
err1 := oops.New("could not fetch user")

// with optional domain
err2 := oops.
    In("repository").
    Tags("database", "sql").
    Errorf("could not fetch user")

// with custom attributes
ctx := context.WithContext(context.Background(), "a key", "value")
err3 := oops.
    With("driver", "postgresql").
    With("query", query).
    With("query.duration", queryDuration).
    With("lorem", func() string { return "ipsum" }).	// lazy evaluation
    WithContext(ctx, "a key", "another key").
    Errorf("could not fetch user")

// with trace+span
err4 := oops.
    Trace(traceID).
    Span(spanID).
    Errorf("could not fetch user")

// with hint and ownership, for helping developer to solve the issue
err5 := oops.
    Hint("The user could have been removed. Please check deleted_at column.").
    Owner("Slack: #api-gateway").
    Errorf("could not fetch user")

// with optional userID
err6 := oops.
    User(userID).
    Errorf("could not fetch user")

// with optional user data
err7 := oops.
    User(userID, "firstname", "Samuel").
    Errorf("could not fetch user")

// with optional user and tenant
err8 := oops.
    User(userID, "firstname", "Samuel").
    Tenant(workspaceID, "name", "my little project").
    Errorf("could not fetch user")

// with optional http request and response
err9 := oops.
    Request(req, false).
    Response(res, true).
    Errorf("could not fetch user")

// reuse an existing OopsErrorBuilder transported in a Go context
ctx := oops.WithBuilder(context.TODO(), err9)
// [...]
err10 := oops.
    FromContext(ctx).
    Errorf("could not fetch user")
Other helpers
  • oops.AsError[MyError](error) (MyError, bool) as an alias to errors.As(...)
Stack trace

This library provides a pretty printed stack trace for each generated error.

The stack trace max depth can be set using:

// default: 10
oops.StackTraceMaxDepth = 42

The stack trace will be printed this way:

err := oops.Errorf("permission denied")

fmt.Println(err.(oops.OopsError).Stacktrace())
Stacktrace

Wrapped errors will be reported as an annotated stack trace:

err1 := oops.Errorf("permission denied")
// ...
err2 := oops.Wrapf(err1, "something failed")

fmt.Println(err2.(oops.OopsError).Stacktrace())
Stacktrace
Source fragments

The exact error location can be provided in a Go file extract.

Source fragments are hidden by default. You must run oops.SourceFragmentsHidden = false to enable this feature. Go source files being read at run time, you have to keep the source code at the same location.

In a future release, this library is expected to output a colorized extract. Please contribute!

oops.SourceFragmentsHidden = false

err1 := oops.Errorf("permission denied")
// ...
err2 := oops.Wrapf(err, "something failed")

fmt.Println(err2.(oops.OopsError).Sources())
Sources
Panic handling

oops library is delivered with a try/catch -ish error handler. 2 handlers variants are available: oops.Recover() and oops.Recoverf(). Both can be used in the oops error builder with usual methods.

πŸ₯· Start hacking oops.Recover() with this playground.

func mayPanic() {
	panic("permission denied")
}

func handlePanic() error {
    return oops.
        Code("iam_authz_missing_permission").
        In("authz").
        With("permission", "post.create").
        Trace("6710668a-2b2a-4de6-b8cf-3272a476a1c9").
        Hint("Runbook: https://doc.acme.org/doc/abcd.md").
        Recoverf(func() {
            // ...
            mayPanic()
            // ...
        }, "unexpected error %d", 42)
}
Assertions

Assertions may be considered an anti-pattern for Golang since we only call panic() for unexpected and critical errors. In this situation, assertions might help developers to write safer code.

func mayPanic() {
    x := 42

    oops.
        Trace("6710668a-2b2a-4de6-b8cf-3272a476a1c9").
        Hint("Runbook: https://doc.acme.org/doc/abcd.md").
        Assertf(time.Now().Weekday() == 1, "This code should run on Monday only.").
        With("x", x).
        Assertf(x == 42, "expected x to be equal to 42, but got %d", x)

    oops.Assert(re.Match(email))

    // ...
}

func handlePanic() error {
    return oops.
        Code("iam_authz_missing_permission").
        In("authz").
        Recover(func() {
            // ...
            mayPanic()
            // ...
        })
}
Output

Errors can be printed in many ways. Logger formatters provided in this library use these methods.

Errorf %w
str := fmt.Errorf("something failed: %w", oops.Errorf("permission denied"))

fmt.Println(err.Error())
// Output:
// something failed: permission denied
printf %v
err := oops.Errorf("permission denied")

fmt.Printf("%v", err)
// Output:
// permission denied
printf %+v
err := oops.Errorf("permission denied")

fmt.Printf("%+v", err)
Output
JSON Marshal
b := json.MarshalIndent(err, "", "  ")
Output
slog.Valuer
err := oops.Errorf("permission denied")

attr := slog.Error(err.Error(),
    slog.Any("error", err))

// Output:
// slog.Group("error", ...)
Custom timezone
loc, _ := time.LoadLocation("Europe/Paris")
oops.Local = loc
Go context

An OopsErrorBuilder can be transported in a go context.Context to reuse later.

func myFunc(ctx context.Context) {
    oops.
        FromContext(ctx).
        Tag("auth").
        Errorf("not permitted")
}

func main() {
    err := oops.
        In("my domain").
        User("user-123")
    ctx := oops.WithBuilder(context.TODO(), err)

    myFunc(ctx)
}

πŸ“« Loggers

Some loggers may need a custom formatter to extract attributes from oops.OopsError.

Available loggers:

We are looking for contributions and examples for:

  • zap
  • go-sentry
  • otel
  • other?

Examples of formatters can be found in ToMap(), Format(), Marshal() and LogValuer methods of oops.OopsError.

πŸ₯· Tips and best practices

Public facing error message

Humans do not like technical errors. The oops container can bring an additional human-readable message.

err := oops.
    Public("Could not fetch user.").
    Errorf("sql: bad connection")

userMessage := oops.GetPublic(err, "Unexpected error")
Wrap/Wrapf shortcut

oops.Wrap(...) and oops.Wrapf(...) returns nil if the provided error is nil.

❌ So don't write:

err := mayFail()
if err != nil {
    return oops.Wrapf(err, ...)
}

return nil

βœ… but write:

return oops.Wrapf(mayFail(), ...)
Reuse error builder

Writing a full contextualized error can be painful and very repetitive. But a single context can be used for multiple errors in a single function:

❌ So don't write:

err := mayFail1()
if err != nil {
    return oops.
        In("iam").
        Trace("77cb6664").
        With("hello", "world").
        Wrap(err)
}

err = mayFail2()
if err != nil {
    return oops.
        In("iam").
        Trace("77cb6664").
        With("hello", "world").
        Wrap(err)
}

return oops.
    In("iam").
    Trace("77cb6664").
    With("hello", "world").
    Wrap(mayFail3())

βœ… but write:

errorBuilder := oops.
    In("iam").
    Trace("77cb6664").
    With("hello", "world")

err := mayFail1()
if err != nil {
    return errorBuilder.Wrap(err)
}

err = mayFail2()
if err != nil {
    return errorBuilder.Wrap(err)
}

return errorBuilder.Wrap(mayFail3())
Caller/callee attributes

Also, think about feeding error context in every caller, instead of adding extra information at the last moment.

❌ So don't write:

func a() error {
    return b()
}

func b() error {
    return c()
}

func c() error {
    return d()
}

func d() error {
    return oops.
        Code("iam_missing_permission").
        In("authz").
        Trace("4ea76885-a371-46b0-8ce0-b72b277fa9af").
        Time(time.Now()).
        With("hello", "world").
        With("permission", "post.create").
        Hint("Runbook: https://doc.acme.org/doc/abcd.md").
        User("user-123", "firstname", "john", "lastname", "doe").
        Tenant("organization-123", "name", "Microsoft").
        Errorf("permission denied")
}

βœ… but write:

func a() error {
	return b()
}

func b() error {
    return oops.
        In("iam").
        Trace("4ea76885-a371-46b0-8ce0-b72b277fa9af").
        With("hello", "world").
        Wrapf(c(), "something failed")
}

func c() error {
    return d()
}

func d() error {
    return oops.
        Code("iam_missing_permission").
        In("authz").
        Time(time.Now()).
        With("permission", "post.create").
        Hint("Runbook: https://doc.acme.org/doc/abcd.md").
        User("user-123", "firstname", "john", "lastname", "doe").
        Tenant("organization-123", "name", "Microsoft").
        Errorf("permission denied")
}

Why naming this library "oops"?

Have you already heard a developer yelling at unclear error messages in Sentry, with zero context, only to realize he wrote that piece of shit himself?

Yes. Me too.

oops!

🀝 Contributing

Don't hesitate ;)

# Install some dev dependencies
make tools

# Run tests
make test
# or
make watch-test

πŸ‘€ Contributors

Contributors

πŸ’« Show your support

Give a ⭐️ if this project helped you!

GitHub Sponsors

πŸ“ License

Copyright Β© 2023 Samuel Berthe.

This project is MIT licensed.

Documentation ΒΆ

Index ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

View Source
var (
	// SourceFragmentsHidden controls whether source code fragments are included in error output.
	// When true, source code context around error locations is hidden to reduce output size.
	SourceFragmentsHidden = true

	// DereferencePointers controls whether pointer values in error context are automatically
	// dereferenced when converting to map representations. This can be useful for logging
	// but may cause issues with nil pointers.
	DereferencePointers = true

	// Local specifies the timezone used for error timestamps. Defaults to UTC.
	Local *time.Location = time.UTC
)

Global configuration variables that control the behavior of error handling.

View Source
var (
	// StackTraceMaxDepth controls the maximum number of stack frames
	// to capture in a stack trace. This prevents stack traces from
	// becoming excessively long while still providing sufficient
	// context for debugging.
	//
	// The default value of 10 provides a good balance between
	// detail and readability. For deep call stacks, this will
	// capture the most recent 10 frames, which typically include
	// the most relevant debugging information.
	StackTraceMaxDepth = 10
)

Global configuration for stack trace generation.

Functions ΒΆ

func Errorf ΒΆ

func Errorf(format string, args ...any) error

Errorf formats an error and returns `oops.OopsError` object that satisfies `error`.

func GetPublic ΒΆ

func GetPublic(err error, defaultPublicMessage string) string

GetPublic returns a message that is safe to show to an end user, or a default generic message.

func Join ΒΆ

func Join(e ...error) error

func New ΒΆ

func New(message string) error

New returns `oops.OopsError` object that satisfies `error`.

func Recover ΒΆ

func Recover(cb func()) (err error)

Recover handle panic and returns `oops.OopsError` object that satisfies `error`.

func Recoverf ΒΆ

func Recoverf(cb func(), msg string, args ...any) (err error)

Recoverf handle panic and returns `oops.OopsError` object that satisfies `error` and formats an error message.

func WithBuilder ΒΆ

func WithBuilder(ctx context.Context, builder OopsErrorBuilder) context.Context

WithBuilder stores an OopsErrorBuilder in a Go context for later retrieval. This function creates a new context with the builder stored under the package's internal key, allowing the builder to be accessed by subsequent middleware or handlers in the request chain.

This is particularly useful in web applications where you want to propagate error context (like request IDs, user information, or tracing data) through the entire request processing pipeline without explicitly passing the builder to every function.

The original context is not modified; a new context is returned with the builder added. This follows Go's context immutability pattern.

Example usage:

func middleware(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // Create a builder with request-specific context
    builder := oops.
      Trace(r.Header.Get("X-Request-ID")).
      With("user_agent", r.UserAgent())

    // Store the builder in the request context
    ctx := oops.WithBuilder(r.Context(), builder)

    // Pass the enhanced context to the next handler
    next.ServeHTTP(w, r.WithContext(ctx))
  })
}

func handler(w http.ResponseWriter, r *http.Request) {
  // Retrieve the builder from context and create an error
  err := oops.FromContext(r.Context()).
    In("handler").
    Errorf("something went wrong")

  // The error will automatically include the trace ID and user agent
  // that were set in the middleware
}

func Wrap ΒΆ

func Wrap(err error) error

Wrap wraps an error into an `oops.OopsError` object that satisfies `error`.

func Wrap10 ΒΆ

func Wrap10[A any, B any, C any, D any, E any, F any, G any, H any, I any](a A, b B, c C, d D, e E, f F, g G, h H, i I, err error) (A, B, C, D, E, F, G, H, I, error)

Wrap10 wraps an error while preserving nine return values. This function is useful for functions that return (value1, value2, value3, value4, value5, value6, value7, value8, value9, error) and need the error to be wrapped with oops error handling.

The function takes nine values of any types A, B, C, D, E, F, G, H, and I and an error, wraps the error using oops.Wrap, and returns the original values along with the wrapped error.

func Wrap2 ΒΆ

func Wrap2[A any](a A, err error) (A, error)

Wrap2 wraps an error while preserving a single return value. This function is useful for functions that return (value, error) pairs and need the error to be wrapped with oops error handling.

The function takes a value of any type A and an error, wraps the error using oops.Wrap, and returns the original value along with the wrapped error.

Example usage:

func getUser(id string) (User, error) {
    user, err := database.GetUser(id)
    return oops.Wrap2(user, err)
}

// Equivalent to:
func getUser(id string) (User, error) {
    user, err := database.GetUser(id)
    if err != nil {
        return user, oops.Wrap(err)
    }
    return user, nil
}

func Wrap3 ΒΆ

func Wrap3[A any, B any](a A, b B, err error) (A, B, error)

Wrap3 wraps an error while preserving two return values. This function is useful for functions that return (value1, value2, error) and need the error to be wrapped with oops error handling.

The function takes two values of any types A and B and an error, wraps the error using oops.Wrap, and returns the original values along with the wrapped error.

Example usage:

func getUserAndProfile(id string) (User, Profile, error) {
    user, profile, err := database.GetUserAndProfile(id)
    return oops.Wrap3(user, profile, err)
}

func Wrap4 ΒΆ

func Wrap4[A any, B any, C any](a A, b B, c C, err error) (A, B, C, error)

Wrap4 wraps an error while preserving three return values. This function is useful for functions that return (value1, value2, value3, error) and need the error to be wrapped with oops error handling.

The function takes three values of any types A, B, and C and an error, wraps the error using oops.Wrap, and returns the original values along with the wrapped error.

Example usage:

func getUserProfileAndSettings(id string) (User, Profile, Settings, error) {
    user, profile, settings, err := database.GetUserProfileAndSettings(id)
    return oops.Wrap4(user, profile, settings, err)
}

func Wrap5 ΒΆ

func Wrap5[A any, B any, C any, D any](a A, b B, c C, d D, err error) (A, B, C, D, error)

Wrap5 wraps an error while preserving four return values. This function is useful for functions that return (value1, value2, value3, value4, error) and need the error to be wrapped with oops error handling.

The function takes four values of any types A, B, C, and D and an error, wraps the error using oops.Wrap, and returns the original values along with the wrapped error.

func Wrap6 ΒΆ

func Wrap6[A any, B any, C any, D any, E any](a A, b B, c C, d D, e E, err error) (A, B, C, D, E, error)

Wrap6 wraps an error while preserving five return values. This function is useful for functions that return (value1, value2, value3, value4, value5, error) and need the error to be wrapped with oops error handling.

The function takes five values of any types A, B, C, D, and E and an error, wraps the error using oops.Wrap, and returns the original values along with the wrapped error.

func Wrap7 ΒΆ

func Wrap7[A any, B any, C any, D any, E any, F any](a A, b B, c C, d D, e E, f F, err error) (A, B, C, D, E, F, error)

Wrap7 wraps an error while preserving six return values. This function is useful for functions that return (value1, value2, value3, value4, value5, value6, error) and need the error to be wrapped with oops error handling.

The function takes six values of any types A, B, C, D, E, and F and an error, wraps the error using oops.Wrap, and returns the original values along with the wrapped error.

func Wrap8 ΒΆ

func Wrap8[A any, B any, C any, D any, E any, F any, G any](a A, b B, c C, d D, e E, f F, g G, err error) (A, B, C, D, E, F, G, error)

Wrap8 wraps an error while preserving seven return values. This function is useful for functions that return (value1, value2, value3, value4, value5, value6, value7, error) and need the error to be wrapped with oops error handling.

The function takes seven values of any types A, B, C, D, E, F, and G and an error, wraps the error using oops.Wrap, and returns the original values along with the wrapped error.

func Wrap9 ΒΆ

func Wrap9[A any, B any, C any, D any, E any, F any, G any, H any](a A, b B, c C, d D, e E, f F, g G, h H, err error) (A, B, C, D, E, F, G, H, error)

Wrap9 wraps an error while preserving eight return values. This function is useful for functions that return (value1, value2, value3, value4, value5, value6, value7, value8, error) and need the error to be wrapped with oops error handling.

The function takes eight values of any types A, B, C, D, E, F, G, and H and an error, wraps the error using oops.Wrap, and returns the original values along with the wrapped error.

func Wrapf ΒΆ

func Wrapf(err error, format string, args ...any) error

Wrapf wraps an error into an `oops.OopsError` object that satisfies `error` and formats an error message.

func Wrapf10 ΒΆ

func Wrapf10[A any, B any, C any, D any, E any, F any, G any, H any, I any](a A, b B, c C, d D, e E, f F, g G, h H, i I, err error, format string, args ...any) (A, B, C, D, E, F, G, H, I, error)

Wrapf10 wraps an error with a formatted message while preserving nine return values. This function is similar to Wrap10 but uses oops.Wrapf to add additional context to the error message.

The function takes nine values of any types A, B, C, D, E, F, G, H, and I, an error, and a format string with arguments, wraps the error using oops.Wrapf, and returns the original values along with the wrapped error.

func Wrapf2 ΒΆ

func Wrapf2[A any](a A, err error, format string, args ...any) (A, error)

Wrapf2 wraps an error with a formatted message while preserving a single return value. This function is similar to Wrap2 but uses oops.Wrapf to add additional context to the error message.

The function takes a value of any type A, an error, and a format string with arguments, wraps the error using oops.Wrapf, and returns the original value along with the wrapped error.

Example usage:

func getUser(id string) (User, error) {
    user, err := database.GetUser(id)
    return oops.Wrapf2(user, err, "failed to get user with id %s", id)
}

func Wrapf3 ΒΆ

func Wrapf3[A any, B any](a A, b B, err error, format string, args ...any) (A, B, error)

Wrapf3 wraps an error with a formatted message while preserving two return values. This function is similar to Wrap3 but uses oops.Wrapf to add additional context to the error message.

The function takes two values of any types A and B, an error, and a format string with arguments, wraps the error using oops.Wrapf, and returns the original values along with the wrapped error.

Example usage:

func getUserAndProfile(id string) (User, Profile, error) {
    user, profile, err := database.GetUserAndProfile(id)
    return oops.Wrapf3(user, profile, err, "failed to get user and profile for id %s", id)
}

func Wrapf4 ΒΆ

func Wrapf4[A any, B any, C any](a A, b B, c C, err error, format string, args ...any) (A, B, C, error)

Wrapf4 wraps an error with a formatted message while preserving three return values. This function is similar to Wrap4 but uses oops.Wrapf to add additional context to the error message.

The function takes three values of any types A, B, and C, an error, and a format string with arguments, wraps the error using oops.Wrapf, and returns the original values along with the wrapped error.

func Wrapf5 ΒΆ

func Wrapf5[A any, B any, C any, D any](a A, b B, c C, d D, err error, format string, args ...any) (A, B, C, D, error)

Wrapf5 wraps an error with a formatted message while preserving four return values. This function is similar to Wrap5 but uses oops.Wrapf to add additional context to the error message.

The function takes four values of any types A, B, C, and D, an error, and a format string with arguments, wraps the error using oops.Wrapf, and returns the original values along with the wrapped error.

func Wrapf6 ΒΆ

func Wrapf6[A any, B any, C any, D any, E any](a A, b B, c C, d D, e E, err error, format string, args ...any) (A, B, C, D, E, error)

Wrapf6 wraps an error with a formatted message while preserving five return values. This function is similar to Wrap6 but uses oops.Wrapf to add additional context to the error message.

The function takes five values of any types A, B, C, D, and E, an error, and a format string with arguments, wraps the error using oops.Wrapf, and returns the original values along with the wrapped error.

func Wrapf7 ΒΆ

func Wrapf7[A any, B any, C any, D any, E any, F any](a A, b B, c C, d D, e E, f F, err error, format string, args ...any) (A, B, C, D, E, F, error)

Wrapf7 wraps an error with a formatted message while preserving six return values. This function is similar to Wrap7 but uses oops.Wrapf to add additional context to the error message.

The function takes six values of any types A, B, C, D, E, and F, an error, and a format string with arguments, wraps the error using oops.Wrapf, and returns the original values along with the wrapped error.

func Wrapf8 ΒΆ

func Wrapf8[A any, B any, C any, D any, E any, F any, G any](a A, b B, c C, d D, e E, f F, g G, err error, format string, args ...any) (A, B, C, D, E, F, G, error)

Wrapf8 wraps an error with a formatted message while preserving seven return values. This function is similar to Wrap8 but uses oops.Wrapf to add additional context to the error message.

The function takes seven values of any types A, B, C, D, E, F, and G, an error, and a format string with arguments, wraps the error using oops.Wrapf, and returns the original values along with the wrapped error.

func Wrapf9 ΒΆ

func Wrapf9[A any, B any, C any, D any, E any, F any, G any, H any](a A, b B, c C, d D, e E, f F, g G, h H, err error, format string, args ...any) (A, B, C, D, E, F, G, H, error)

Wrapf9 wraps an error with a formatted message while preserving eight return values. This function is similar to Wrap9 but uses oops.Wrapf to add additional context to the error message.

The function takes eight values of any types A, B, C, D, E, F, G, and H, an error, and a format string with arguments, wraps the error using oops.Wrapf, and returns the original values along with the wrapped error.

Types ΒΆ

type OopsError ΒΆ

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

OopsError represents an enhanced error with additional contextual information. It implements the standard error interface while providing rich metadata for debugging, logging, and error handling.

func AsOops ΒΆ

func AsOops(err error) (OopsError, bool)

AsOops checks if an error is an oops.OopsError instance and returns it if so. This function is an alias to errors.As and provides a convenient way to type-assert errors to oops.OopsError without importing the errors package.

This function is useful when you need to access the rich metadata and context information stored in oops.OopsError instances, such as error codes, stacktraces, user information, or custom context data.

Example usage:

err := someFunction()
if oopsErr, ok := oops.AsOops(err); ok {
  // Access oops-specific information
  fmt.Printf("Error code: %s\n", oopsErr.Code())
  fmt.Printf("Domain: %s\n", oopsErr.Domain())
  fmt.Printf("Stacktrace: %s\n", oopsErr.Stacktrace())

  // Check for specific tags
  if oopsErr.HasTag("critical") {
    // Handle critical errors differently
    sendAlert(oopsErr)
  }
}

// Chain with other error handling
if oopsErr, ok := oops.AsOops(err); ok && oopsErr.Code() == "database_error" {
  // Handle database errors specifically
  retryOperation()
}

func (OopsError) Code ΒΆ

func (o OopsError) Code() string

Code returns the error code from the deepest error in the chain. Error codes are machine-readable identifiers that can be used for programmatic error handling and cross-service error correlation.

func (OopsError) Context ΒΆ

func (o OopsError) Context() map[string]any

Context returns a flattened key-value context map from the error chain. Context from all errors in the chain is merged, with later errors taking precedence. Pointer values are dereferenced if DereferencePointers is enabled. Lazy evaluation functions are executed to get their values.

func (OopsError) Domain ΒΆ

func (o OopsError) Domain() string

Domain returns the domain/feature category of the error. Returns the domain from the deepest error in the chain.

func (OopsError) Duration ΒΆ

func (o OopsError) Duration() time.Duration

Duration returns the duration associated with the error. Returns the duration from the deepest error in the chain.

func (OopsError) Error ΒΆ

func (o OopsError) Error() string

Error returns the error message without additional context. This method implements the error interface. If the error wraps another error, it returns "message: wrapped_error". Otherwise, it returns just the message.

func (OopsError) Format ΒΆ

func (o OopsError) Format(s fmt.State, verb rune)

Format implements the fmt.Formatter interface for custom formatting. Supports the following format verbs: - %v: standard error message - %+v: verbose format with stack trace and context - %#v: Go syntax representation.

func (OopsError) HasTag ΒΆ

func (o OopsError) HasTag(tag string) bool

HasTag checks if the error or any of its wrapped errors contain the specified tag. This is useful for conditional error handling based on error categories.

func (OopsError) Hint ΒΆ

func (o OopsError) Hint() string

Hint returns a debugging hint for resolving the error. Returns the hint from the deepest error in the chain.

func (OopsError) Is ΒΆ

func (c OopsError) Is(err error) bool

Is checks if this error matches the target error. This method implements the errors.Is interface for error comparison.

func (OopsError) LogValue ΒΆ

func (o OopsError) LogValue() slog.Value

LogValue returns a slog.Value representation of the error for structured logging. This method implements the slog.LogValuer interface and provides a flattened representation of the error's context and metadata suitable for logging systems.

func (OopsError) LogValuer deprecated

func (o OopsError) LogValuer() slog.Value

LogValuer returns a slog.Value representation of the error. This method implements the slog.LogValuer interface for structured logging.

Deprecated: Use LogValue instead.

func (OopsError) MarshalJSON ΒΆ

func (o OopsError) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface. This allows OopsError to be directly serialized to JSON.

func (OopsError) Owner ΒΆ

func (o OopsError) Owner() string

Owner returns the name/email of the person/team responsible for handling this error. Returns the owner from the deepest error in the chain.

func (OopsError) Public ΒΆ

func (o OopsError) Public() string

Public returns a user-safe error message. Returns the public message from the deepest error in the chain.

func (OopsError) Request ΒΆ

func (o OopsError) Request() *http.Request

Request returns the associated HTTP request. Returns the request from the deepest error in the chain.

func (OopsError) Response ΒΆ

func (o OopsError) Response() *http.Response

Response returns the associated HTTP response. Returns the response from the deepest error in the chain.

func (OopsError) Sources ΒΆ

func (o OopsError) Sources() string

Sources returns formatted source code fragments around the error location. This provides context about the code that caused the error, which is particularly useful for debugging. The output includes line numbers and highlights the exact line where the error occurred.

func (OopsError) Span ΒΆ

func (o OopsError) Span() string

Span returns the current span identifier. Unlike other attributes, span returns the current error's span, not the deepest one.

func (OopsError) StackFrames ΒΆ

func (o OopsError) StackFrames() []runtime.Frame

StackFrames returns the raw stack frames as runtime.Frame objects. This is useful for custom stack trace formatting or analysis.

func (OopsError) Stacktrace ΒΆ

func (o OopsError) Stacktrace() string

Stacktrace returns a formatted string representation of the error's stack trace. The stack trace shows the call hierarchy leading to the error, excluding frames from the Go standard library and this package. The stacktrace is basically written from the bottom to the top, in order to dedup frames. It support recursive code.

func (OopsError) Tags ΒΆ

func (o OopsError) Tags() []string

Tags returns all unique tags from the error chain. Tags are merged from all errors in the chain and deduplicated.

func (OopsError) Tenant ΒΆ

func (o OopsError) Tenant() (string, map[string]any)

Tenant returns the tenant ID and associated tenant data. Returns the tenant information from the deepest error in the chain.

func (OopsError) Time ΒΆ

func (o OopsError) Time() time.Time

Time returns the timestamp when the error occurred. Returns the time from the deepest error in the chain.

func (OopsError) ToMap ΒΆ

func (o OopsError) ToMap() map[string]any

ToMap converts the error to a map representation suitable for JSON serialization. This method provides a flattened view of all error attributes and is useful for logging, debugging, and cross-service error transmission.

func (OopsError) Trace ΒΆ

func (o OopsError) Trace() string

Trace returns the transaction/trace/correlation ID. If no trace ID is set, generates a new ULID-based trace ID. Returns the trace ID from the deepest error in the chain.

func (OopsError) Unwrap ΒΆ

func (o OopsError) Unwrap() error

Unwrap returns the underlying error that this OopsError wraps. This method implements the errors.Wrapper interface.

func (OopsError) User ΒΆ

func (o OopsError) User() (string, map[string]any)

User returns the user ID and associated user data. Returns the user information from the deepest error in the chain.

type OopsErrorBuilder ΒΆ

type OopsErrorBuilder OopsError

OopsErrorBuilder implements the builder pattern for creating OopsError instances. It provides a fluent API for setting error attributes and creating error objects. The builder is designed to be chainable, allowing multiple method calls in sequence.

func Assert ΒΆ

func Assert(condition bool) OopsErrorBuilder

Assert panics if condition is false. Panic payload will be of type oops.OopsError. Assertions can be chained.

func Assertf ΒΆ

func Assertf(condition bool, msg string, args ...any) OopsErrorBuilder

Assertf panics if condition is false. Panic payload will be of type oops.OopsError. Assertions can be chained.

func Code ΒΆ

func Code(code string) OopsErrorBuilder

Code set a code or slug that describes the error. Error messages are intended to be read by humans, but such code is expected to be read by machines and even transported over different services.

func Duration ΒΆ

func Duration(duration time.Duration) OopsErrorBuilder

Duration set the error duration.

func FromContext ΒΆ

func FromContext(ctx context.Context) OopsErrorBuilder

func Hint ΒΆ

func Hint(hint string) OopsErrorBuilder

Hint set a hint for faster debugging.

func In ΒΆ

func In(domain string) OopsErrorBuilder

In set the feature category or domain.

func Owner ΒΆ

func Owner(owner string) OopsErrorBuilder

Owner set the name/email of the colleague/team responsible for handling this error. Useful for alerting purpose.

func Public ΒΆ

func Public(public string) OopsErrorBuilder

Public sets a message that is safe to show to an end user.

func Request ΒΆ

func Request(req *http.Request, withBody bool) OopsErrorBuilder

Request supplies a http.Request.

func Response ΒΆ

func Response(res *http.Response, withBody bool) OopsErrorBuilder

Response supplies a http.Response.

func Since ΒΆ

func Since(time time.Time) OopsErrorBuilder

Since set the error duration.

func Span ΒΆ

func Span(span string) OopsErrorBuilder

Span represents a unit of work or operation.

func Tags ΒΆ

func Tags(tags ...string) OopsErrorBuilder

Tags adds multiple tags, describing the feature returning an error.

func Tenant ΒΆ

func Tenant(tenantID string, data map[string]any) OopsErrorBuilder

Tenant supplies tenant id and a chain of key/value.

func Time ΒΆ

func Time(time time.Time) OopsErrorBuilder

Time set the error time. Default: `time.Now()`.

func Trace ΒΆ

func Trace(trace string) OopsErrorBuilder

Trace set a transaction id, trace id or correlation id...

func User ΒΆ

func User(userID string, data map[string]any) OopsErrorBuilder

User supplies user id and a chain of key/value.

func With ΒΆ

func With(kv ...any) OopsErrorBuilder

With supplies a list of attributes declared by pair of key+value.

func WithContext ΒΆ

func WithContext(ctx context.Context, keys ...any) OopsErrorBuilder

WithContext supplies a list of values declared in context.

func (OopsErrorBuilder) Assert ΒΆ

func (o OopsErrorBuilder) Assert(condition bool) OopsErrorBuilder

Assert panics if the condition is false. This method provides a way to add assertions to code that will panic with an OopsError if the condition fails. The assertion can be chained with other builder methods.

Example:

oops.
  Code("assertion_failed").
  Assert(userID != "", "user ID cannot be empty").
  Assert(email != "", "user email cannot be empty").
  Assert(orgID != "", "user organization ID cannot be empty")

func (OopsErrorBuilder) Assertf ΒΆ

func (o OopsErrorBuilder) Assertf(condition bool, msg string, args ...any) OopsErrorBuilder

Assertf panics if the condition is false with a custom message. Similar to Assert, but allows for a custom formatted message when the assertion fails.

Example:

oops.
  Code("assertion_failed").
  Assertf(userID != "", "user ID cannot be empty, got: %s", userID).
  Assertf(email != "", "user email cannot be empty, got: %s", email).
  Assertf(orgID != "", "user organization ID cannot be empty, got: %s", orgID)

func (OopsErrorBuilder) Code ΒΆ

Code sets a machine-readable error code or slug. Error codes are useful for programmatic error handling and cross-service error correlation. They should be consistent and well-documented.

Example:

oops.Code("database_connection_failed").Errorf("connection timeout")

func (OopsErrorBuilder) Duration ΒΆ

func (o OopsErrorBuilder) Duration(duration time.Duration) OopsErrorBuilder

Duration sets the duration associated with the error. This is useful for errors that are related to timeouts or performance issues.

Example:

oops.Duration(5 * time.Second).Errorf("request timeout")

func (OopsErrorBuilder) Errorf ΒΆ

func (o OopsErrorBuilder) Errorf(format string, args ...any) error

Errorf creates a new error with a formatted message. Similar to New, but allows for formatted messages using printf-style formatting.

Example:

err := oops.
  Code("validation_error").
  Errorf("invalid input: expected %s, got %s", expectedType, actualType)

func (OopsErrorBuilder) Hint ΒΆ

Hint provides a debugging hint for resolving the error. Hints should provide actionable guidance for developers.

Example:

oops.Hint("Check database connection and credentials").Errorf("connection failed")

func (OopsErrorBuilder) In ΒΆ

In sets the domain or feature category for the error. Domains help categorize errors by the part of the system they relate to.

Example:

oops.In("database").Errorf("connection failed")

func (OopsErrorBuilder) Join ΒΆ

func (o OopsErrorBuilder) Join(e ...error) error

Join combines multiple errors into a single error. This method uses the standard errors.Join function to combine multiple errors while preserving the builder's context.

Example:

err := oops.
  Code("multi_error").
  Join(err1, err2, err3)

func (OopsErrorBuilder) New ΒΆ

func (o OopsErrorBuilder) New(message string) error

New creates a new error with the specified message. This method creates a simple error without wrapping an existing one. The message is treated as the primary error message.

Example:

err := oops.
  Code("validation_error").
  New("invalid input parameters")

func (OopsErrorBuilder) Owner ΒΆ

func (o OopsErrorBuilder) Owner(owner string) OopsErrorBuilder

Owner sets the person or team responsible for handling this error. This is useful for alerting and error routing.

Example:

oops.Owner("database-team@company.com").Errorf("connection failed")

func (OopsErrorBuilder) Public ΒΆ

func (o OopsErrorBuilder) Public(public string) OopsErrorBuilder

Public sets a user-safe error message. This message should be safe to display to end users without exposing internal system details.

Example:

oops.Public("Unable to process your request").Errorf("internal server error")

func (OopsErrorBuilder) Recover ΒΆ

func (o OopsErrorBuilder) Recover(cb func()) (err error)

Recover handles panics and converts them to OopsError instances. This method executes the provided callback function and catches any panics, converting them to properly formatted OopsError instances with stack traces. If the panic payload is already an error, it wraps that error. Otherwise, it creates a new error from the panic value.

Example:

err := oops.
  Code("panic_recovered").
  Recover(func() {
    // Potentially panicking code
    riskyOperation()
  })

func (OopsErrorBuilder) Recoverf ΒΆ

func (o OopsErrorBuilder) Recoverf(cb func(), msg string, args ...any) (err error)

Recoverf handles panics with additional context message. Similar to Recover, but adds a formatted message to describe the context in which the panic occurred.

Example:

err := oops.
  Code("panic_recovered").
  Recoverf(func() {
    riskyOperation()
  }, "panic in operation: %s", operationName)

func (OopsErrorBuilder) Request ΒΆ

func (o OopsErrorBuilder) Request(req *http.Request, withBody bool) OopsErrorBuilder

Request adds HTTP request information to the error context. The withBody parameter controls whether the request body is included. Including request bodies may impact performance and memory usage.

Example:

oops.Request(req, true).Errorf("request processing failed")

func (OopsErrorBuilder) Response ΒΆ

func (o OopsErrorBuilder) Response(res *http.Response, withBody bool) OopsErrorBuilder

Response adds HTTP response information to the error context. The withBody parameter controls whether the response body is included. Including response bodies may impact performance and memory usage.

Example:

oops.Response(res, false).Errorf("response processing failed")

func (OopsErrorBuilder) Since ΒΆ

Since calculates the duration since the specified time. This is useful for measuring how long an operation took before failing.

Example:

start := time.Now()
// ... perform operation ...
oops.Since(start).Errorf("operation timed out")

func (OopsErrorBuilder) Span ΒΆ

Span sets the current span identifier. Spans represent units of work and are useful for distributed tracing.

Example:

oops.Span("database-query").Errorf("query failed")

func (OopsErrorBuilder) Tags ΒΆ

func (o OopsErrorBuilder) Tags(tags ...string) OopsErrorBuilder

Tags adds multiple tags for categorizing the error. Tags are useful for filtering and grouping errors in monitoring systems.

Example:

oops.Tags("auth", "permission", "critical").Errorf("access denied")

func (OopsErrorBuilder) Tenant ΒΆ

func (o OopsErrorBuilder) Tenant(tenantID string, tenantData ...any) OopsErrorBuilder

Tenant adds tenant information to the error context. This method accepts a tenant ID followed by key-value pairs for tenant data.

Example:

oops.Tenant("tenant-456", "name", "Acme Corp", "plan", "premium").Errorf("quota exceeded")

func (OopsErrorBuilder) Time ΒΆ

Time sets the timestamp when the error occurred. If not set, the error will use the current time when created.

Example:

oops.Time(time.Now()).Errorf("operation failed")

func (OopsErrorBuilder) Trace ΒΆ

func (o OopsErrorBuilder) Trace(trace string) OopsErrorBuilder

Trace sets a transaction, trace, or correlation ID. This is useful for distributed tracing and correlating errors across services.

Example:

oops.Trace("req-123-456").Errorf("service call failed")

func (OopsErrorBuilder) User ΒΆ

func (o OopsErrorBuilder) User(userID string, userData ...any) OopsErrorBuilder

User adds user information to the error context. This method accepts a user ID followed by key-value pairs for user data.

Example:

oops.User("user-123", "firstname", "John", "lastname", "Doe").Errorf("permission denied")

func (OopsErrorBuilder) With ΒΆ

func (o OopsErrorBuilder) With(kv ...any) OopsErrorBuilder

With adds key-value pairs to the error context. Context values are useful for debugging and provide additional information about the error. Values can be of any type and will be serialized appropriately.

Performance: Context values are stored in a map and processed during error creation. Large numbers of context values may impact performance later, but not during error creation.

Example:

oops.With("user_id", 123, "operation", "create").Errorf("validation failed")

func (OopsErrorBuilder) WithContext ΒΆ

func (o OopsErrorBuilder) WithContext(ctx context.Context, keys ...any) OopsErrorBuilder

WithContext extracts values from a Go context and adds them to the error context. This is useful for propagating context values through error chains.

Example:

oops.WithContext(ctx, "request_id", "user_id").Errorf("operation failed")

func (OopsErrorBuilder) Wrap ΒΆ

func (o OopsErrorBuilder) Wrap(err error) error

Wrap wraps an existing error into an OopsError with the current builder's context. If the input error is nil, returns nil. Otherwise, creates a new OopsError that wraps the original error while preserving all the contextual information set in the builder.

Example:

err := oops.
  Code("database_error").
  In("database").
  Wrap(originalError)

func (OopsErrorBuilder) Wrapf ΒΆ

func (o OopsErrorBuilder) Wrapf(err error, format string, args ...any) error

Wrapf wraps an existing error with additional formatted message. Similar to Wrap, but adds a formatted message that describes the context in which the error occurred.

Example:

err := oops.
  Code("database_error").
  In("database").
  Wrapf(originalError, "failed to execute query: %s", queryName)

Directories ΒΆ

Path Synopsis
examples
log module
logrus module
panic module
segfault module
slog module
sources module
zerolog module
loggers
logrus module
zerolog module
recovery
gin module

Jump to

Keyboard shortcuts

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