Skip to content

Commit

Permalink
Merge branch 'master' into implment-logvalue-method-to-withstack
Browse files Browse the repository at this point in the history
  • Loading branch information
nozo-moto authored Sep 18, 2023
2 parents 1598b95 + c1cc191 commit adf5647
Show file tree
Hide file tree
Showing 50 changed files with 26,856 additions and 2,841 deletions.
27 changes: 21 additions & 6 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,36 @@ on:
branches: [ master ]

jobs:

build-and-test:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go:
- "1.17"
- "1.18"
- "1.19"
- "1.20"
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Set up Go (${{ matrix.go }}
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}

- name: Build (${{ matrix.go }})
run: go build ./...
build-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
go:
- "1.20"
- "1.21"
steps:
- uses: actions/checkout@v3

- name: Set up Go (${{ matrix.go }}
uses: actions/setup-go@v2
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}

Expand All @@ -32,4 +47,4 @@ jobs:
run: go test ./...

- name: Tidy (${{ matrix.go }})
run: '[[ `go version` < "go version go1.15.10" ]] || go mod tidy'
run: go mod tidy
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,12 @@ return errors.Wrap(foo(), "foo")
- how to access the detail: `Error()`, regular Go formatting, Sentry Report.

- `WithDetail(error, string) error`, `WithDetailf(error, string, ...interface{}) error`, user-facing detail with contextual information.
- **when to use: need to embark a message string to output when the error is presented to a human.**
- **when to use: need to embark a message string to output when the error is presented to a developer.**
- what it does: captures detail strings.
- how to access the detail: `errors.GetAllDetails()`, `errors.FlattenDetails()` (all details are preserved), format with `%+v`. Not included in Sentry reports.

- `WithHint(error, string) error`, `WithHintf(error, string, ...interface{}) error`: user-facing detail with suggestion for action to take.
- **when to use: need to embark a message string to output when the error is presented to a human.**
- **when to use: need to embark a message string to output when the error is presented to an end user.**
- what it does: captures hint strings.
- how to access the detail: `errors.GetAllHints()`, `errors.FlattenHints()` (hints are de-duplicated), format with `%+v`. Not included in Sentry reports.

Expand Down Expand Up @@ -536,7 +536,8 @@ Example use:
| `WrapWithDepthf` | `WithMessagef` + `WithStackDepth` |
| `AssertionFailedWithDepthf` | `NewWithDepthf` + `WithAssertionFailure` |
| `NewAssertionErrorWithWrappedErrf` | `HandledWithMessagef` (barrier) + `WrapWithDepthf` + `WithAssertionFailure` |

| `Join` | `JoinWithDepth` (see below) |
| `JoinWithDepth` | multi-cause wrapper + `WithStackDepth` |
## API (not constructing error objects)

The following is a summary of the non-constructor API functions, grouped by category.
Expand Down Expand Up @@ -573,10 +574,16 @@ func RegisterLeafDecoder(typeName TypeKey, decoder LeafDecoder)
func RegisterLeafEncoder(typeName TypeKey, encoder LeafEncoder)
func RegisterWrapperDecoder(typeName TypeKey, decoder WrapperDecoder)
func RegisterWrapperEncoder(typeName TypeKey, encoder WrapperEncoder)
func RegisterWrapperEncoderWithMessageOverride (typeName TypeKey, encoder WrapperEncoderWithMessageOverride)
func RegisterMultiCauseEncoder(theType TypeKey, encoder MultiCauseEncoder)
func RegisterMultiCauseDecoder(theType TypeKey, decoder MultiCauseDecoder)
type LeafEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
type LeafDecoder = func(ctx context.Context, msg string, safeDetails []string, payload proto.Message) error
type WrapperEncoder = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message)
type WrapperEncoderWithMessageOverride = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message, overrideError bool)
type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, safeDetails []string, payload proto.Message) error
type MultiCauseEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
type MultiCauseDecoder = func(ctx context.Context, causes []error, msgPrefix string, safeDetails []string, payload proto.Message) error
// Registering package renames for custom error types.
func RegisterTypeMigration(previousPkgPath, previousTypeName string, newType error)
Expand Down
2 changes: 1 addition & 1 deletion contexttags/contexttags.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
// WithContextTags captures the k/v pairs stored in the context via the
// `logtags` package and annotates them on the error.
//
// Only the stromg representation of values remains available. This is
// Only the strong representation of values remains available. This is
// because the library cannot guarantee that the underlying value is
// preserved across the network. To avoid creating a stateful interface
// (where the user code needs to know whether an error has traveled
Expand Down
2 changes: 1 addition & 1 deletion contexttags_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
// WithContextTags captures the k/v pairs stored in the context via the
// `logtags` package and annotates them on the error.
//
// Only the stromg representation of values remains available. This is
// Only the strong representation of values remains available. This is
// because the library cannot guarantee that the underlying value is
// preserved across the network. To avoid creating a stateful interface
// (where the user code needs to know whether an error has traveled
Expand Down
38 changes: 38 additions & 0 deletions errbase/adapters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,44 @@ func TestAdaptBaseGoErr(t *testing.T) {
tt.CheckDeepEqual(newErr, origErr)
}

