Skip to content

Commit

Permalink
Add context feature
Browse files Browse the repository at this point in the history
  • Loading branch information
m-mizutani committed Oct 19, 2024
1 parent 2f192e6 commit d61c452
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 9 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,32 @@ Output:
}
```

### Context

`goerr` provides `WithContext` method to add values from context to the error. Values can be added to context.Context by `goerr.InjectValue`.

```go
func someAction(id requestID) error {
ctx := context.Background()
ctx = goerr.InjectValue(ctx, "request_id", id)

// ... some action

return goerr.New("failed").WithContext(ctx)
}

func main() {
id := requestID("req-123")
if err := someAction(id); err != nil {
slog.Default().Error("aborted", slog.Any("error", err))
}
}
```

Output:
```
2024/10/19 09:51:04 ERROR aborted error.message=failed error.values.request_id=req-123 (snip)
```

## License

Expand Down
24 changes: 24 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package goerr

import "context"

type errContext struct {
values map[string]any
}

type errContextKey struct{}

func InjectValue(ctx context.Context, key string, value any) context.Context {
newCtx := errContext{
values: make(map[string]any),
}
oldCtx, ok := ctx.Value(errContextKey{}).(*errContext)
if ok {
for k, v := range oldCtx.values {
newCtx.values[k] = v
}
}

newCtx.values[key] = value
return context.WithValue(ctx, errContextKey{}, &newCtx)
}
41 changes: 41 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package goerr_test

import (
"context"
"testing"

"github.com/m-mizutani/goerr"
)

func funcWithContext(ctx context.Context, color string) error {
if color != "red" {
return goerr.New("color is not red").With("color", color).WithContext(ctx)
}
return nil
}

func TestContext(t *testing.T) {
reqID := "req-123"

ctx := context.Background()
ctx = goerr.InjectValue(ctx, "request_id", reqID)

err := funcWithContext(ctx, "blue")

if err == nil {
t.Error("Expected error, got nil")
}

goErr := goerr.Unwrap(err)
if goErr == nil {
t.Error("Expected goerr.Error, got other type")
}

if goErr.Values()["color"] != "blue" {
t.Errorf("Expected color value to be 'blue', got '%v'", goErr.Values()["color"])
}

if goErr.Values()["request_id"] != reqID {
t.Errorf("Expected request_id value to be '%s', got '%v'", reqID, goErr.Values()["request_id"])
}
}
38 changes: 29 additions & 9 deletions errors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package goerr

import (
"context"
"errors"
"fmt"
"io"
Expand All @@ -21,20 +22,24 @@ func New(format string, args ...any) *Error {
// Wrap creates a new Error and add message.
func Wrap(cause error, msg ...any) *Error {
err := newError()

if len(msg) > 0 {
var newMsgs []string
for _, m := range msg {
newMsgs = append(newMsgs, fmt.Sprintf("%v", m))
}
err.msg = strings.Join(newMsgs, " ")
}

err.msg = toWrapMessage(msg...)
err.cause = cause

return err
}

func toWrapMessage(msg ...any) string {
if len(msg) == 0 {
return ""
}

var newMsgs []string
for _, m := range msg {
newMsgs = append(newMsgs, fmt.Sprintf("%v", m))
}
return strings.Join(newMsgs, " ")
}

// Wrapf creates a new Error and add message. The error message is formatted by fmt.Sprintf.
func Wrapf(cause error, format string, args ...any) *Error {
err := newError()
Expand Down Expand Up @@ -158,6 +163,21 @@ func (x *Error) With(key string, value any) *Error {
return x
}

// WithContext adds key and value related to the error event from context.Context
func (x *Error) WithContext(ctx context.Context) *Error {
if ctx == nil {
return x
}

if errCtx, ok := ctx.Value(errContextKey{}).(*errContext); ok {
for k, v := range errCtx.values {
x.values[k] = v
}
}

return x
}

// Unstack trims stack trace by 1. It can be used for internal helper or utility functions.
func (x *Error) Unstack() *Error {
x.st = unstack(x.st, 1)
Expand Down
26 changes: 26 additions & 0 deletions examples/context/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import (
"context"
"log/slog"

"github.com/m-mizutani/goerr"
)

type requestID string

func someAction(id requestID) error {
ctx := context.Background()
ctx = goerr.InjectValue(ctx, "request_id", id)

// ... some action

return goerr.New("failed").WithContext(ctx)
}

func main() {
id := requestID("req-123")
if err := someAction(id); err != nil {
slog.Default().Error("aborted", slog.Any("error", err))
}
}

0 comments on commit d61c452

Please sign in to comment.