Skip to content

Commit adf5647

Browse files
authored
Merge branch 'master' into implment-logvalue-method-to-withstack
2 parents 1598b95 + c1cc191 commit adf5647

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+26856
-2841
lines changed

.github/workflows/ci.yaml

+21-6
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,36 @@ on:
77
branches: [ master ]
88

99
jobs:
10-
11-
build-and-test:
10+
build:
1211
runs-on: ubuntu-latest
1312
strategy:
1413
matrix:
1514
go:
16-
- "1.17"
1715
- "1.18"
1816
- "1.19"
1917
- "1.20"
2018
steps:
21-
- uses: actions/checkout@v2
19+
- uses: actions/checkout@v3
20+
21+
- name: Set up Go (${{ matrix.go }}
22+
uses: actions/setup-go@v4
23+
with:
24+
go-version: ${{ matrix.go }}
25+
26+
- name: Build (${{ matrix.go }})
27+
run: go build ./...
28+
build-and-test:
29+
runs-on: ubuntu-latest
30+
strategy:
31+
matrix:
32+
go:
33+
- "1.20"
34+
- "1.21"
35+
steps:
36+
- uses: actions/checkout@v3
2237

2338
- name: Set up Go (${{ matrix.go }}
24-
uses: actions/setup-go@v2
39+
uses: actions/setup-go@v4
2540
with:
2641
go-version: ${{ matrix.go }}
2742

@@ -32,4 +47,4 @@ jobs:
3247
run: go test ./...
3348

3449
- name: Tidy (${{ matrix.go }})
35-
run: '[[ `go version` < "go version go1.15.10" ]] || go mod tidy'
50+
run: go mod tidy

README.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,12 @@ return errors.Wrap(foo(), "foo")
209209
- how to access the detail: `Error()`, regular Go formatting, Sentry Report.
210210

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

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

@@ -536,7 +536,8 @@ Example use:
536536
| `WrapWithDepthf` | `WithMessagef` + `WithStackDepth` |
537537
| `AssertionFailedWithDepthf` | `NewWithDepthf` + `WithAssertionFailure` |
538538
| `NewAssertionErrorWithWrappedErrf` | `HandledWithMessagef` (barrier) + `WrapWithDepthf` + `WithAssertionFailure` |
539-
539+
| `Join` | `JoinWithDepth` (see below) |
540+
| `JoinWithDepth` | multi-cause wrapper + `WithStackDepth` |
540541
## API (not constructing error objects)
541542

542543
The following is a summary of the non-constructor API functions, grouped by category.
@@ -573,10 +574,16 @@ func RegisterLeafDecoder(typeName TypeKey, decoder LeafDecoder)
573574
func RegisterLeafEncoder(typeName TypeKey, encoder LeafEncoder)
574575
func RegisterWrapperDecoder(typeName TypeKey, decoder WrapperDecoder)
575576
func RegisterWrapperEncoder(typeName TypeKey, encoder WrapperEncoder)
577+
func RegisterWrapperEncoderWithMessageOverride (typeName TypeKey, encoder WrapperEncoderWithMessageOverride)
578+
func RegisterMultiCauseEncoder(theType TypeKey, encoder MultiCauseEncoder)
579+
func RegisterMultiCauseDecoder(theType TypeKey, decoder MultiCauseDecoder)
576580
type LeafEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
577581
type LeafDecoder = func(ctx context.Context, msg string, safeDetails []string, payload proto.Message) error
578582
type WrapperEncoder = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message)
583+
type WrapperEncoderWithMessageOverride = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message, overrideError bool)
579584
type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, safeDetails []string, payload proto.Message) error
585+
type MultiCauseEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
586+
type MultiCauseDecoder = func(ctx context.Context, causes []error, msgPrefix string, safeDetails []string, payload proto.Message) error
580587
581588
// Registering package renames for custom error types.
582589
func RegisterTypeMigration(previousPkgPath, previousTypeName string, newType error)

contexttags/contexttags.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
// WithContextTags captures the k/v pairs stored in the context via the
2626
// `logtags` package and annotates them on the error.
2727
//
28-
// Only the stromg representation of values remains available. This is
28+
// Only the strong representation of values remains available. This is
2929
// because the library cannot guarantee that the underlying value is
3030
// preserved across the network. To avoid creating a stateful interface
3131
// (where the user code needs to know whether an error has traveled

contexttags_api.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
// WithContextTags captures the k/v pairs stored in the context via the
2525
// `logtags` package and annotates them on the error.
2626
//
27-
// Only the stromg representation of values remains available. This is
27+
// Only the strong representation of values remains available. This is
2828
// because the library cannot guarantee that the underlying value is
2929
// preserved across the network. To avoid creating a stateful interface
3030
// (where the user code needs to know whether an error has traveled

errbase/adapters_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,44 @@ func TestAdaptBaseGoErr(t *testing.T) {
5555
tt.CheckDeepEqual(newErr, origErr)
5656
}
5757

