Update dependencies

This commit is contained in:
eleksir
2024-07-24 22:57:56 +03:00
parent ac243bf4fd
commit 00257b8e6c
729 changed files with 47714 additions and 29066 deletions

View File

@@ -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.
@@ -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.
@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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.
@@ -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).
@@ -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
@@ -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),
}
}
@@ -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
}
}

View File

@@ -33,18 +33,24 @@ func EncodeError(ctx context.Context, err error) EncodedError {
if cause := UnwrapOnce(err); cause != nil {
return encodeWrapper(ctx, err, cause)
}
// Not a causer.
return encodeLeaf(ctx, err)
return encodeLeaf(ctx, err, UnwrapMulti(err))
}
// encodeLeaf encodes a leaf error.
func encodeLeaf(ctx context.Context, err error) EncodedError {
// encodeLeaf encodes a leaf error. This function accepts a `causes`
// argument because we encode multi-cause errors using the Leaf
// protobuf. This was done to enable backwards compatibility when
// introducing this functionality since the Wrapper type already has a
// required single `cause` field.
func encodeLeaf(ctx context.Context, err error, causes []error) EncodedError {
var msg string
var details errorspb.EncodedErrorDetails
if e, ok := err.(*opaqueLeaf); ok {
msg = e.msg
details = e.details
} else if e, ok := err.(*opaqueLeafCauses); ok {
msg = e.msg
details = e.details
} else {
details.OriginalTypeName, details.ErrorTypeMark.FamilyName, details.ErrorTypeMark.Extension = getTypeDetails(err, false /*onlyFamily*/)
@@ -74,11 +80,21 @@ func encodeLeaf(ctx context.Context, err error) EncodedError {
details.FullDetails = encodeAsAny(ctx, err, payload)
}
var cs []*EncodedError
if len(causes) > 0 {
cs = make([]*EncodedError, len(causes))
for i, ee := range causes {
ee := EncodeError(ctx, ee)
cs[i] = &ee
}
}
return EncodedError{
Error: &errorspb.EncodedError_Leaf{
Leaf: &errorspb.EncodedErrorLeaf{
Message: msg,
Details: details,
Message: msg,
Details: details,
MultierrorCauses: cs,
},
},
}
@@ -115,10 +131,18 @@ func encodeAsAny(ctx context.Context, err error, payload proto.Message) *types.A
func encodeWrapper(ctx context.Context, err, cause error) EncodedError {
var msg string
var details errorspb.EncodedErrorDetails
messageType := Prefix
if e, ok := err.(*opaqueWrapper); ok {
// We delegate all knowledge of the error string
// to the original encoder and do not try to re-engineer
// the prefix out of the error. This helps maintain
// backward compatibility with earlier versions of the
// encoder which don't have any understanding of
// error string ownership by the wrapper.
msg = e.prefix
details = e.details
messageType = e.messageType
} else {
details.OriginalTypeName, details.ErrorTypeMark.FamilyName, details.ErrorTypeMark.Extension = getTypeDetails(err, false /*onlyFamily*/)
@@ -127,12 +151,12 @@ func encodeWrapper(ctx context.Context, err, cause error) EncodedError {
// If we have a manually registered encoder, use that.
typeKey := TypeKey(details.ErrorTypeMark.FamilyName)
if enc, ok := encoders[typeKey]; ok {
msg, details.ReportablePayload, payload = enc(ctx, err)
msg, details.ReportablePayload, payload, messageType = enc(ctx, err)
} else {
// No encoder.
// In that case, we'll try to compute a message prefix
// manually.
msg = extractPrefix(err, cause)
msg, messageType = extractPrefix(err, cause)
// If there are known safe details, use them.
if s, ok := err.(SafeDetailer); ok {
@@ -148,9 +172,10 @@ func encodeWrapper(ctx context.Context, err, cause error) EncodedError {
return EncodedError{
Error: &errorspb.EncodedError_Wrapper{
Wrapper: &errorspb.EncodedWrapper{
Cause: EncodeError(ctx, cause),
MessagePrefix: msg,
Details: details,
Cause: EncodeError(ctx, cause),
Message: msg,
Details: details,
MessageType: errorspb.MessageType(messageType),
},
},
}
@@ -158,21 +183,36 @@ func encodeWrapper(ctx context.Context, err, cause error) EncodedError {
// extractPrefix extracts the prefix from a wrapper's error message.
// For example,
// err := errors.New("bar")
// err = errors.Wrap(err, "foo")
// extractPrefix(err)
//
// err := errors.New("bar")
// err = errors.Wrap(err, "foo")
// extractPrefix(err)
//
// returns "foo".
func extractPrefix(err, cause error) string {
//
// If a presumed wrapper does not have a message prefix, it is assumed
// to override the entire error message and `extractPrefix` returns
// the entire message and the boolean `true` to signify that the causes
// should not be appended to it.
func extractPrefix(err, cause error) (string, MessageType) {
causeSuffix := cause.Error()
errMsg := err.Error()
if strings.HasSuffix(errMsg, causeSuffix) {
prefix := errMsg[:len(errMsg)-len(causeSuffix)]
// If error msg matches exactly then this is a wrapper
// with no message of its own.
if len(prefix) == 0 {
return "", Prefix
}
if strings.HasSuffix(prefix, ": ") {
return prefix[:len(prefix)-2]
return prefix[:len(prefix)-2], Prefix
}
}
return ""
// If we don't have the cause as a suffix, then we have
// some other string as our error msg, preserve that and
// mark as override
return errMsg, FullMessage
}
func getTypeDetails(
@@ -183,6 +223,8 @@ func getTypeDetails(
switch t := err.(type) {
case *opaqueLeaf:
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
case *opaqueLeafCauses:
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
case *opaqueWrapper:
return t.details.OriginalTypeName, t.details.ErrorTypeMark.FamilyName, t.details.ErrorTypeMark.Extension
}
@@ -286,6 +328,28 @@ type LeafEncoder = func(ctx context.Context, err error) (msg string, safeDetails
// registry for RegisterLeafEncoder.
var leafEncoders = map[TypeKey]LeafEncoder{}
// RegisterMultiCauseEncoder can be used to register new multi-cause
// error types to the library. Registered types will be encoded using
// their own Go type when an error is encoded. Multi-cause wrappers
// that have not been registered will be encoded using the
// opaqueWrapper type.
func RegisterMultiCauseEncoder(theType TypeKey, encoder MultiCauseEncoder) {
// This implementation is a simple wrapper around `LeafEncoder`
// because we implemented multi-cause error wrapper encoding into a
// `Leaf` instead of a `Wrapper` for smoother backwards
// compatibility support. Exposing this detail to consumers of the
// API is confusing and hence avoided. The causes of the error are
// encoded separately regardless of this encoder's implementation.
RegisterLeafEncoder(theType, encoder)
}
// MultiCauseEncoder is to be provided (via RegisterMultiCauseEncoder
// above) by additional multi-cause wrapper types not yet known to this
// library. The encoder will automatically extract and encode the
// causes of this error by calling `Unwrap()` and expecting a slice of
// errors.
type MultiCauseEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
// RegisterWrapperEncoder can be used to register new wrapper types to
// the library. Registered wrappers will be encoded using their own
// Go type when an error is encoded. Wrappers that have not been
@@ -295,6 +359,35 @@ var leafEncoders = map[TypeKey]LeafEncoder{}
// or a different type, ensure that RegisterTypeMigration() was called
// prior to RegisterWrapperEncoder().
func RegisterWrapperEncoder(theType TypeKey, encoder WrapperEncoder) {
RegisterWrapperEncoderWithMessageType(
theType,
func(ctx context.Context, err error) (
msgPrefix string,
safeDetails []string,
payload proto.Message,
messageType MessageType,
) {
prefix, details, payload := encoder(ctx, err)
return prefix, details, payload, messageType
})
}
// RegisterWrapperEncoderWithMessageType can be used to register
// new wrapper types to the library. Registered wrappers will be
// encoded using their own Go type when an error is encoded. Wrappers
// that have not been registered will be encoded using the
// opaqueWrapper type.
//
// This function differs from RegisterWrapperEncoder by allowing the
// caller to explicitly decide whether the wrapper owns the entire
// error message or not. Otherwise, the relationship is inferred.
//
// Note: if the error type has been migrated from a previous location
// or a different type, ensure that RegisterTypeMigration() was called
// prior to RegisterWrapperEncoder().
func RegisterWrapperEncoderWithMessageType(
theType TypeKey, encoder WrapperEncoderWithMessageType,
) {
if encoder == nil {
delete(encoders, theType)
} else {
@@ -304,7 +397,42 @@ func RegisterWrapperEncoder(theType TypeKey, encoder WrapperEncoder) {
// WrapperEncoder is to be provided (via RegisterWrapperEncoder above)
// by additional wrapper types not yet known to this library.
type WrapperEncoder = func(ctx context.Context, err error) (msgPrefix string, safeDetails []string, payload proto.Message)
type WrapperEncoder func(ctx context.Context, err error) (
msgPrefix string,
safeDetails []string,
payload proto.Message,
)
// MessageType is used to encode information about an error message
// within a wrapper error type. This information is used to affect
// display logic.
type MessageType errorspb.MessageType
// Values below should match the ones in errorspb.MessageType for
// direct conversion.
const (
// Prefix denotes an error message that should be prepended to the
// message of its cause.
Prefix MessageType = MessageType(errorspb.MessageType_PREFIX)
// FullMessage denotes an error message that contains the text of its
// causes and can be displayed standalone.
FullMessage = MessageType(errorspb.MessageType_FULL_MESSAGE)
)
// WrapperEncoderWithMessageType is to be provided (via
// RegisterWrapperEncoderWithMessageType above) by additional wrapper
// types not yet known to this library. This encoder returns an
// additional enum which indicates whether the wrapper owns the error
// message completely instead of simply being a prefix with the error
// message of its causes appended to it. This information is encoded
// along with the prefix in order to provide context during error
// display.
type WrapperEncoderWithMessageType func(ctx context.Context, err error) (
msgPrefix string,
safeDetails []string,
payload proto.Message,
messageType MessageType,
)
// registry for RegisterWrapperType.
var encoders = map[TypeKey]WrapperEncoder{}
var encoders = map[TypeKey]WrapperEncoderWithMessageType{}

View File

@@ -102,7 +102,13 @@ func formatErrorInternal(err error, s fmt.State, verb rune, redactableOutput boo
// to enable stack trace de-duplication. This requires a
// post-order traversal. Since we have a linked list, the best we
// can do is a recursion.
p.formatRecursive(err, true /* isOutermost */, true /* withDetail */)
p.formatRecursive(
err,
true, /* isOutermost */
true, /* withDetail */
false, /* withDepth */
0, /* depth */
)
// We now have all the data, we can render the result.
p.formatEntries(err)
@@ -146,7 +152,13 @@ func formatErrorInternal(err error, s fmt.State, verb rune, redactableOutput boo
// by calling FormatError(), in which case we'd get an infinite
// recursion. So we have no choice but to peel the data
// and then assemble the pieces ourselves.
p.formatRecursive(err, true /* isOutermost */, false /* withDetail */)
p.formatRecursive(
err,
true, /* isOutermost */
false, /* withDetail */
false, /* withDepth */
0, /* depth */
)
p.formatSingleLineOutput()
p.finishDisplay(verb)
@@ -195,7 +207,19 @@ func (s *state) formatEntries(err error) {
// Wraps: (N) <details>
//
for i, j := len(s.entries)-2, 2; i >= 0; i, j = i-1, j+1 {
fmt.Fprintf(&s.finalBuf, "\nWraps: (%d)", j)
s.finalBuf.WriteByte('\n')
// Extra indentation starts at depth==2 because the direct
// children of the root error area already printed on separate
// newlines.
for m := 0; m < s.entries[i].depth-1; m += 1 {
if m == s.entries[i].depth-2 {
s.finalBuf.WriteString("└─ ")
} else {
s.finalBuf.WriteByte(' ')
s.finalBuf.WriteByte(' ')
}
}
fmt.Fprintf(&s.finalBuf, "Wraps: (%d)", j)
entry := s.entries[i]
s.printEntry(entry)
}
@@ -278,8 +302,9 @@ func (s *state) printEntry(entry formatEntry) {
//
// This function is used both when FormatError() is called indirectly
// from .Error(), e.g. in:
// (e *myType) Error() { return fmt.Sprintf("%v", e) }
// (e *myType) Format(s fmt.State, verb rune) { errors.FormatError(s, verb, e) }
//
// (e *myType) Error() { return fmt.Sprintf("%v", e) }
// (e *myType) Format(s fmt.State, verb rune) { errors.FormatError(s, verb, e) }
//
// and also to print the first line in the output of a %+v format.
//
@@ -329,13 +354,35 @@ func (s *state) formatSingleLineOutput() {
// s.finalBuf is untouched. The conversion of s.entries
// to s.finalBuf is done by formatSingleLineOutput() and/or
// formatEntries().
func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
//
// `withDepth` and `depth` are used to tag subtrees of multi-cause
// errors for added indentation during printing. Once a multi-cause
// error is encountered, all subsequent calls with set `withDepth` to
// true, and increment `depth` during recursion. This information is
// persisted into the generated entries and used later to display the
// error with increased indentation based in the depth.
func (s *state) formatRecursive(err error, isOutermost, withDetail, withDepth bool, depth int) int {
cause := UnwrapOnce(err)
numChildren := 0
if cause != nil {
// Recurse first.
s.formatRecursive(cause, false /*isOutermost*/, withDetail)
// Recurse first, which populates entries list starting from innermost
// entry. If we've previously seen a multi-cause wrapper, `withDepth`
// will be true, and we'll record the depth below ensuring that extra
// indentation is applied to this inner cause during printing.
// Otherwise, we maintain "straight" vertical formatting by keeping the
// parent callers `withDepth` value of `false` by default.
numChildren += s.formatRecursive(cause, false, withDetail, withDepth, depth+1)
}
causes := UnwrapMulti(err)
for _, c := range causes {
// Override `withDepth` to true for all child entries ensuring they have
// indentation applied during formatting to distinguish them from
// parents.
numChildren += s.formatRecursive(c, false, withDetail, true, depth+1)
}
// inserted := len(s.entries) - 1 - startChildren
// Reinitialize the state for this stage of wrapping.
s.wantDetail = withDetail
s.needSpace = false
@@ -354,11 +401,11 @@ func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
bufIsRedactable = true
desiredShortening := v.SafeFormatError((*safePrinter)(s))
if desiredShortening == nil {
// The error wants to elide the short messages from inner
// causes. Do it.
for i := range s.entries {
s.entries[i].elideShort = true
}
// The error wants to elide the short messages from inner causes.
// Read backwards through list of entries up to the number of new
// entries created "under" this one amount and mark `elideShort`
// true.
s.elideShortChildren(numChildren)
}
case Formatter:
@@ -366,9 +413,7 @@ func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
if desiredShortening == nil {
// The error wants to elide the short messages from inner
// causes. Do it.
for i := range s.entries {
s.entries[i].elideShort = true
}
s.elideShortChildren(numChildren)
}
case fmt.Formatter:
@@ -389,7 +434,11 @@ func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
s.lastStack = st.StackTrace()
}
} else {
s.formatSimple(err, cause)
if elideCauseMsg := s.formatSimple(err, cause); elideCauseMsg {
// The error wants to elide the short messages from inner
// causes. Do it.
s.elideShortChildren(numChildren)
}
}
default:
@@ -411,9 +460,7 @@ func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
if desiredShortening == nil {
// The error wants to elide the short messages from inner
// causes. Do it.
for i := range s.entries {
s.entries[i].elideShort = true
}
s.elideShortChildren(numChildren)
}
break
}
@@ -422,12 +469,21 @@ func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
// If the error did not implement errors.Formatter nor
// fmt.Formatter, but it is a wrapper, still attempt best effort:
// print what we can at this level.
s.formatSimple(err, cause)
elideChildren := s.formatSimple(err, cause)
// always elideChildren when dealing with multi-cause errors.
if len(causes) > 0 {
elideChildren = true
}
if elideChildren {
// The error wants to elide the short messages from inner
// causes. Do it.
s.elideShortChildren(numChildren)
}
}
}
// Collect the result.
entry := s.collectEntry(err, bufIsRedactable)
entry := s.collectEntry(err, bufIsRedactable, withDepth, depth)
// If there's an embedded stack trace, also collect it.
// This will get either a stack from pkg/errors, or ours.
@@ -441,9 +497,22 @@ func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
// Remember the entry for later rendering.
s.entries = append(s.entries, entry)
s.buf = bytes.Buffer{}
return numChildren + 1
}
func (s *state) collectEntry(err error, bufIsRedactable bool) formatEntry {
// elideShortChildren takes a number of entries to set `elideShort` to
// false. The reason a number of entries is needed is that we may be
// eliding a subtree of causes in the case of a multi-cause error. In
// the multi-cause case, we need to know how many of the prior errors
// in the list of entries is a child of this subtree.
func (s *state) elideShortChildren(newEntries int) {
for i := 0; i < newEntries; i++ {
s.entries[len(s.entries)-1-i].elideShort = true
}
}
func (s *state) collectEntry(err error, bufIsRedactable bool, withDepth bool, depth int) formatEntry {
entry := formatEntry{err: err}
if s.wantDetail {
// The buffer has been populated as a result of formatting with
@@ -480,6 +549,10 @@ func (s *state) collectEntry(err error, bufIsRedactable bool) formatEntry {
}
}
if withDepth {
entry.depth = depth
}
return entry
}
@@ -500,16 +573,23 @@ func RegisterSpecialCasePrinter(fn safeErrorPrinterFn) {
// formatSimple performs a best effort at extracting the details at a
// given level of wrapping when the error object does not implement
// the Formatter interface.
func (s *state) formatSimple(err, cause error) {
// Returns true if we want to elide errors from causal chain.
func (s *state) formatSimple(err, cause error) bool {
var pref string
elideCauses := false
if cause != nil {
pref = extractPrefix(err, cause)
var messageType MessageType
pref, messageType = extractPrefix(err, cause)
if messageType == FullMessage {
elideCauses = true
}
} else {
pref = err.Error()
}
if len(pref) > 0 {
s.Write([]byte(pref))
}
return elideCauses
}
// finishDisplay renders s.finalBuf into s.State.
@@ -568,7 +648,7 @@ type state struct {
// entries collect the result of formatRecursive(). They are
// consumed by formatSingleLineOutput() and formatEntries() to
// procude the contents of finalBuf.
// produce the contents of finalBuf.
entries []formatEntry
// buf collects the details of the current error object at a given
@@ -690,6 +770,11 @@ type formatEntry struct {
// truncated to avoid duplication of entries. This is used to
// display a truncation indicator during verbose rendering.
elidedStackTrace bool
// depth, if positive, represents a nesting depth of this error as
// a causer of others. This is used with verbose printing to
// illustrate the nesting depth for multi-cause error wrappers.
depth int
}
// String is used for debugging only.
@@ -711,6 +796,12 @@ func (s *state) Write(b []byte) (n int, err error) {
for i, c := range b {
if c == '\n' {
//if s.needNewline > 0 {
// for i := 0; i < s.needNewline-1; i++ {
// s.buf.Write(detailSep[:len(sep)-1])
// }
// s.needNewline = 0
//}
// Flush all the bytes seen so far.
s.buf.Write(b[k:i])
// Don't print the newline itself; instead, prepare the state so
@@ -740,6 +831,11 @@ func (s *state) Write(b []byte) (n int, err error) {
s.notEmpty = true
}
}
//if s.needNewline > 0 {
// for i := 0; i < s.needNewline-1; i++ {
// s.buf.Write(detailSep[:len(sep)-1])
// }
//}
s.buf.Write(b[k:])
return len(b), nil
}
@@ -766,6 +862,9 @@ func (p *state) switchOver() {
p.buf = bytes.Buffer{}
p.notEmpty = false
p.hasDetail = true
// One of the newlines is accounted for in the switch over.
// p.needNewline -= 1
}
func (s *printer) Detail() bool {
@@ -829,7 +928,7 @@ func (s *safePrinter) enhanceArgs(args []interface{}) {
args[i] = redact.Safe(thisStack)
lastSeen = st
}
// In contrast with (*printer).enhanceArgs(), we dont use a
// In contrast with (*printer).enhanceArgs(), we don't use a
// special case for `error` here, because the redact package
// already helps us recursing into a safe print for
// error objects.

View File

@@ -1,36 +0,0 @@
// Copyright 2019 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package errbase
import "io"
// formatSimpleError is a helper used by FormatError() for the top
// level error/wrapper argument, if it does not implement the
// errors.Formatter interface.
func formatSimpleError(err error, p *state, sep string) error {
if cause := UnwrapOnce(err); cause != nil {
pref := extractPrefix(err, cause)
p.buf.WriteString(pref)
if pref != "" {
p.buf.WriteByte(':')
p.buf.WriteString(sep)
}
err = cause
} else {
io.WriteString(&p.buf, err.Error())
err = nil
}
return err
}

View File

@@ -30,19 +30,39 @@ type opaqueLeaf struct {
details errorspb.EncodedErrorDetails
}
// opaqueLeafCauses is used when receiving an unknown multi-cause
// wrapper type. Its important property is that if it is communicated
// back to some network system that _does_ know about the type, the
// original object can be restored. We encode multi-cause errors as
// leaf nodes over the network, in order to support backwards
// compatibility with existing single-cause wrapper messages.
//
// This struct *must* be initialized with a non-nil causes value in
// order to comply with go stdlib expectations for `Unwrap()`.
type opaqueLeafCauses struct {
opaqueLeaf
causes []error
}
var _ error = (*opaqueLeaf)(nil)
var _ SafeDetailer = (*opaqueLeaf)(nil)
var _ fmt.Formatter = (*opaqueLeaf)(nil)
var _ SafeFormatter = (*opaqueLeaf)(nil)
var _ error = (*opaqueLeafCauses)(nil)
var _ SafeDetailer = (*opaqueLeafCauses)(nil)
var _ fmt.Formatter = (*opaqueLeafCauses)(nil)
var _ SafeFormatter = (*opaqueLeafCauses)(nil)
// opaqueWrapper is used when receiving an unknown wrapper type.
// Its important property is that if it is communicated
// back to some network system that _does_ know about
// the type, the original object can be restored.
type opaqueWrapper struct {
cause error
prefix string
details errorspb.EncodedErrorDetails
cause error
prefix string
details errorspb.EncodedErrorDetails
messageType MessageType
}
var _ error = (*opaqueWrapper)(nil)
@@ -53,6 +73,9 @@ var _ SafeFormatter = (*opaqueWrapper)(nil)
func (e *opaqueLeaf) Error() string { return e.msg }
func (e *opaqueWrapper) Error() string {
if e.messageType == FullMessage {
return e.prefix
}
if e.prefix == "" {
return e.cause.Error()
}
@@ -66,8 +89,12 @@ func (e *opaqueWrapper) Unwrap() error { return e.cause }
func (e *opaqueLeaf) SafeDetails() []string { return e.details.ReportablePayload }
func (e *opaqueWrapper) SafeDetails() []string { return e.details.ReportablePayload }
func (e *opaqueLeaf) Format(s fmt.State, verb rune) { FormatError(e, s, verb) }
func (e *opaqueWrapper) Format(s fmt.State, verb rune) { FormatError(e, s, verb) }
func (e *opaqueLeaf) Format(s fmt.State, verb rune) { FormatError(e, s, verb) }
func (e *opaqueLeafCauses) Format(s fmt.State, verb rune) { FormatError(e, s, verb) }
func (e *opaqueWrapper) Format(s fmt.State, verb rune) { FormatError(e, s, verb) }
// opaqueLeafCauses is a multi-cause wrapper
func (e *opaqueLeafCauses) Unwrap() []error { return e.causes }
func (e *opaqueLeaf) SafeFormatError(p Printer) (next error) {
p.Print(e.msg)
@@ -103,5 +130,8 @@ func (e *opaqueWrapper) SafeFormatError(p Printer) (next error) {
p.Printf("\npayload type: %s", redact.Safe(e.details.FullDetails.TypeUrl))
}
}
if e.messageType == FullMessage {
return nil
}
return e.cause
}

View File

@@ -27,6 +27,16 @@ package errbase
// It supports both errors implementing causer (`Cause()` method, from
// github.com/pkg/errors) and `Wrapper` (`Unwrap()` method, from the
// Go 2 error proposal).
//
// UnwrapOnce treats multi-errors (those implementing the
// `Unwrap() []error` interface as leaf-nodes since they cannot
// reasonably be iterated through to a single cause. These errors
// are typically constructed as a result of `fmt.Errorf` which results
// in a `wrapErrors` instance that contains an interpolated error
// string along with a list of causes.
//
// The go stdlib does not define output on `Unwrap()` for a multi-cause
// error, so we default to nil here.
func UnwrapOnce(err error) (cause error) {
switch e := err.(type) {
case interface{ Cause() error }:
@@ -39,6 +49,7 @@ func UnwrapOnce(err error) (cause error) {
// UnwrapAll accesses the root cause object of the error.
// If the error has no cause (leaf error), it is returned directly.
// UnwrapAll treats multi-errors as leaf nodes.
func UnwrapAll(err error) error {
for {
if cause := UnwrapOnce(err); cause != nil {
@@ -49,3 +60,12 @@ func UnwrapAll(err error) error {
}
return err
}
// UnwrapMulti access the slice of causes that an error contains, if it is a
// multi-error.
func UnwrapMulti(err error) []error {
if me, ok := err.(interface{ Unwrap() []error }); ok {
return me.Unwrap()
}
return nil
}

View File

@@ -86,6 +86,20 @@ func GetTypeKey(err error) TypeKey { return errbase.GetTypeKey(err) }
// A nil return indicates that decoding was not successful.
type LeafDecoder = errbase.LeafDecoder
// 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 = errbase.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) {
errbase.RegisterMultiCauseDecoder(theType, decoder)
}
// RegisterWrapperDecoder can be used to register new wrapper types to
// the library. Registered wrappers will be decoded using their own
// Go type when an error is decoded. Wrappers that have not been
@@ -135,6 +149,41 @@ func RegisterWrapperEncoder(typeName TypeKey, encoder WrapperEncoder) {
// by additional wrapper types not yet known to this library.
type WrapperEncoder = errbase.WrapperEncoder
// RegisterWrapperEncoderWithMessageType can be used to register new wrapper
// types to the library. These wrappers can optionally override the child error
// messages with their own error string instead of relying on iterative
// concatenation. Registered wrappers will be encoded using their own Go type
// when an error is encoded. Wrappers that have not been registered will be
// encoded using the opaqueWrapper type.
//
// Note: if the error type has been migrated from a previous location
// or a different type, ensure that RegisterTypeMigration() was called
// prior to RegisterWrapperEncoder().
func RegisterWrapperEncoderWithMessageType(typeName TypeKey, encoder WrapperEncoderWithMessageType) {
errbase.RegisterWrapperEncoderWithMessageType(typeName, encoder)
}
// WrapperEncoderWithMessageType is to be provided (via
// RegisterWrapperEncoderWithMessageType) by additional wrapper
// types not yet known to this library.
type WrapperEncoderWithMessageType = errbase.WrapperEncoderWithMessageType
// RegisterMultiCauseEncoder can be used to register new multi-cause
// error types to the library. Registered types will be encoded using
// their own Go type when an error is encoded. Multi-cause wrappers
// that have not been registered will be encoded using the
// opaqueWrapper type.
func RegisterMultiCauseEncoder(typeName TypeKey, encoder MultiCauseEncoder) {
errbase.RegisterMultiCauseEncoder(typeName, encoder)
}
// MultiCauseEncoder is to be provided (via RegisterMultiCauseEncoder
// above) by additional multi-cause wrapper types not yet known to this
// library. The encoder will automatically extract and encode the
// causes of this error by calling `Unwrap()` and expecting a slice of
// errors.
type MultiCauseEncoder = errbase.MultiCauseEncoder
// SetWarningFn enables configuration of the warning function.
func SetWarningFn(fn func(context.Context, string, ...interface{})) { errbase.SetWarningFn(fn) }
@@ -185,7 +234,8 @@ func Formattable(err error) fmt.Formatter { return errbase.Formattable(err) }
// The value of previousTypeName must be the result of calling
// reflect.TypeOf(err).String() on the original error object.
// This is usually composed as follows:
// [*]<shortpackage>.<errortype>
//
// [*]<shortpackage>.<errortype>
//
// For example, Go's standard error type has name "*errors.errorString".
// The asterisk indicates that `errorString` implements the `error`

View File

@@ -24,10 +24,42 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// MessageType encodes information regarding the interpretation of an
// error's message string.
type MessageType int32
const (
// Keeping zero value as default for backwards compatibility.
// PREFIX is a message that should be prepended to its cause.
MessageType_PREFIX MessageType = 0
// FULL_MESSAGE is a complete error message that can be displayed
// without its cause.
MessageType_FULL_MESSAGE MessageType = 1
)
var MessageType_name = map[int32]string{
0: "PREFIX",
1: "FULL_MESSAGE",
}
var MessageType_value = map[string]int32{
"PREFIX": 0,
"FULL_MESSAGE": 1,
}
func (x MessageType) String() string {
return proto.EnumName(MessageType_name, int32(x))
}
func (MessageType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_ddc818d0729874b8, []int{0}
}
// EncodedError is the wire-encodable representation
// of an error (or error cause chain).
type EncodedError struct {
// Types that are valid to be assigned to Error:
//
// *EncodedError_Leaf
// *EncodedError_Wrapper
Error isEncodedError_Error `protobuf_oneof:"error"`
@@ -108,7 +140,7 @@ func (*EncodedError) XXX_OneofWrappers() []interface{} {
}
// EncodedErrorLeaf is the wire-encodable representation
// of an error leaf.
// of an error leaf or a multi-cause wrapper from go 1.20+
type EncodedErrorLeaf struct {
// The main error message (mandatory), that can be printed to human
// users and may contain PII. This contains the value of the leaf
@@ -116,6 +148,11 @@ type EncodedErrorLeaf struct {
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
// The error details.
Details EncodedErrorDetails `protobuf:"bytes,2,opt,name=details,proto3" json:"details"`
// multierror_causes is a list of errors that contain the causal tree
// of this leaf. If this field is not empty, then this leaf encodes
// an error from go 1.20 or later that encodes multiple causes in its
// chain.
MultierrorCauses []*EncodedError `protobuf:"bytes,3,rep,name=multierror_causes,json=multierrorCauses,proto3" json:"multierror_causes,omitempty"`
}
func (m *EncodedErrorLeaf) Reset() { *m = EncodedErrorLeaf{} }
@@ -210,17 +247,23 @@ var xxx_messageInfo_EncodedErrorDetails proto.InternalMessageInfo
type EncodedWrapper struct {
// The cause error. Mandatory.
Cause EncodedError `protobuf:"bytes,1,opt,name=cause,proto3" json:"cause"`
// The wrapper message prefix (which may be empty). This
// isbprinted before the cause's own message when
// constructing a full message. This may contain PII.
// The wrapper message. This could either be a full error message
// that can be printed independently, or a (potentially empty) prefix
// which is printed before the cause's own message to construct the
// full message. This may contain PII.
//
// This is extracted automatically:
//
// - for wrappers that have a registered encoder,
// - otherwise, when the wrapper's Error() has its cause's Error() as suffix.
MessagePrefix string `protobuf:"bytes,2,opt,name=message_prefix,json=messagePrefix,proto3" json:"message_prefix,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
// The error details.
Details EncodedErrorDetails `protobuf:"bytes,3,opt,name=details,proto3" json:"details"`
// message_type encodes the interpretation of `message`. Prior
// versions will not set this field and it will be left as `PREFIX`.
// This retains backwards compatibility since the new behavior is
// only enabled when this enum is set to `FULL_MESSAGE`.
MessageType MessageType `protobuf:"varint,4,opt,name=message_type,json=messageType,proto3,enum=cockroach.errorspb.MessageType" json:"message_type,omitempty"`
}
func (m *EncodedWrapper) Reset() { *m = EncodedWrapper{} }
@@ -382,6 +425,7 @@ func (m *ErrnoPayload) XXX_DiscardUnknown() {
var xxx_messageInfo_ErrnoPayload proto.InternalMessageInfo
func init() {
proto.RegisterEnum("cockroach.errorspb.MessageType", MessageType_name, MessageType_value)
proto.RegisterType((*EncodedError)(nil), "cockroach.errorspb.EncodedError")
proto.RegisterType((*EncodedErrorLeaf)(nil), "cockroach.errorspb.EncodedErrorLeaf")
proto.RegisterType((*EncodedErrorDetails)(nil), "cockroach.errorspb.EncodedErrorDetails")
@@ -394,46 +438,51 @@ func init() {
func init() { proto.RegisterFile("errorspb/errors.proto", fileDescriptor_ddc818d0729874b8) }
var fileDescriptor_ddc818d0729874b8 = []byte{
// 624 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0x41, 0x6f, 0xd3, 0x4c,
0x10, 0xb5, 0xbf, 0xa4, 0x4d, 0x32, 0x49, 0xfa, 0x95, 0xa5, 0x48, 0x6e, 0x45, 0xdd, 0x34, 0x80,
0xa8, 0x2a, 0x70, 0x24, 0x38, 0x20, 0x21, 0x84, 0x44, 0x45, 0x45, 0x0f, 0x50, 0x2a, 0x53, 0x09,
0x89, 0x8b, 0xb5, 0x49, 0xc6, 0xee, 0xaa, 0xb6, 0xd7, 0xda, 0x75, 0x44, 0xfd, 0x2f, 0x90, 0xf8,
0x3f, 0x9c, 0x7b, 0xec, 0xb1, 0x27, 0x04, 0xa9, 0xf8, 0x17, 0x1c, 0x90, 0x77, 0xbd, 0x6d, 0x0a,
0x15, 0x3d, 0x70, 0x1b, 0xbf, 0x79, 0xe3, 0xf7, 0x3c, 0x6f, 0x0c, 0xb7, 0x50, 0x08, 0x2e, 0x64,
0x36, 0x1c, 0xe8, 0xc2, 0xcb, 0x04, 0xcf, 0x39, 0x21, 0x23, 0x3e, 0x3a, 0x14, 0x9c, 0x8e, 0x0e,
0x3c, 0x43, 0x58, 0x59, 0x8e, 0x38, 0x8f, 0x62, 0x1c, 0x28, 0xc6, 0x70, 0x12, 0x0e, 0x68, 0x5a,
0x68, 0xfa, 0xca, 0x52, 0xc4, 0x23, 0xae, 0xca, 0x41, 0x59, 0x69, 0xb4, 0xff, 0xd9, 0x86, 0xce,
0x76, 0x3a, 0xe2, 0x63, 0x1c, 0x6f, 0x97, 0x2f, 0x21, 0x4f, 0xa1, 0x1e, 0x23, 0x0d, 0x1d, 0xbb,
0x67, 0x6f, 0xb4, 0x1f, 0xdd, 0xf5, 0xfe, 0x14, 0xf1, 0x66, 0xf9, 0xaf, 0x91, 0x86, 0x3b, 0x96,
0xaf, 0x66, 0xc8, 0x73, 0x68, 0x7c, 0x14, 0x34, 0xcb, 0x50, 0x38, 0xff, 0xa9, 0xf1, 0xfe, 0x5f,
0xc6, 0xdf, 0x6b, 0xe6, 0x8e, 0xe5, 0x9b, 0xa1, 0xad, 0x06, 0xcc, 0x29, 0x56, 0x7f, 0x02, 0x8b,
0xbf, 0x8b, 0x10, 0x07, 0x1a, 0x09, 0x4a, 0x49, 0x23, 0x54, 0xde, 0x5a, 0xbe, 0x79, 0x24, 0xaf,
0xa0, 0x31, 0xc6, 0x9c, 0xb2, 0x58, 0x56, 0xb2, 0xf7, 0xaf, 0x73, 0xfd, 0x52, 0xd3, 0xb7, 0xea,
0xc7, 0x5f, 0xd7, 0x2c, 0xdf, 0x4c, 0xf7, 0x7f, 0xda, 0x70, 0xf3, 0x0a, 0x1a, 0x79, 0x00, 0x84,
0x0b, 0x16, 0xb1, 0x94, 0xc6, 0x41, 0x5e, 0x64, 0x18, 0xa4, 0x34, 0x31, 0x2e, 0x16, 0x4d, 0x67,
0xbf, 0xc8, 0x70, 0x97, 0x26, 0x48, 0xde, 0xc2, 0xff, 0x4a, 0x54, 0x53, 0x13, 0x2a, 0x0e, 0x2b,
0x5b, 0xeb, 0x57, 0xda, 0x2a, 0x8b, 0x72, 0xf6, 0x0d, 0x15, 0x87, 0x95, 0xa1, 0x2e, 0xce, 0x82,
0xe4, 0x21, 0x10, 0x81, 0x19, 0x17, 0x39, 0x1d, 0xc6, 0x18, 0x64, 0xb4, 0x88, 0x39, 0x1d, 0x3b,
0xb5, 0x5e, 0x6d, 0xa3, 0xe5, 0xdf, 0xb8, 0xe8, 0xec, 0xe9, 0x06, 0x79, 0x02, 0x9d, 0x70, 0x12,
0xc7, 0x81, 0xd9, 0x49, 0x5d, 0x89, 0x2f, 0x79, 0xfa, 0x34, 0x3c, 0x73, 0x1a, 0xde, 0x8b, 0xb4,
0xf0, 0xdb, 0x25, 0xb3, 0xfa, 0xcc, 0xfe, 0x17, 0x1b, 0x16, 0x2e, 0x87, 0x43, 0x9e, 0xc1, 0xdc,
0x88, 0x4e, 0x24, 0x56, 0xe7, 0xd0, 0xbb, 0x6e, 0xb1, 0xd5, 0x07, 0xe8, 0x21, 0x72, 0x0f, 0x16,
0xaa, 0x8c, 0x82, 0x4c, 0x60, 0xc8, 0x8e, 0xd4, 0x22, 0x5a, 0x7e, 0xb7, 0x42, 0xf7, 0x14, 0x38,
0x9b, 0x5f, 0xed, 0x9f, 0xf2, 0xdb, 0x85, 0xee, 0xa5, 0x75, 0x92, 0x35, 0x68, 0x87, 0x34, 0x61,
0x71, 0x31, 0x9b, 0x18, 0x68, 0x48, 0x65, 0x75, 0x1b, 0x5a, 0x78, 0x94, 0x63, 0x2a, 0x19, 0x4f,
0x2b, 0x73, 0x17, 0x40, 0x7f, 0x13, 0x16, 0xde, 0xe5, 0x82, 0xa5, 0x91, 0x34, 0xbb, 0x75, 0x2e,
0xac, 0xda, 0x6a, 0xff, 0xe7, 0xda, 0x3f, 0xca, 0x1f, 0x49, 0x88, 0x94, 0x1b, 0xea, 0x2a, 0x40,
0x79, 0x1a, 0x01, 0x96, 0xa0, 0x92, 0xae, 0xf9, 0xad, 0x12, 0x51, 0x2c, 0x42, 0xa0, 0x4e, 0xc5,
0xe8, 0xa0, 0x12, 0x55, 0x35, 0xb9, 0x03, 0x5d, 0x26, 0x83, 0x0c, 0x45, 0xc2, 0xa4, 0x72, 0x54,
0xae, 0xa3, 0xe9, 0x77, 0x98, 0xdc, 0x3b, 0xc7, 0xc8, 0x32, 0x34, 0x99, 0x0c, 0xf0, 0x88, 0xc9,
0x5c, 0x45, 0xdb, 0xf4, 0x1b, 0x4c, 0x6e, 0x97, 0x8f, 0xa4, 0x07, 0x1d, 0x26, 0x83, 0x94, 0xe7,
0x55, 0x7b, 0x4e, 0xb5, 0x81, 0xc9, 0x5d, 0x9e, 0x6b, 0xc6, 0x2a, 0x00, 0x93, 0x41, 0xce, 0x12,
0xe4, 0x93, 0xdc, 0x99, 0x57, 0xfd, 0x16, 0x93, 0xfb, 0x1a, 0x20, 0xeb, 0xea, 0x05, 0x39, 0x26,
0x19, 0x17, 0x54, 0x14, 0x4e, 0x43, 0x11, 0xda, 0x4c, 0xee, 0x1b, 0x68, 0x6b, 0xf3, 0xf8, 0xbb,
0x6b, 0x1d, 0x4f, 0x5d, 0xfb, 0x64, 0xea, 0xda, 0xa7, 0x53, 0xd7, 0xfe, 0x36, 0x75, 0xed, 0x4f,
0x67, 0xae, 0x75, 0x72, 0xe6, 0x5a, 0xa7, 0x67, 0xae, 0xf5, 0xa1, 0x69, 0x22, 0x1b, 0xce, 0xab,
0x5b, 0x7b, 0xfc, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x63, 0x5c, 0xa2, 0x72, 0xc1, 0x04, 0x00, 0x00,
// 693 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0x41, 0x4f, 0x13, 0x41,
0x14, 0xde, 0xa5, 0x85, 0xb6, 0xaf, 0x05, 0xcb, 0x88, 0xc9, 0x42, 0x64, 0x29, 0xab, 0x89, 0x04,
0x75, 0x9b, 0xe0, 0xc1, 0xc4, 0x18, 0x13, 0xaa, 0x05, 0x4c, 0x28, 0x92, 0x05, 0xa3, 0xf1, 0xb2,
0x99, 0xb6, 0xd3, 0x65, 0xc2, 0xee, 0xce, 0x66, 0x66, 0x1b, 0xd9, 0x7f, 0x61, 0xe2, 0x5f, 0xf2,
0xc0, 0x91, 0x23, 0x27, 0xa3, 0x25, 0xfe, 0x04, 0x6f, 0x1e, 0xcc, 0xce, 0xee, 0xd8, 0xa2, 0x28,
0x07, 0x6f, 0x6f, 0xde, 0xfb, 0xbe, 0xf7, 0xde, 0xf7, 0xde, 0x1b, 0xb8, 0x45, 0x38, 0x67, 0x5c,
0x44, 0xdd, 0x66, 0x66, 0xd8, 0x11, 0x67, 0x31, 0x43, 0xa8, 0xc7, 0x7a, 0xc7, 0x9c, 0xe1, 0xde,
0x91, 0xad, 0x00, 0x4b, 0x8b, 0x1e, 0x63, 0x9e, 0x4f, 0x9a, 0x12, 0xd1, 0x1d, 0x0e, 0x9a, 0x38,
0x4c, 0x32, 0xf8, 0xd2, 0x82, 0xc7, 0x3c, 0x26, 0xcd, 0x66, 0x6a, 0x65, 0x5e, 0xeb, 0xa3, 0x0e,
0xb5, 0x76, 0xd8, 0x63, 0x7d, 0xd2, 0x6f, 0xa7, 0x49, 0xd0, 0x13, 0x28, 0xfa, 0x04, 0x0f, 0x0c,
0xbd, 0xa1, 0xaf, 0x55, 0x37, 0xee, 0xda, 0x7f, 0x16, 0xb1, 0x27, 0xf1, 0xbb, 0x04, 0x0f, 0x76,
0x34, 0x47, 0x72, 0xd0, 0x33, 0x28, 0xbd, 0xe7, 0x38, 0x8a, 0x08, 0x37, 0xa6, 0x24, 0xdd, 0xfa,
0x07, 0xfd, 0x4d, 0x86, 0xdc, 0xd1, 0x1c, 0x45, 0x6a, 0x95, 0x60, 0x5a, 0xa2, 0xac, 0x4f, 0x3a,
0xd4, 0x7f, 0xaf, 0x82, 0x0c, 0x28, 0x05, 0x44, 0x08, 0xec, 0x11, 0xd9, 0x5c, 0xc5, 0x51, 0x4f,
0xb4, 0x0d, 0xa5, 0x3e, 0x89, 0x31, 0xf5, 0x45, 0x5e, 0xf7, 0xde, 0x75, 0x6d, 0xbf, 0xc8, 0xe0,
0xad, 0xe2, 0xe9, 0xe7, 0x15, 0xcd, 0x51, 0x6c, 0xd4, 0x81, 0xf9, 0x60, 0xe8, 0xc7, 0x54, 0x72,
0xdc, 0x1e, 0x1e, 0x0a, 0x22, 0x8c, 0x42, 0xa3, 0xb0, 0x56, 0xdd, 0x68, 0x5c, 0x97, 0xd2, 0xa9,
0x8f, 0xa9, 0xcf, 0x25, 0xd3, 0xfa, 0xa1, 0xc3, 0xcd, 0x2b, 0xaa, 0xa2, 0x07, 0x80, 0x18, 0xa7,
0x1e, 0x0d, 0xb1, 0xef, 0xc6, 0x49, 0x44, 0xdc, 0x10, 0x07, 0x4a, 0x54, 0x5d, 0x45, 0x0e, 0x93,
0x88, 0xec, 0xe1, 0x80, 0xa0, 0x57, 0x70, 0x23, 0xeb, 0x47, 0x42, 0x03, 0xcc, 0x8f, 0x73, 0x95,
0xab, 0x57, 0xb6, 0x94, 0x1a, 0x29, 0xb7, 0x83, 0xf9, 0x71, 0xae, 0x6f, 0x96, 0x4c, 0x3a, 0xd1,
0x43, 0x40, 0x9c, 0x44, 0x8c, 0xc7, 0xb8, 0xeb, 0x13, 0x37, 0xc2, 0x89, 0xcf, 0x70, 0x5f, 0xca,
0xac, 0x38, 0xf3, 0xe3, 0xc8, 0x7e, 0x16, 0x40, 0x8f, 0xa1, 0x36, 0x18, 0xfa, 0xbe, 0xab, 0x46,
0x5c, 0x94, 0xc5, 0x17, 0xec, 0xec, 0xd4, 0x6c, 0x75, 0x6a, 0xf6, 0x66, 0x98, 0x38, 0xd5, 0x14,
0x99, 0xcb, 0xb4, 0xbe, 0xeb, 0x30, 0x77, 0x79, 0xd9, 0xe8, 0x29, 0x4c, 0xcb, 0xa9, 0xe6, 0xe7,
0x75, 0xed, 0x50, 0x73, 0x01, 0x19, 0x69, 0xf2, 0x02, 0xa6, 0xfe, 0x7a, 0x01, 0x85, 0xff, 0xba,
0x80, 0x16, 0xd4, 0xf2, 0x9c, 0x72, 0xdc, 0x52, 0xec, 0xdc, 0xc6, 0xca, 0x55, 0xd9, 0x3a, 0x19,
0x2e, 0x1d, 0xab, 0x53, 0x0d, 0xc6, 0x0f, 0x6b, 0x0f, 0x66, 0x2f, 0x6d, 0x01, 0xad, 0x40, 0x75,
0x80, 0x03, 0xea, 0x27, 0x93, 0x8b, 0x86, 0xcc, 0x25, 0x57, 0x7c, 0x1b, 0x2a, 0xe4, 0x24, 0x26,
0xa1, 0xa0, 0x2c, 0xcc, 0xa5, 0x8d, 0x1d, 0xd6, 0x3a, 0xcc, 0x1d, 0xc4, 0x9c, 0x86, 0x9e, 0x50,
0x2b, 0x31, 0xc6, 0x72, 0x75, 0xb9, 0x36, 0xf5, 0xb4, 0xbe, 0xa5, 0xff, 0x99, 0xf3, 0x90, 0x29,
0xe8, 0x32, 0x40, 0x7a, 0x51, 0x2e, 0x49, 0x9d, 0xb2, 0x74, 0xc1, 0xa9, 0xa4, 0x1e, 0x89, 0x42,
0x08, 0x8a, 0x98, 0xf7, 0x8e, 0xf2, 0xa2, 0xd2, 0x46, 0x77, 0x60, 0x96, 0x0a, 0x37, 0x22, 0x3c,
0xa0, 0x42, 0x76, 0x94, 0x8e, 0xb4, 0xec, 0xd4, 0xa8, 0xd8, 0xff, 0xe5, 0x43, 0x8b, 0x50, 0xa6,
0xc2, 0x25, 0x27, 0x54, 0xc4, 0x72, 0x48, 0x65, 0xa7, 0x44, 0x45, 0x3b, 0x7d, 0xa2, 0x06, 0xd4,
0xa8, 0x70, 0x43, 0x16, 0xe7, 0xe1, 0x69, 0x19, 0x06, 0x2a, 0xf6, 0x58, 0x9c, 0x21, 0x96, 0x01,
0xa8, 0x70, 0x63, 0x1a, 0x10, 0x36, 0x8c, 0x8d, 0x19, 0x19, 0xaf, 0x50, 0x71, 0x98, 0x39, 0xd0,
0xaa, 0x4c, 0x10, 0x93, 0x20, 0x62, 0x1c, 0xf3, 0xc4, 0x28, 0x49, 0x40, 0x95, 0x8a, 0x43, 0xe5,
0x5a, 0xbf, 0x0f, 0xd5, 0x89, 0xf9, 0x23, 0x80, 0x99, 0x7d, 0xa7, 0xbd, 0xf5, 0xf2, 0x6d, 0x5d,
0x43, 0x75, 0xa8, 0x6d, 0xbd, 0xde, 0xdd, 0x75, 0x3b, 0xed, 0x83, 0x83, 0xcd, 0xed, 0x76, 0x5d,
0x6f, 0xad, 0x9f, 0x7e, 0x35, 0xb5, 0xd3, 0x91, 0xa9, 0x9f, 0x8d, 0x4c, 0xfd, 0x7c, 0x64, 0xea,
0x5f, 0x46, 0xa6, 0xfe, 0xe1, 0xc2, 0xd4, 0xce, 0x2e, 0x4c, 0xed, 0xfc, 0xc2, 0xd4, 0xde, 0x95,
0xd5, 0x56, 0xbb, 0x33, 0xf2, 0x9e, 0x1f, 0xfd, 0x0c, 0x00, 0x00, 0xff, 0xff, 0xd7, 0x2c, 0x21,
0xe3, 0x75, 0x05, 0x00, 0x00,
}
func (m *EncodedError) Marshal() (dAtA []byte, err error) {
@@ -530,6 +579,20 @@ func (m *EncodedErrorLeaf) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.MultierrorCauses) > 0 {
for iNdEx := len(m.MultierrorCauses) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.MultierrorCauses[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintErrors(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
}
{
size, err := m.Details.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
@@ -631,6 +694,11 @@ func (m *EncodedWrapper) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.MessageType != 0 {
i = encodeVarintErrors(dAtA, i, uint64(m.MessageType))
i--
dAtA[i] = 0x20
}
{
size, err := m.Details.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
@@ -641,10 +709,10 @@ func (m *EncodedWrapper) MarshalToSizedBuffer(dAtA []byte) (int, error) {
}
i--
dAtA[i] = 0x1a
if len(m.MessagePrefix) > 0 {
i -= len(m.MessagePrefix)
copy(dAtA[i:], m.MessagePrefix)
i = encodeVarintErrors(dAtA, i, uint64(len(m.MessagePrefix)))
if len(m.Message) > 0 {
i -= len(m.Message)
copy(dAtA[i:], m.Message)
i = encodeVarintErrors(dAtA, i, uint64(len(m.Message)))
i--
dAtA[i] = 0x12
}
@@ -874,6 +942,12 @@ func (m *EncodedErrorLeaf) Size() (n int) {
}
l = m.Details.Size()
n += 1 + l + sovErrors(uint64(l))
if len(m.MultierrorCauses) > 0 {
for _, e := range m.MultierrorCauses {
l = e.Size()
n += 1 + l + sovErrors(uint64(l))
}
}
return n
}
@@ -910,12 +984,15 @@ func (m *EncodedWrapper) Size() (n int) {
_ = l
l = m.Cause.Size()
n += 1 + l + sovErrors(uint64(l))
l = len(m.MessagePrefix)
l = len(m.Message)
if l > 0 {
n += 1 + l + sovErrors(uint64(l))
}
l = m.Details.Size()
n += 1 + l + sovErrors(uint64(l))
if m.MessageType != 0 {
n += 1 + sovErrors(uint64(m.MessageType))
}
return n
}
@@ -1202,6 +1279,40 @@ func (m *EncodedErrorLeaf) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field MultierrorCauses", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowErrors
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthErrors
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthErrors
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.MultierrorCauses = append(m.MultierrorCauses, &EncodedError{})
if err := m.MultierrorCauses[len(m.MultierrorCauses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipErrors(dAtA[iNdEx:])
@@ -1470,7 +1581,7 @@ func (m *EncodedWrapper) Unmarshal(dAtA []byte) error {
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field MessagePrefix", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
@@ -1498,7 +1609,7 @@ func (m *EncodedWrapper) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.MessagePrefix = string(dAtA[iNdEx:postIndex])
m.Message = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
@@ -1533,6 +1644,25 @@ func (m *EncodedWrapper) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field MessageType", wireType)
}
m.MessageType = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowErrors
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.MessageType |= MessageType(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipErrors(dAtA[iNdEx:])

View File

@@ -17,7 +17,7 @@ message EncodedError {
}
// EncodedErrorLeaf is the wire-encodable representation
// of an error leaf.
// of an error leaf or a multi-cause wrapper from go 1.20+
message EncodedErrorLeaf {
// The main error message (mandatory), that can be printed to human
// users and may contain PII. This contains the value of the leaf
@@ -26,6 +26,12 @@ message EncodedErrorLeaf {
// The error details.
EncodedErrorDetails details = 2 [(gogoproto.nullable) = false];
// multierror_causes is a list of errors that contain the causal tree
// of this leaf. If this field is not empty, then this leaf encodes
// an error from go 1.20 or later that encodes multiple causes in its
// chain.
repeated EncodedError multierror_causes = 3;
}
message EncodedErrorDetails {
@@ -60,24 +66,42 @@ message EncodedErrorDetails {
google.protobuf.Any full_details = 4;
}
// MessageType encodes information regarding the interpretation of an
// error's message string.
enum MessageType {
// Keeping zero value as default for backwards compatibility.
// PREFIX is a message that should be prepended to its cause.
PREFIX = 0;
// FULL_MESSAGE is a complete error message that can be displayed
// without its cause.
FULL_MESSAGE = 1;
}
// EncodedWrapper is the wire-encodable representation
// of an error wrapper.
message EncodedWrapper {
// The cause error. Mandatory.
EncodedError cause = 1 [(gogoproto.nullable) = false];
// The wrapper message prefix (which may be empty). This
// isbprinted before the cause's own message when
// constructing a full message. This may contain PII.
// The wrapper message. This could either be a full error message
// that can be printed independently, or a (potentially empty) prefix
// which is printed before the cause's own message to construct the
// full message. This may contain PII.
//
// This is extracted automatically:
//
// - for wrappers that have a registered encoder,
// - otherwise, when the wrapper's Error() has its cause's Error() as suffix.
string message_prefix = 2;
string message = 2;
// The error details.
EncodedErrorDetails details = 3 [(gogoproto.nullable) = false];
// message_type encodes the interpretation of `message`. Prior
// versions will not set this field and it will be left as `PREFIX`.
// This retains backwards compatibility since the new behavior is
// only enabled when this enum is set to `FULL_MESSAGE`.
MessageType message_type = 4;
}
// ErrorTypeMark identifies an error type for the purpose of determining

View File

@@ -58,7 +58,16 @@ func As(err error, target interface{}) bool {
if x, ok := c.(interface{ As(interface{}) bool }); ok && x.As(target) {
return true
}
// If at any point in the single cause chain including the top,
// we encounter a multi-cause chain, recursively explore it.
for _, cause := range errbase.UnwrapMulti(c) {
if As(cause, target) {
return true
}
}
}
return false
}

View File

@@ -14,6 +14,6 @@
// Package errutil combines primitives from the library, the Go errors
// package and github.com/pkg/errors. It aims to serve as drop-in
// replacement to gitub.com/pkg/errors and replace the legacy
// replacement to github.com/pkg/errors and replace the legacy
// implementation of `pgerrors`.
package errutil

View File

@@ -15,6 +15,7 @@
package errutil
import (
"github.com/cockroachdb/errors/join"
"github.com/cockroachdb/errors/secondary"
"github.com/cockroachdb/errors/withstack"
"github.com/cockroachdb/redact"
@@ -158,3 +159,10 @@ func WrapWithDepthf(depth int, err error, format string, args ...interface{}) er
err = withstack.WithStackDepth(err, depth+1)
return err
}
// JoinWithDepth constructs a Join error with the provided list of
// errors as arguments, and wraps it in a `WithStackDepth` to capture a
// stacktrace alongside.
func JoinWithDepth(depth int, errs ...error) error {
return withstack.WithStackDepth(join.Join(errs...), depth+1)
}

View File

@@ -192,3 +192,19 @@ func HandleAsAssertionFailureDepth(depth int, origErr error) error {
// - it also supports recursing through causes with Cause().
// - if it detects an API use error, its panic object is a valid error.
func As(err error, target interface{}) bool { return errutil.As(err, target) }
// Join returns an error that wraps the given errors.
// Any nil error values are discarded.
// Join returns nil if errs contains no non-nil values.
// The error formats as the concatenation of the strings obtained
// by calling the Error method of each element of errs, with a newline
// between each string. A stack trace is also retained.
func Join(errs ...error) error {
return errutil.JoinWithDepth(1, errs...)
}
// JoinWithDepth is like Join but the depth at which the call stack is
// captured can be specified.
func JoinWithDepth(depth int, errs ...error) error {
return errutil.JoinWithDepth(depth+1, errs...)
}

View File

@@ -23,6 +23,7 @@ import (
// WithHint decorates an error with a textual hint.
// The hint may contain PII and thus will not reportable.
// The suggested use case for hint is to relay information to end users.
//
// Hint is shown:
// - when formatting with `%+v`.
@@ -97,10 +98,12 @@ type ErrorHinter interface {
// WithDetail decorates an error with a textual detail.
// The detail may contain PII and thus will not reportable.
// The suggested use case for detail is to augment errors with information
// useful for debugging.
//
// Detail is shown:
// - when formatting with `%+v`.
// - with `GetAllHints()` / `FlattenHints()` below.
// - with `GetAllDetails()` / `FlattenDetails()` below.
//
// Note: the detail does not appear in the main error message returned
// with Error(). Use GetAllDetails() or FlattenDetails() to retrieve

View File

@@ -27,6 +27,7 @@ type ErrorDetailer = hintdetail.ErrorDetailer
// WithHint decorates an error with a textual hint.
// The hint may contain PII and thus will not reportable.
// The suggested use case for hint is to relay information to end users.
//
// Hint is shown:
// - when formatting with `%+v`.
@@ -44,10 +45,12 @@ func WithHintf(err error, format string, args ...interface{}) error {
// WithDetail decorates an error with a textual detail.
// The detail may contain PII and thus will not reportable.
// The suggested use case for detail is to augment errors with information
// useful for debugging.
//
// Detail is shown:
// - when formatting with `%+v`.
// - with `GetAllHints()` / `FlattenHints()` below.
// - with `GetAllDetails()` / `FlattenDetails()` below.
//
// Note: the detail does not appear in the main error message returned
// with Error(). Use GetAllDetails() or FlattenDetails() to retrieve

105
vendor/github.com/cockroachdb/errors/join/join.go generated vendored Normal file
View File

@@ -0,0 +1,105 @@
// Copyright 2019 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package join
import (
"context"
"fmt"
"github.com/cockroachdb/errors/errbase"
"github.com/cockroachdb/redact"
"github.com/gogo/protobuf/proto"
)
// Join returns an error that wraps the given errors.
// Any nil error values are discarded.
// Join returns nil if errs contains no non-nil values.
// The error formats as the concatenation of the strings obtained
// by calling the Error method of each element of errs, with a newline
// between each string.
func Join(errs ...error) error {
n := 0
for _, err := range errs {
if err != nil {
n++
}
}
if n == 0 {
return nil
}
e := &joinError{
errs: make([]error, 0, n),
}
for _, err := range errs {
if err != nil {
e.errs = append(e.errs, err)
}
}
return e
}
type joinError struct {
errs []error
}
var _ error = (*joinError)(nil)
var _ fmt.Formatter = (*joinError)(nil)
var _ errbase.SafeFormatter = (*joinError)(nil)
func (e *joinError) Error() string {
return redact.Sprint(e).StripMarkers()
}
func (e *joinError) Unwrap() []error {
return e.errs
}
func (e *joinError) SafeFormatError(p errbase.Printer) error {
for i, err := range e.errs {
if i > 0 {
p.Print("\n")
}
p.Print(err)
}
return nil
}
func (e *joinError) Format(s fmt.State, verb rune) {
errbase.FormatError(e, s, verb)
}
func init() {
errbase.RegisterMultiCauseEncoder(
errbase.GetTypeKey(&joinError{}),
func(
ctx context.Context,
err error,
) (msg string, safeDetails []string, payload proto.Message) {
return "", nil, nil
},
)
errbase.RegisterMultiCauseDecoder(
errbase.GetTypeKey(&joinError{}),
func(
ctx context.Context,
causes []error,
msgPrefix string,
safeDetails []string,
payload proto.Message,
) error {
return Join(causes...)
},
)
}

View File

@@ -59,6 +59,13 @@ func Is(err, reference error) bool {
if tryDelegateToIsMethod(c, reference) {
return true
}
// Recursively try multi-error causes, if applicable.
for _, me := range errbase.UnwrapMulti(c) {
if Is(me, reference) {
return true
}
}
}
if err == nil {
@@ -68,8 +75,8 @@ func Is(err, reference error) bool {
return false
}
// Not directly equal. Try harder, using error marks. We don't this
// during the loop above as it may be more expensive.
// Not directly equal. Try harder, using error marks. We don't do
// this during the loop above as it may be more expensive.
//
// Note: there is a more effective recursive algorithm that ensures
// that any pair of string only gets compared once. Should the
@@ -172,6 +179,13 @@ func IsAny(err error, references ...error) bool {
return true
}
}
// Recursively try multi-error causes, if applicable.
for _, me := range errbase.UnwrapMulti(c) {
if IsAny(me, references...) {
return true
}
}
}
// Try harder with marks.

View File

@@ -34,36 +34,39 @@ import (
//
// A Sentry report is displayed visually in the Sentry UI as follows:
//
////////////////
// //////////////
// Title: (1) some prefix in bold (2) one line for a stack trace
// (3) a single-line subtitle
//
// (4) the tags, as a tag soup (concatenated in a single paragrah,
// (4) the tags, as a tag soup (concatenated in a single paragraph,
// unsorted)
//
// (5) a "message"
//
// (6) zero or more "exceptions", each composed of:
// (7) a bold title
// (8) some freeform text
// (9) a stack trace
//
// (7) a bold title
// (8) some freeform text
// (9) a stack trace
//
// (10) metadata fields: environment, arch, etc
//
// (11) "Additional data" fields
//
// (12) SDK version
/////////////////
// ///////////////
//
// These visual items map to the Sentry Event object as follows:
//
// (1) the Type field of the 1st Exception object, if any
// otherwise the Message field
//
// otherwise the Message field
//
// (2) the topmost entry from the Stacktrace field of the 1st Exception object, if any
// (3) the Value field of the 1st Exception object, if any, unwrapped as a single line
// (4) the Tags field
// (5) the Message field
// (7) the Type field (same as (1) for 1st execption)
// (7) the Type field (same as (1) for 1st exception)
// (8) the Value field (same as (3) for 1st exception)
// (9) the Stacktrace field (input to (2) on 1st exception)
// (10) the other fields on the Event object
@@ -78,7 +81,9 @@ import (
// (3)/(8): first line of verbose error printout
// (4): not populated in this function, caller is to manage this
// (5): detailed structure of the entire error object, with references to
// additional "exception" objects
//
// additional "exception" objects
//
// (9): generated from innermost stack trace
// (6): every exception object after the 1st reports additional stack trace contexts
// (11): the detailed error types and their error mark.
@@ -92,7 +97,6 @@ import (
// is included in the Sentry report. This does not affect error types
// provided by the library, but could impact error types defined by
// 3rd parties. This limitation may be lifted in a later version.
//
func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]interface{}) {
if err == nil {
// No error: do nothing.
@@ -102,13 +106,13 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
// First step is to collect the details.
var stacks []*withstack.ReportableStackTrace
var details []errbase.SafeDetailPayload
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
visitAllMulti(err, func(c error) {
st := withstack.GetReportableStackTrace(c)
stacks = append(stacks, st)
sd := errbase.GetSafeDetails(c)
details = append(details, sd)
}
})
module := string(domains.GetDomain(err))
// firstDetailLine is the first detail string encountered.
@@ -292,7 +296,7 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
event.Message = longMsgBuf.String()
event.Exception = exceptions
// If there is no exception payload, synthetize one.
// If there is no exception payload, synthesize one.
if len(event.Exception) == 0 {
// We know we don't have a stack trace to extract line/function
// info from (if we had, we'd have an Exception payload at that
@@ -397,3 +401,13 @@ func reverseExceptionOrder(ex []sentry.Exception) {
ex[i], ex[len(ex)-i-1] = ex[len(ex)-i-1], ex[i]
}
}
func visitAllMulti(err error, f func(error)) {
f(err)
if e := errbase.UnwrapOnce(err); e != nil {
visitAllMulti(e, f)
}
for _, e := range errbase.UnwrapMulti(err) {
visitAllMulti(e, f)
}
}

View File

@@ -32,7 +32,7 @@ import (
// Title: (1) some prefix in bold (2) one line for a stack trace
// (3) a single-line subtitle
//
// (4) the tags, as a tag soup (concatenated in a single paragrah,
// (4) the tags, as a tag soup (concatenated in a single paragraph,
// unsorted)
//
// (5) a "message"
@@ -57,7 +57,7 @@ import (
// (3) the Value field of the 1st Exception object, if any, unwrapped as a single line
// (4) the Tags field
// (5) the Message field
// (7) the Type field (same as (1) for 1st execption)
// (7) the Type field (same as (1) for 1st exception)
// (8) the Value field (same as (3) for 1st exception)
// (9) the Stacktrace field (input to (2) on 1st exception)
// (10) the other fields on the Event object