func TestAdaptGoSingleWrapErr(t *testing.T) {
origErr := fmt.Errorf("an error %w", goErr.New("hello"))
t.Logf("start err: %# v", pretty.Formatter(origErr))

newErr := network(t, origErr)

tt := testutils.T{T: t}
// The library preserves the cause. It's not possible to preserve the fmt
// string.
tt.CheckEqual(newErr.Error(), origErr.Error())
tt.CheckContains(newErr.Error(), "hello")
}

func TestAdaptBaseGoJoinErr(t *testing.T) {
origErr := goErr.Join(goErr.New("hello"), goErr.New("world"))
t.Logf("start err: %# v", pretty.Formatter(origErr))

newErr := network(t, origErr)

tt := testutils.T{T: t}
// The library preserves the error message.
tt.CheckEqual(newErr.Error(), origErr.Error())

}

func TestAdaptGoMultiWrapErr(t *testing.T) {
origErr := fmt.Errorf("an error %w and also %w", goErr.New("hello"), goErr.New("world"))
t.Logf("start err: %# v", pretty.Formatter(origErr))

newErr := network(t, origErr)

tt := testutils.T{T: t}
// The library preserves the causes. It's not possible to preserve the fmt string.
tt.CheckEqual(newErr.Error(), origErr.Error())
tt.CheckContains(newErr.Error(), "hello")
tt.CheckContains(newErr.Error(), "world")
}

func TestAdaptPkgWithMessage(t *testing.T) {
// Simple message wrappers from github.com/pkg/errors are preserved
// completely.
Expand Down
52 changes: 48 additions & 4 deletions errbase/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
return genErr
}
// Decoding failed, we'll drop through to opaqueLeaf{} below.
} else if decoder, ok := multiCauseDecoders[typeKey]; ok {
causes := make([]error, len(enc.MultierrorCauses))
for i, e := range enc.MultierrorCauses {
causes[i] = DecodeError(ctx, *e)
}
genErr := decoder(ctx, causes, enc.Message, enc.Details.ReportablePayload, payload)
if genErr != nil {
return genErr
}
} else {
// Shortcut for non-registered proto-encodable error types:
// if it already implements `error`, it's good to go.
Expand All @@ -66,6 +75,19 @@ func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
}
}

if len(enc.MultierrorCauses) > 0 {
causes := make([]error, len(enc.MultierrorCauses))
for i, e := range enc.MultierrorCauses {
causes[i] = DecodeError(ctx, *e)
}
leaf := &opaqueLeafCauses{
causes: causes,
}
leaf.msg = enc.Message
leaf.details = enc.Details
return leaf
}

// No decoder and no error type: we'll keep what we received and
// make it ready to re-encode exactly (if the error leaves over the
// network again).
Expand Down Expand Up @@ -97,7 +119,7 @@ func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error {
typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName)
if decoder, ok := decoders[typeKey]; ok {
// Yes, use it.
genErr := decoder(ctx, cause, enc.MessagePrefix, enc.Details.ReportablePayload, payload)
genErr := decoder(ctx, cause, enc.Message, enc.Details.ReportablePayload, payload)
if genErr != nil {
// Decoding succeeded. Use this.
return genErr
Expand All @@ -107,9 +129,10 @@ func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error {

// Otherwise, preserve all details about the original object.
return &opaqueWrapper{
cause: cause,
prefix: enc.MessagePrefix,
details: enc.Details,
cause: cause,
prefix: enc.Message,
details: enc.Details,
messageType: MessageType(enc.MessageType),
}
}

Expand Down Expand Up @@ -160,3 +183,24 @@ type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, s

// registry for RegisterWrapperType.
var decoders = map[TypeKey]WrapperDecoder{}

// MultiCauseDecoder is to be provided (via RegisterMultiCauseDecoder
// above) by additional multi-cause wrapper types not yet known by the
// library. A nil return indicates that decoding was not successful.
type MultiCauseDecoder = func(ctx context.Context, causes []error, msgPrefix string, safeDetails []string, payload proto.Message) error

// registry for RegisterMultiCauseDecoder.
var multiCauseDecoders = map[TypeKey]MultiCauseDecoder{}

// RegisterMultiCauseDecoder can be used to register new multi-cause
// wrapper types to the library. Registered wrappers will be decoded
// using their own Go type when an error is decoded. Multi-cause
// wrappers that have not been registered will be decoded using the
// opaqueWrapper type.
func RegisterMultiCauseDecoder(theType TypeKey, decoder MultiCauseDecoder) {
if decoder == nil {
delete(multiCauseDecoders, theType)
} else {
multiCauseDecoders[theType] = decoder
}
}
Loading

0 comments on commit adf5647

Please sign in to comment.