58+
func TestAdaptGoSingleWrapErr(t *testing.T) {
59+
origErr := fmt.Errorf("an error %w", goErr.New("hello"))
60+
t.Logf("start err: %# v", pretty.Formatter(origErr))
61+
62+
newErr := network(t, origErr)
63+
64+
tt := testutils.T{T: t}
65+
// The library preserves the cause. It's not possible to preserve the fmt
66+
// string.
67+
tt.CheckEqual(newErr.Error(), origErr.Error())
68+
tt.CheckContains(newErr.Error(), "hello")
69+
}
70+
71+
func TestAdaptBaseGoJoinErr(t *testing.T) {
72+
origErr := goErr.Join(goErr.New("hello"), goErr.New("world"))
73+
t.Logf("start err: %# v", pretty.Formatter(origErr))
74+
75+
newErr := network(t, origErr)
76+
77+
tt := testutils.T{T: t}
78+
// The library preserves the error message.
79+
tt.CheckEqual(newErr.Error(), origErr.Error())
80+
81+
}
82+
83+
func TestAdaptGoMultiWrapErr(t *testing.T) {
84+
origErr := fmt.Errorf("an error %w and also %w", goErr.New("hello"), goErr.New("world"))
85+
t.Logf("start err: %# v", pretty.Formatter(origErr))
86+
87+
newErr := network(t, origErr)
88+
89+
tt := testutils.T{T: t}
90+
// The library preserves the causes. It's not possible to preserve the fmt string.
91+
tt.CheckEqual(newErr.Error(), origErr.Error())
92+
tt.CheckContains(newErr.Error(), "hello")
93+
tt.CheckContains(newErr.Error(), "world")
94+
}
95+
5896
func TestAdaptPkgWithMessage(t *testing.T) {
5997
// Simple message wrappers from github.com/pkg/errors are preserved
6098
// completely.

errbase/decode.go

+48-4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
5757
return genErr
5858
}
5959
// Decoding failed, we'll drop through to opaqueLeaf{} below.
60+
} else if decoder, ok := multiCauseDecoders[typeKey]; ok {
61+
causes := make([]error, len(enc.MultierrorCauses))
62+
for i, e := range enc.MultierrorCauses {
63+
causes[i] = DecodeError(ctx, *e)
64+
}
65+
genErr := decoder(ctx, causes, enc.Message, enc.Details.ReportablePayload, payload)
66+
if genErr != nil {
67+
return genErr
68+
}
6069
} else {
6170
// Shortcut for non-registered proto-encodable error types:
6271
// if it already implements `error`, it's good to go.
@@ -66,6 +75,19 @@ func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
6675
}
6776
}
6877

78+
if len(enc.MultierrorCauses) > 0 {
79+
causes := make([]error, len(enc.MultierrorCauses))
80+
for i, e := range enc.MultierrorCauses {
81+
causes[i] = DecodeError(ctx, *e)
82+
}
83+
leaf := &opaqueLeafCauses{
84+
causes: causes,
85+
}
86+
leaf.msg = enc.Message
87+
leaf.details = enc.Details
88+
return leaf
89+
}
90+
6991
// No decoder and no error type: we'll keep what we received and
7092
// make it ready to re-encode exactly (if the error leaves over the
7193
// network again).
@@ -97,7 +119,7 @@ func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error {
97119
typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName)
98120
if decoder, ok := decoders[typeKey]; ok {
99121
// Yes, use it.
100-
genErr := decoder(ctx, cause, enc.MessagePrefix, enc.Details.ReportablePayload, payload)
122+
genErr := decoder(ctx, cause, enc.Message, enc.Details.ReportablePayload, payload)
101123
if genErr != nil {
102124
// Decoding succeeded. Use this.
103125
return genErr
@@ -107,9 +129,10 @@ func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error {
107129

108130
// Otherwise, preserve all details about the original object.
109131
return &opaqueWrapper{
110-
cause: cause,
111-
prefix: enc.MessagePrefix,
112-
details: enc.Details,
132+
cause: cause,
133+
prefix: enc.Message,
134+
details: enc.Details,
135+
messageType: MessageType(enc.MessageType),
113136
}
114137
}
115138

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

161184
// registry for RegisterWrapperType.
162185
var decoders = map[TypeKey]WrapperDecoder{}
186+
187+
// MultiCauseDecoder is to be provided (via RegisterMultiCauseDecoder
188+
// above) by additional multi-cause wrapper types not yet known by the
189+
// library. A nil return indicates that decoding was not successful.
190+
type MultiCauseDecoder = func(ctx context.Context, causes []error, msgPrefix string, safeDetails []string, payload proto.Message) error
191+
192+
// registry for RegisterMultiCauseDecoder.
193+
var multiCauseDecoders = map[TypeKey]MultiCauseDecoder{}
194+
195+
// RegisterMultiCauseDecoder can be used to register new multi-cause
196+
// wrapper types to the library. Registered wrappers will be decoded
197+
// using their own Go type when an error is decoded. Multi-cause
198+
// wrappers that have not been registered will be decoded using the
199+
// opaqueWrapper type.
200+
func RegisterMultiCauseDecoder(theType TypeKey, decoder MultiCauseDecoder) {
201+
if decoder == nil {
202+
delete(multiCauseDecoders, theType)
203+
} else {
204+
multiCauseDecoders[theType] = decoder
205+
}
206+
}

0 commit comments

Comments
 (0)