Initial commit
This commit is contained in:
214
vendor/github.com/cockroachdb/errors/errbase/adapters.go
generated
vendored
Normal file
214
vendor/github.com/cockroachdb/errors/errbase/adapters.go
generated
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
// 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 (
|
||||
"context"
|
||||
goErr "errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/cockroachdb/errors/errorspb"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
pkgErr "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// This file provides the library the ability to encode/decode
|
||||
// standard error types.
|
||||
|
||||
// errors.errorString from base Go does not need an encoding
|
||||
// function, because the base encoding logic in EncodeLeaf() is
|
||||
// able to extract everything about it.
|
||||
|
||||
// we can then decode it exactly.
|
||||
func decodeErrorString(_ context.Context, msg string, _ []string, _ proto.Message) error {
|
||||
return goErr.New(msg)
|
||||
}
|
||||
|
||||
// context.DeadlineExceeded uses a custom type.
|
||||
func decodeDeadlineExceeded(_ context.Context, _ string, _ []string, _ proto.Message) error {
|
||||
return context.DeadlineExceeded
|
||||
}
|
||||
|
||||
// errors.fundamental from github.com/pkg/errors cannot be encoded
|
||||
// exactly because it includes a non-serializable stack trace
|
||||
// object. In order to work with it, we encode it by dumping
|
||||
// the stack trace in a safe reporting detail field, and decode
|
||||
// it as an opaqueLeaf instance in this package.
|
||||
|
||||
func encodePkgFundamental(
|
||||
_ context.Context, err error,
|
||||
) (msg string, safe []string, _ proto.Message) {
|
||||
msg = err.Error()
|
||||
iErr := err.(interface{ StackTrace() pkgErr.StackTrace })
|
||||
safeDetails := []string{fmt.Sprintf("%+v", iErr.StackTrace())}
|
||||
return msg, safeDetails, nil
|
||||
}
|
||||
|
||||
// errors.withMessage from github.com/pkg/errors can be encoded
|
||||
// exactly because it just has a message prefix. The base encoding
|
||||
// logic in EncodeWrapper() is able to extract everything from it.
|
||||
|
||||
// we can then decode it exactly.
|
||||
func decodeWithMessage(
|
||||
_ context.Context, cause error, msgPrefix string, _ []string, _ proto.Message,
|
||||
) error {
|
||||
return pkgErr.WithMessage(cause, msgPrefix)
|
||||
}
|
||||
|
||||
// errors.withStack from github.com/pkg/errors cannot be encoded
|
||||
// exactly because it includes a non-serializable stack trace
|
||||
// object. In order to work with it, we encode it by dumping
|
||||
// the stack trace in a safe reporting detail field, and decode
|
||||
// it as an opaqueWrapper instance in this package.
|
||||
|
||||
func encodePkgWithStack(
|
||||
_ context.Context, err error,
|
||||
) (msgPrefix string, safe []string, _ proto.Message) {
|
||||
iErr := err.(interface{ StackTrace() pkgErr.StackTrace })
|
||||
safeDetails := []string{fmt.Sprintf("%+v", iErr.StackTrace())}
|
||||
return "" /* withStack does not have a message prefix */, safeDetails, nil
|
||||
}
|
||||
|
||||
func encodePathError(
|
||||
_ context.Context, err error,
|
||||
) (msgPrefix string, safe []string, details proto.Message) {
|
||||
p := err.(*os.PathError)
|
||||
msg := p.Op + " " + p.Path
|
||||
details = &errorspb.StringsPayload{
|
||||
Details: []string{p.Op, p.Path},
|
||||
}
|
||||
return msg, []string{p.Op}, details
|
||||
}
|
||||
|
||||
func decodePathError(
|
||||
_ context.Context, cause error, _ string, _ []string, payload proto.Message,
|
||||
) (result error) {
|
||||
m, ok := payload.(*errorspb.StringsPayload)
|
||||
if !ok || len(m.Details) < 2 {
|
||||
// If this ever happens, this means some version of the library
|
||||
// (presumably future) changed the payload type, and we're
|
||||
// receiving this here. In this case, give up and let
|
||||
// DecodeError use the opaque type.
|
||||
return nil
|
||||
}
|
||||
return &os.PathError{
|
||||
Op: m.Details[0],
|
||||
Path: m.Details[1],
|
||||
Err: cause,
|
||||
}
|
||||
}
|
||||
|
||||
func encodeLinkError(
|
||||
_ context.Context, err error,
|
||||
) (msgPrefix string, safe []string, details proto.Message) {
|
||||
p := err.(*os.LinkError)
|
||||
msg := p.Op + " " + p.Old + " " + p.New
|
||||
details = &errorspb.StringsPayload{
|
||||
Details: []string{p.Op, p.Old, p.New},
|
||||
}
|
||||
return msg, []string{p.Op}, details
|
||||
}
|
||||
|
||||
func decodeLinkError(
|
||||
_ context.Context, cause error, _ string, _ []string, payload proto.Message,
|
||||
) (result error) {
|
||||
m, ok := payload.(*errorspb.StringsPayload)
|
||||
if !ok || len(m.Details) < 3 {
|
||||
// If this ever happens, this means some version of the library
|
||||
// (presumably future) changed the payload type, and we're
|
||||
// receiving this here. In this case, give up and let
|
||||
// DecodeError use the opaque type.
|
||||
return nil
|
||||
}
|
||||
return &os.LinkError{
|
||||
Op: m.Details[0],
|
||||
Old: m.Details[1],
|
||||
New: m.Details[2],
|
||||
Err: cause,
|
||||
}
|
||||
}
|
||||
|
||||
func encodeSyscallError(
|
||||
_ context.Context, err error,
|
||||
) (msgPrefix string, safe []string, details proto.Message) {
|
||||
p := err.(*os.SyscallError)
|
||||
return p.Syscall, nil, nil
|
||||
}
|
||||
|
||||
func decodeSyscallError(
|
||||
_ context.Context, cause error, msg string, _ []string, _ proto.Message,
|
||||
) (result error) {
|
||||
return os.NewSyscallError(msg, cause)
|
||||
}
|
||||
|
||||
// OpaqueErrno represents a syscall.Errno error object that
|
||||
// was constructed on a different OS/platform combination.
|
||||
type OpaqueErrno struct {
|
||||
msg string
|
||||
details *errorspb.ErrnoPayload
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (o *OpaqueErrno) Error() string { return o.msg }
|
||||
|
||||
// Is tests whether this opaque errno object represents a special os error type.
|
||||
func (o *OpaqueErrno) Is(target error) bool {
|
||||
return (target == os.ErrPermission && o.details.IsPermission) ||
|
||||
(target == os.ErrExist && o.details.IsExist) ||
|
||||
(target == os.ErrNotExist && o.details.IsNotExist)
|
||||
}
|
||||
|
||||
// Temporary tests whether this opaque errno object encodes a temporary error.
|
||||
func (o *OpaqueErrno) Temporary() bool { return o.details.IsTemporary }
|
||||
|
||||
// Timeout tests whether this opaque errno object encodes a timeout error.
|
||||
func (o *OpaqueErrno) Timeout() bool { return o.details.IsTimeout }
|
||||
|
||||
func encodeOpaqueErrno(
|
||||
_ context.Context, err error,
|
||||
) (msg string, safe []string, payload proto.Message) {
|
||||
e := err.(*OpaqueErrno)
|
||||
return e.Error(), []string{e.Error()}, e.details
|
||||
}
|
||||
|
||||
func init() {
|
||||
baseErr := goErr.New("")
|
||||
RegisterLeafDecoder(GetTypeKey(baseErr), decodeErrorString)
|
||||
|
||||
RegisterLeafDecoder(GetTypeKey(context.DeadlineExceeded), decodeDeadlineExceeded)
|
||||
|
||||
pkgE := pkgErr.New("")
|
||||
RegisterLeafEncoder(GetTypeKey(pkgE), encodePkgFundamental)
|
||||
|
||||
RegisterWrapperDecoder(GetTypeKey(pkgErr.WithMessage(baseErr, "")), decodeWithMessage)
|
||||
|
||||
ws := pkgErr.WithStack(baseErr)
|
||||
RegisterWrapperEncoder(GetTypeKey(ws), encodePkgWithStack)
|
||||
|
||||
registerOsPathErrorMigration() // Needed for Go 1.16.
|
||||
pKey := GetTypeKey(&os.PathError{})
|
||||
RegisterWrapperEncoder(pKey, encodePathError)
|
||||
RegisterWrapperDecoder(pKey, decodePathError)
|
||||
|
||||
pKey = GetTypeKey(&os.LinkError{})
|
||||
RegisterWrapperEncoder(pKey, encodeLinkError)
|
||||
RegisterWrapperDecoder(pKey, decodeLinkError)
|
||||
pKey = GetTypeKey(&os.SyscallError{})
|
||||
RegisterWrapperEncoder(pKey, encodeSyscallError)
|
||||
RegisterWrapperDecoder(pKey, decodeSyscallError)
|
||||
|
||||
RegisterLeafEncoder(GetTypeKey(&OpaqueErrno{}), encodeOpaqueErrno)
|
||||
}
|
||||
66
vendor/github.com/cockroachdb/errors/errbase/adapters_errno.go
generated
vendored
Normal file
66
vendor/github.com/cockroachdb/errors/errbase/adapters_errno.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
// +build !plan9
|
||||
|
||||
package errbase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/cockroachdb/errors/errorspb"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
)
|
||||
|
||||
const thisArch = runtime.GOOS + ":" + runtime.GOARCH
|
||||
|
||||
func encodeErrno(_ context.Context, err error) (msg string, safe []string, payload proto.Message) {
|
||||
e := err.(syscall.Errno)
|
||||
payload = &errorspb.ErrnoPayload{
|
||||
OrigErrno: int64(e),
|
||||
Arch: thisArch,
|
||||
IsPermission: e.Is(os.ErrPermission),
|
||||
IsExist: e.Is(os.ErrExist),
|
||||
IsNotExist: e.Is(os.ErrNotExist),
|
||||
IsTimeout: e.Timeout(),
|
||||
IsTemporary: e.Temporary(),
|
||||
}
|
||||
return e.Error(), []string{e.Error()}, payload
|
||||
}
|
||||
|
||||
func decodeErrno(_ context.Context, msg string, _ []string, payload proto.Message) error {
|
||||
m, ok := payload.(*errorspb.ErrnoPayload)
|
||||
if !ok {
|
||||
// If this ever happens, this means some version of the library
|
||||
// (presumably future) changed the payload type, and we're
|
||||
// receiving this here. In this case, give up and let
|
||||
// DecodeError use the opaque type.
|
||||
return nil
|
||||
}
|
||||
if m.Arch != thisArch {
|
||||
// The errno object is coming from a different platform. We'll
|
||||
// keep it opaque here.
|
||||
return &OpaqueErrno{msg: msg, details: m}
|
||||
}
|
||||
return syscall.Errno(m.OrigErrno)
|
||||
}
|
||||
|
||||
func init() {
|
||||
pKey := GetTypeKey(syscall.Errno(0))
|
||||
RegisterLeafEncoder(pKey, encodeErrno)
|
||||
RegisterLeafDecoder(pKey, decodeErrno)
|
||||
}
|
||||
206
vendor/github.com/cockroachdb/errors/errbase/decode.go
generated
vendored
Normal file
206
vendor/github.com/cockroachdb/errors/errbase/decode.go
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
// 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 (
|
||||
"context"
|
||||
|
||||
"github.com/cockroachdb/errors/errorspb"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/gogo/protobuf/types"
|
||||
)
|
||||
|
||||
// DecodeError decodes an error.
|
||||
//
|
||||
// Can only be called if the EncodedError is set (see IsSet()).
|
||||
func DecodeError(ctx context.Context, enc EncodedError) error {
|
||||
if w := enc.GetWrapper(); w != nil {
|
||||
return decodeWrapper(ctx, w)
|
||||
}
|
||||
return decodeLeaf(ctx, enc.GetLeaf())
|
||||
}
|
||||
|
||||
func decodeLeaf(ctx context.Context, enc *errorspb.EncodedErrorLeaf) error {
|
||||
// In case there is a detailed payload, decode it.
|
||||
var payload proto.Message
|
||||
if enc.Details.FullDetails != nil {
|
||||
var d types.DynamicAny
|
||||
err := types.UnmarshalAny(enc.Details.FullDetails, &d)
|
||||
if err != nil {
|
||||
// It's OK if we can't decode. We'll use
|
||||
// the opaque type below.
|
||||
warningFn(ctx, "error while unmarshalling error: %+v", err)
|
||||
} else {
|
||||
payload = d.Message
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a leaf decoder for this type?
|
||||
typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName)
|
||||
if decoder, ok := leafDecoders[typeKey]; ok {
|
||||
// Yes, use it.
|
||||
genErr := decoder(ctx, enc.Message, enc.Details.ReportablePayload, payload)
|
||||
if genErr != nil {
|
||||
// Decoding succeeded. Use this.
|
||||
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.
|
||||
if e, ok := payload.(error); ok {
|
||||
// yes: we're done!
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
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).
|
||||
return &opaqueLeaf{
|
||||
msg: enc.Message,
|
||||
details: enc.Details,
|
||||
}
|
||||
}
|
||||
|
||||
func decodeWrapper(ctx context.Context, enc *errorspb.EncodedWrapper) error {
|
||||
// First decode the cause.
|
||||
cause := DecodeError(ctx, enc.Cause)
|
||||
|
||||
// In case there is a detailed payload, decode it.
|
||||
var payload proto.Message
|
||||
if enc.Details.FullDetails != nil {
|
||||
var d types.DynamicAny
|
||||
err := types.UnmarshalAny(enc.Details.FullDetails, &d)
|
||||
if err != nil {
|
||||
// It's OK if we can't decode. We'll use
|
||||
// the opaque type below.
|
||||
warningFn(ctx, "error while unmarshalling wrapper error: %+v", err)
|
||||
} else {
|
||||
payload = d.Message
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a wrapper decoder for this?
|
||||
typeKey := TypeKey(enc.Details.ErrorTypeMark.FamilyName)
|
||||
if decoder, ok := decoders[typeKey]; ok {
|
||||
// Yes, use it.
|
||||
genErr := decoder(ctx, cause, enc.Message, enc.Details.ReportablePayload, payload)
|
||||
if genErr != nil {
|
||||
// Decoding succeeded. Use this.
|
||||
return genErr
|
||||
}
|
||||
// Decoding failed, we'll drop through to opaqueWrapper{} below.
|
||||
}
|
||||
|
||||
// Otherwise, preserve all details about the original object.
|
||||
return &opaqueWrapper{
|
||||
cause: cause,
|
||||
prefix: enc.Message,
|
||||
details: enc.Details,
|
||||
messageType: MessageType(enc.MessageType),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterLeafDecoder can be used to register new leaf error types to
|
||||
// the library. Registered types will be decoded using their own
|
||||
// Go type when an error is decoded. Wrappers that have not been
|
||||
// registered will be decoded using the opaqueLeaf type.
|
||||
//
|
||||
// Note: if the error type has been migrated from a previous location
|
||||
// or a different type, ensure that RegisterTypeMigration() was called
|
||||
// prior to RegisterLeafDecoder().
|
||||
func RegisterLeafDecoder(theType TypeKey, decoder LeafDecoder) {
|
||||
if decoder == nil {
|
||||
delete(leafDecoders, theType)
|
||||
} else {
|
||||
leafDecoders[theType] = decoder
|
||||
}
|
||||
}
|
||||
|
||||
// LeafDecoder is to be provided (via RegisterLeafDecoder above)
|
||||
// by additional wrapper types not yet known to this library.
|
||||
// A nil return indicates that decoding was not successful.
|
||||
type LeafDecoder = func(ctx context.Context, msg string, safeDetails []string, payload proto.Message) error
|
||||
|
||||
// registry for RegisterLeafDecoder.
|
||||
var leafDecoders = map[TypeKey]LeafDecoder{}
|
||||
|
||||
// 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
|
||||
// registered will be decoded 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 RegisterWrapperDecoder().
|
||||
func RegisterWrapperDecoder(theType TypeKey, decoder WrapperDecoder) {
|
||||
if decoder == nil {
|
||||
delete(decoders, theType)
|
||||
} else {
|
||||
decoders[theType] = decoder
|
||||
}
|
||||
}
|
||||
|
||||
// WrapperDecoder is to be provided (via RegisterWrapperDecoder above)
|
||||
// by additional wrapper types not yet known to this library.
|
||||
// A nil return indicates that decoding was not successful.
|
||||
type WrapperDecoder = func(ctx context.Context, cause error, msgPrefix string, safeDetails []string, payload proto.Message) error
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
438
vendor/github.com/cockroachdb/errors/errbase/encode.go
generated
vendored
Normal file
438
vendor/github.com/cockroachdb/errors/errbase/encode.go
generated
vendored
Normal file
@@ -0,0 +1,438 @@
|
||||
// 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 (
|
||||
"context"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors/errorspb"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/gogo/protobuf/types"
|
||||
)
|
||||
|
||||
// EncodedError is the type of an encoded (and protobuf-encodable) error.
|
||||
type EncodedError = errorspb.EncodedError
|
||||
|
||||
// EncodeError encodes an error.
|
||||
func EncodeError(ctx context.Context, err error) EncodedError {
|
||||
if cause := UnwrapOnce(err); cause != nil {
|
||||
return encodeWrapper(ctx, err, cause)
|
||||
}
|
||||
return encodeLeaf(ctx, err, UnwrapMulti(err))
|
||||
}
|
||||
|
||||
// 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*/)
|
||||
|
||||
var payload proto.Message
|
||||
|
||||
// If we have a manually registered encoder, use that.
|
||||
typeKey := TypeKey(details.ErrorTypeMark.FamilyName)
|
||||
if enc, ok := leafEncoders[typeKey]; ok {
|
||||
msg, details.ReportablePayload, payload = enc(ctx, err)
|
||||
} else {
|
||||
// No encoder. Let's try to manually extract fields.
|
||||
|
||||
// The message comes from Error(). Simple.
|
||||
msg = err.Error()
|
||||
|
||||
// If there are known safe details, use them.
|
||||
if s, ok := err.(SafeDetailer); ok {
|
||||
details.ReportablePayload = s.SafeDetails()
|
||||
}
|
||||
|
||||
// If it's also a protobuf message, we'll use that as
|
||||
// payload. DecodeLeaf() will know how to turn that back into a
|
||||
// full error if there is no decoder.
|
||||
payload, _ = err.(proto.Message)
|
||||
}
|
||||
// If there is a detail payload, encode it.
|
||||
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,
|
||||
MultierrorCauses: cs,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// warningFn can be overridden with a suitable logging function using
|
||||
// SetWarningFn() below.
|
||||
var warningFn = func(_ context.Context, format string, args ...interface{}) {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
|
||||
// SetWarningFn enables configuration of the warning function.
|
||||
func SetWarningFn(fn func(context.Context, string, ...interface{})) {
|
||||
warningFn = fn
|
||||
}
|
||||
|
||||
func encodeAsAny(ctx context.Context, err error, payload proto.Message) *types.Any {
|
||||
if payload == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
any, marshalErr := types.MarshalAny(payload)
|
||||
if marshalErr != nil {
|
||||
warningFn(ctx,
|
||||
"error %+v (%T) announces proto message, but marshaling fails: %+v",
|
||||
err, err, marshalErr)
|
||||
return nil
|
||||
}
|
||||
|
||||
return any
|
||||
}
|
||||
|
||||
// encodeWrapper encodes an error wrapper.
|
||||
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*/)
|
||||
|
||||
var payload proto.Message
|
||||
|
||||
// If we have a manually registered encoder, use that.
|
||||
typeKey := TypeKey(details.ErrorTypeMark.FamilyName)
|
||||
if enc, ok := encoders[typeKey]; ok {
|
||||
msg, details.ReportablePayload, payload, messageType = enc(ctx, err)
|
||||
} else {
|
||||
// No encoder.
|
||||
// In that case, we'll try to compute a message prefix
|
||||
// manually.
|
||||
msg, messageType = extractPrefix(err, cause)
|
||||
|
||||
// If there are known safe details, use them.
|
||||
if s, ok := err.(SafeDetailer); ok {
|
||||
details.ReportablePayload = s.SafeDetails()
|
||||
}
|
||||
|
||||
// That's all we can get.
|
||||
}
|
||||
// If there is a detail payload, encode it.
|
||||
details.FullDetails = encodeAsAny(ctx, err, payload)
|
||||
}
|
||||
|
||||
return EncodedError{
|
||||
Error: &errorspb.EncodedError_Wrapper{
|
||||
Wrapper: &errorspb.EncodedWrapper{
|
||||
Cause: EncodeError(ctx, cause),
|
||||
Message: msg,
|
||||
Details: details,
|
||||
MessageType: errorspb.MessageType(messageType),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// extractPrefix extracts the prefix from a wrapper's error message.
|
||||
// For example,
|
||||
//
|
||||
// err := errors.New("bar")
|
||||
// err = errors.Wrap(err, "foo")
|
||||
// extractPrefix(err)
|
||||
//
|
||||
// returns "foo".
|
||||
//
|
||||
// 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], Prefix
|
||||
}
|
||||
}
|
||||
// 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(
|
||||
err error, onlyFamily bool,
|
||||
) (origTypeName string, typeKeyFamily string, typeKeyExtension string) {
|
||||
// If we have received an error of type not known locally,
|
||||
// we still know its type name. Return that.
|
||||
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
|
||||
}
|
||||
|
||||
// Compute the full error name, for reporting and printing details.
|
||||
tn := getFullTypeName(err)
|
||||
// Compute a family name, used to find decoders and to compare error identities.
|
||||
fm := tn
|
||||
if prevKey, ok := backwardRegistry[TypeKey(tn)]; ok {
|
||||
fm = string(prevKey)
|
||||
}
|
||||
|
||||
if onlyFamily {
|
||||
return tn, fm, ""
|
||||
}
|
||||
|
||||
// If the error has an extra type marker, add it.
|
||||
// This is not used by the base functionality but
|
||||
// is hooked into by the barrier subsystem.
|
||||
var em string
|
||||
if tm, ok := err.(TypeKeyMarker); ok {
|
||||
em = tm.ErrorKeyMarker()
|
||||
}
|
||||
return tn, fm, em
|
||||
}
|
||||
|
||||
// TypeKeyMarker can be implemented by errors that wish to extend
|
||||
// their type name as seen by GetTypeKey().
|
||||
//
|
||||
// Note: the key marker is considered safe for reporting and
|
||||
// is included in sentry reports.
|
||||
type TypeKeyMarker interface {
|
||||
ErrorKeyMarker() string
|
||||
}
|
||||
|
||||
func getFullTypeName(err error) string {
|
||||
t := reflect.TypeOf(err)
|
||||
pkgPath := getPkgPath(t)
|
||||
return makeTypeKey(pkgPath, t.String())
|
||||
}
|
||||
|
||||
func makeTypeKey(pkgPath, typeNameString string) string {
|
||||
return pkgPath + "/" + typeNameString
|
||||
}
|
||||
|
||||
// getPkgPath extract the package path for a Go type. We'll do some
|
||||
// extra work for typical types that did not get a name, for example
|
||||
// *E has the package path of E.
|
||||
func getPkgPath(t reflect.Type) string {
|
||||
pkgPath := t.PkgPath()
|
||||
if pkgPath != "" {
|
||||
return pkgPath
|
||||
}
|
||||
// Try harder.
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
return getPkgPath(t.Elem())
|
||||
}
|
||||
// Nothing to report.
|
||||
return ""
|
||||
}
|
||||
|
||||
// TypeKey identifies an error for the purpose of looking up decoders.
|
||||
// It is equivalent to the "family name" in ErrorTypeMarker.
|
||||
type TypeKey string
|
||||
|
||||
// GetTypeKey retrieve the type key for a given error object. This
|
||||
// is meant for use in combination with the Register functions.
|
||||
func GetTypeKey(err error) TypeKey {
|
||||
_, familyName, _ := getTypeDetails(err, true /*onlyFamily*/)
|
||||
return TypeKey(familyName)
|
||||
}
|
||||
|
||||
// GetTypeMark retrieves the ErrorTypeMark for a given error object.
|
||||
// This is meant for use in the markers sub-package.
|
||||
func GetTypeMark(err error) errorspb.ErrorTypeMark {
|
||||
_, familyName, extension := getTypeDetails(err, false /*onlyFamily*/)
|
||||
return errorspb.ErrorTypeMark{FamilyName: familyName, Extension: extension}
|
||||
}
|
||||
|
||||
// RegisterLeafEncoder can be used to register new leaf error types to
|
||||
// the library. Registered types will be encoded using their own
|
||||
// Go type when an error is encoded. Wrappers that have not been
|
||||
// registered will be encoded using the opaqueLeaf type.
|
||||
//
|
||||
// Note: if the error type has been migrated from a previous location
|
||||
// or a different type, ensure that RegisterTypeMigration() was called
|
||||
// prior to RegisterLeafEncoder().
|
||||
func RegisterLeafEncoder(theType TypeKey, encoder LeafEncoder) {
|
||||
if encoder == nil {
|
||||
delete(leafEncoders, theType)
|
||||
} else {
|
||||
leafEncoders[theType] = encoder
|
||||
}
|
||||
}
|
||||
|
||||
// LeafEncoder is to be provided (via RegisterLeafEncoder above)
|
||||
// by additional wrapper types not yet known to this library.
|
||||
type LeafEncoder = func(ctx context.Context, err error) (msg string, safeDetails []string, payload proto.Message)
|
||||
|
||||
// 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
|
||||
// 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 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 {
|
||||
encoders[theType] = encoder
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
)
|
||||
|
||||
// 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]WrapperEncoderWithMessageType{}
|
||||
990
vendor/github.com/cockroachdb/errors/errbase/format_error.go
generated
vendored
Normal file
990
vendor/github.com/cockroachdb/errors/errbase/format_error.go
generated
vendored
Normal file
@@ -0,0 +1,990 @@
|
||||
// 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.
|
||||
|
||||
// This file is forked and modified from golang.org/x/xerrors,
|
||||
// at commit 3ee3066db522c6628d440a3a91c4abdd7f5ef22f (2019-05-10).
|
||||
// From the original code:
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// Changes specific to this fork marked as inline comments.
|
||||
|
||||
package errbase
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/redact"
|
||||
"github.com/kr/pretty"
|
||||
pkgErr "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// FormatError formats an error according to s and verb.
|
||||
// This is a helper meant for use when implementing the fmt.Formatter
|
||||
// interface on custom error objects.
|
||||
//
|
||||
// If the error implements errors.Formatter, FormatError calls its
|
||||
// FormatError method of f with an errors.Printer configured according
|
||||
// to s and verb, and writes the result to s.
|
||||
//
|
||||
// Otherwise, if it is a wrapper, FormatError prints out its error prefix,
|
||||
// then recurses on its cause.
|
||||
//
|
||||
// Otherwise, its Error() text is printed.
|
||||
func FormatError(err error, s fmt.State, verb rune) {
|
||||
formatErrorInternal(err, s, verb, false /* redactableOutput */)
|
||||
}
|
||||
|
||||
// FormatRedactableError formats an error as a safe object.
|
||||
//
|
||||
// Note that certain verb/flags combinations are currently not
|
||||
// supported, and result in a rendering that considers the entire
|
||||
// object as unsafe. For example, %q, %#v are not yet supported.
|
||||
func FormatRedactableError(err error, s redact.SafePrinter, verb rune) {
|
||||
formatErrorInternal(err, s, verb, true /* redactable */)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Also inform the redact package of how to print an error
|
||||
// safely. This is used when an error is passed as argument
|
||||
// to one of the redact print functions.
|
||||
redact.RegisterRedactErrorFn(FormatRedactableError)
|
||||
}
|
||||
|
||||
// Formattable wraps an error into a fmt.Formatter which
|
||||
// will provide "smart" formatting even if the outer layer
|
||||
// of the error does not implement the Formatter interface.
|
||||
func Formattable(err error) fmt.Formatter {
|
||||
return &errorFormatter{err}
|
||||
}
|
||||
|
||||
// formatErrorInternal is the shared logic between FormatError
|
||||
// and FormatErrorRedactable.
|
||||
//
|
||||
// When the redactableOutput argument is true, the fmt.State argument
|
||||
// is really a redact.SafePrinter and casted down as necessary.
|
||||
//
|
||||
// If verb and flags are not one of the supported error formatting
|
||||
// combinations (in particular, %q, %#v etc), then the redactableOutput
|
||||
// argument is ignored. This limitation may be lifted in a later
|
||||
// version.
|
||||
func formatErrorInternal(err error, s fmt.State, verb rune, redactableOutput bool) {
|
||||
// Assuming this function is only called from the Format method, and given
|
||||
// that FormatError takes precedence over Format, it cannot be called from
|
||||
// any package that supports errors.Formatter. It is therefore safe to
|
||||
// disregard that State may be a specific printer implementation and use one
|
||||
// of our choice instead.
|
||||
|
||||
p := state{State: s, redactableOutput: redactableOutput}
|
||||
|
||||
switch {
|
||||
case verb == 'v' && s.Flag('+') && !s.Flag('#'):
|
||||
// Here we are going to format as per %+v, into p.buf.
|
||||
//
|
||||
// We need to start with the innermost (root cause) error first,
|
||||
// then the layers of wrapping from innermost to outermost, so as
|
||||
// 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 */
|
||||
false, /* withDepth */
|
||||
0, /* depth */
|
||||
)
|
||||
|
||||
// We now have all the data, we can render the result.
|
||||
p.formatEntries(err)
|
||||
|
||||
// We're done formatting. Apply width/precision parameters.
|
||||
p.finishDisplay(verb)
|
||||
|
||||
case !redactableOutput && verb == 'v' && s.Flag('#'):
|
||||
// We only know how to process %#v if redactable output is not
|
||||
// requested. This is because the structured output may emit
|
||||
// arbitrary unsafe strings without redaction markers,
|
||||
// or improperly balanced/escaped redaction markers.
|
||||
if stringer, ok := err.(fmt.GoStringer); ok {
|
||||
io.WriteString(&p.finalBuf, stringer.GoString())
|
||||
} else {
|
||||
// Not a GoStringer: delegate to the pretty library.
|
||||
fmt.Fprintf(&p.finalBuf, "%# v", pretty.Formatter(err))
|
||||
}
|
||||
p.finishDisplay(verb)
|
||||
|
||||
case verb == 's' ||
|
||||
// We only handle %v/%+v or other combinations here; %#v is unsupported.
|
||||
(verb == 'v' && !s.Flag('#')) ||
|
||||
// If redactable output is not requested, then we also
|
||||
// know how to format %x/%X (print bytes of error message in hex)
|
||||
// and %q (quote the result).
|
||||
// If redactable output is requested, then we don't know
|
||||
// how to perform these exotic verbs, because they
|
||||
// may muck with the redaction markers. In this case,
|
||||
// we simply refuse the format as per the default clause below.
|
||||
(!redactableOutput && (verb == 'x' || verb == 'X' || verb == 'q')):
|
||||
// Only the error message.
|
||||
//
|
||||
// Use an intermediate buffer because there may be alignment
|
||||
// instructions to obey in the final rendering or
|
||||
// quotes to add (for %q).
|
||||
//
|
||||
// Conceptually, we could just do
|
||||
// p.buf.WriteString(err.Error())
|
||||
// However we also advertise that Error() can be implemented
|
||||
// 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 */
|
||||
false, /* withDepth */
|
||||
0, /* depth */
|
||||
)
|
||||
p.formatSingleLineOutput()
|
||||
p.finishDisplay(verb)
|
||||
|
||||
default:
|
||||
// Unknown verb. Do like fmt.Printf and tell the user we're
|
||||
// confused.
|
||||
//
|
||||
// Note that the following logic is correct regardless of the
|
||||
// value of 'redactableOutput', because the display of the verb and type
|
||||
// are always safe for redaction. If/when this code is changed to
|
||||
// print more details, care is to be taken to add redaction
|
||||
// markers if s.redactableOutput is set.
|
||||
p.finalBuf.WriteString("%!")
|
||||
p.finalBuf.WriteRune(verb)
|
||||
p.finalBuf.WriteByte('(')
|
||||
switch {
|
||||
case err != nil:
|
||||
p.finalBuf.WriteString(reflect.TypeOf(err).String())
|
||||
default:
|
||||
p.finalBuf.WriteString("<nil>")
|
||||
}
|
||||
p.finalBuf.WriteByte(')')
|
||||
io.Copy(s, &p.finalBuf)
|
||||
}
|
||||
}
|
||||
|
||||
// formatEntries reads the entries from s.entries and produces a
|
||||
// detailed rendering in s.finalBuf.
|
||||
//
|
||||
// Note that if s.redactableOutput is true, s.finalBuf is to contain a
|
||||
// RedactableBytes. However, we are not using the helper facilities
|
||||
// from redact.SafePrinter to do this, so care should be taken below
|
||||
// to properly escape markers, etc.
|
||||
func (s *state) formatEntries(err error) {
|
||||
// The first entry at the top is special. We format it as follows:
|
||||
//
|
||||
// <complete error message>
|
||||
// (1) <details>
|
||||
s.formatSingleLineOutput()
|
||||
s.finalBuf.WriteString("\n(1)")
|
||||
|
||||
s.printEntry(s.entries[len(s.entries)-1])
|
||||
|
||||
// All the entries that follow are printed as follows:
|
||||
//
|
||||
// Wraps: (N) <details>
|
||||
//
|
||||
for i, j := len(s.entries)-2, 2; i >= 0; i, j = i-1, j+1 {
|
||||
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)
|
||||
}
|
||||
|
||||
// At the end, we link all the (N) references to the Go type of the
|
||||
// error.
|
||||
s.finalBuf.WriteString("\nError types:")
|
||||
for i, j := len(s.entries)-1, 1; i >= 0; i, j = i-1, j+1 {
|
||||
fmt.Fprintf(&s.finalBuf, " (%d) %T", j, s.entries[i].err)
|
||||
}
|
||||
}
|
||||
|
||||
// printEntry renders the entry given as argument
|
||||
// into s.finalBuf.
|
||||
//
|
||||
// If s.redactableOutput is set, then s.finalBuf is to contain
|
||||
// a RedactableBytes, with redaction markers. In that
|
||||
// case, we must be careful to escape (or not) the entry
|
||||
// depending on entry.redactable.
|
||||
//
|
||||
// If s.redactableOutput is unset, then we are not caring about
|
||||
// redactability. In that case entry.redactable is not set
|
||||
// anyway and we can pass contents through.
|
||||
func (s *state) printEntry(entry formatEntry) {
|
||||
if len(entry.head) > 0 {
|
||||
if entry.head[0] != '\n' {
|
||||
s.finalBuf.WriteByte(' ')
|
||||
}
|
||||
if len(entry.head) > 0 {
|
||||
if !s.redactableOutput || entry.redactable {
|
||||
// If we don't care about redaction, then we can pass the string
|
||||
// through.
|
||||
//
|
||||
// If we do care about redaction, and entry.redactable is true,
|
||||
// then entry.head is already a RedactableBytes. Then we can
|
||||
// also pass it through.
|
||||
s.finalBuf.Write(entry.head)
|
||||
} else {
|
||||
// We care about redaction, and the head is unsafe. Escape it
|
||||
// and enclose the result within redaction markers.
|
||||
s.finalBuf.Write([]byte(redact.EscapeBytes(entry.head)))
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(entry.details) > 0 {
|
||||
if len(entry.head) == 0 {
|
||||
if entry.details[0] != '\n' {
|
||||
s.finalBuf.WriteByte(' ')
|
||||
}
|
||||
}
|
||||
if !s.redactableOutput || entry.redactable {
|
||||
// If we don't care about redaction, then we can pass the string
|
||||
// through.
|
||||
//
|
||||
// If we do care about redaction, and entry.redactable is true,
|
||||
// then entry.details is already a RedactableBytes. Then we can
|
||||
// also pass it through.
|
||||
s.finalBuf.Write(entry.details)
|
||||
} else {
|
||||
// We care about redaction, and the details are unsafe. Escape
|
||||
// them and enclose the result within redaction markers.
|
||||
s.finalBuf.Write([]byte(redact.EscapeBytes(entry.details)))
|
||||
}
|
||||
}
|
||||
if entry.stackTrace != nil {
|
||||
s.finalBuf.WriteString("\n -- stack trace:")
|
||||
s.finalBuf.WriteString(strings.ReplaceAll(
|
||||
fmt.Sprintf("%+v", entry.stackTrace),
|
||||
"\n", string(detailSep)))
|
||||
if entry.elidedStackTrace {
|
||||
fmt.Fprintf(&s.finalBuf, "%s[...repeated from below...]", detailSep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// formatSingleLineOutput prints the details extracted via
|
||||
// formatRecursive() through the chain of errors as if .Error() has
|
||||
// been called: it only prints the non-detail parts and prints them on
|
||||
// one line with ": " separators.
|
||||
//
|
||||
// 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) }
|
||||
//
|
||||
// and also to print the first line in the output of a %+v format.
|
||||
//
|
||||
// It reads from s.entries and writes to s.finalBuf.
|
||||
// s.buf is left untouched.
|
||||
//
|
||||
// Note that if s.redactableOutput is true, s.finalBuf is to contain a
|
||||
// RedactableBytes. However, we are not using the helper facilities
|
||||
// from redact.SafePrinter to do this, so care should be taken below
|
||||
// to properly escape markers, etc.
|
||||
func (s *state) formatSingleLineOutput() {
|
||||
for i := len(s.entries) - 1; i >= 0; i-- {
|
||||
entry := &s.entries[i]
|
||||
if entry.elideShort {
|
||||
continue
|
||||
}
|
||||
if s.finalBuf.Len() > 0 && len(entry.head) > 0 {
|
||||
s.finalBuf.WriteString(": ")
|
||||
}
|
||||
if len(entry.head) == 0 {
|
||||
// shortcut, to avoid the copy below.
|
||||
continue
|
||||
}
|
||||
if !s.redactableOutput || entry.redactable {
|
||||
// If we don't care about redaction, then we can pass the string
|
||||
// through.
|
||||
//
|
||||
// If we do care about redaction, and entry.redactable is true,
|
||||
// then entry.head is already a RedactableBytes. Then we can
|
||||
// also pass it through.
|
||||
s.finalBuf.Write(entry.head)
|
||||
} else {
|
||||
// We do care about redaction, but entry.redactable is unset.
|
||||
// This means entry.head is unsafe. We need to escape it.
|
||||
s.finalBuf.Write([]byte(redact.EscapeBytes(entry.head)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// formatRecursive performs a post-order traversal on the chain of
|
||||
// errors to collect error details from innermost to outermost.
|
||||
//
|
||||
// It uses s.buf as an intermediate buffer to collect strings.
|
||||
// It populates s.entries as a result.
|
||||
// Between each layer of error, s.buf is reset.
|
||||
//
|
||||
// s.finalBuf is untouched. The conversion of s.entries
|
||||
// to s.finalBuf is done by formatSingleLineOutput() and/or
|
||||
// formatEntries().
|
||||
//
|
||||
// `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, 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
|
||||
s.needNewline = 0
|
||||
s.multiLine = false
|
||||
s.notEmpty = false
|
||||
s.hasDetail = false
|
||||
s.headBuf = nil
|
||||
|
||||
seenTrace := false
|
||||
|
||||
bufIsRedactable := false
|
||||
|
||||
switch v := err.(type) {
|
||||
case SafeFormatter:
|
||||
bufIsRedactable = true
|
||||
desiredShortening := v.SafeFormatError((*safePrinter)(s))
|
||||
if desiredShortening == nil {
|
||||
// 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:
|
||||
desiredShortening := v.FormatError((*printer)(s))
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
s.elideShortChildren(numChildren)
|
||||
}
|
||||
|
||||
case fmt.Formatter:
|
||||
// We can only use a fmt.Formatter when both the following
|
||||
// conditions are true:
|
||||
// - when it is the leaf error, because a fmt.Formatter
|
||||
// on a wrapper also recurses.
|
||||
// - when it is not the outermost wrapper, because
|
||||
// the Format() method is likely to be calling FormatError()
|
||||
// to do its job and we want to avoid an infinite recursion.
|
||||
if !isOutermost && cause == nil {
|
||||
v.Format(s, 'v')
|
||||
if st, ok := err.(StackTraceProvider); ok {
|
||||
// This is likely a leaf error from github/pkg/errors.
|
||||
// The thing probably printed its stack trace on its own.
|
||||
seenTrace = true
|
||||
// We'll subsequently simplify stack traces in wrappers.
|
||||
s.lastStack = st.StackTrace()
|
||||
}
|
||||
} else {
|
||||
if elideCauseMsg := s.formatSimple(err, cause); elideCauseMsg {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
s.elideShortChildren(numChildren)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// Handle the special case overrides for context.Canceled,
|
||||
// os.PathError, etc for which we know how to extract some safe
|
||||
// strings.
|
||||
//
|
||||
// We need to do this in the `default` branch, instead of doing
|
||||
// this above the switch, because the special handler could call a
|
||||
// .Error() that delegates its implementation to fmt.Formatter,
|
||||
// errors.Safeformatter or errors.Formattable, which brings us
|
||||
// back to this method in a call cycle. So we need to handle the
|
||||
// various interfaces first.
|
||||
printDone := false
|
||||
for _, fn := range specialCases {
|
||||
if handled, desiredShortening := fn(err, (*safePrinter)(s), cause == nil /* leaf */); handled {
|
||||
printDone = true
|
||||
bufIsRedactable = true
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
s.elideShortChildren(numChildren)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !printDone {
|
||||
// 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.
|
||||
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, withDepth, depth)
|
||||
|
||||
// If there's an embedded stack trace, also collect it.
|
||||
// This will get either a stack from pkg/errors, or ours.
|
||||
if !seenTrace {
|
||||
if st, ok := err.(StackTraceProvider); ok {
|
||||
entry.stackTrace, entry.elidedStackTrace = ElideSharedStackTraceSuffix(s.lastStack, st.StackTrace())
|
||||
s.lastStack = entry.stackTrace
|
||||
}
|
||||
}
|
||||
|
||||
// Remember the entry for later rendering.
|
||||
s.entries = append(s.entries, entry)
|
||||
s.buf = bytes.Buffer{}
|
||||
|
||||
return numChildren + 1
|
||||
}
|
||||
|
||||
// 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
|
||||
// %+v. In that case, if the printer has separated detail
|
||||
// from non-detail, we can use the split.
|
||||
if s.hasDetail {
|
||||
entry.head = s.headBuf
|
||||
entry.details = s.buf.Bytes()
|
||||
} else {
|
||||
entry.head = s.buf.Bytes()
|
||||
}
|
||||
} else {
|
||||
entry.head = s.headBuf
|
||||
if len(entry.head) > 0 && entry.head[len(entry.head)-1] != '\n' &&
|
||||
s.buf.Len() > 0 && s.buf.Bytes()[0] != '\n' {
|
||||
entry.head = append(entry.head, '\n')
|
||||
}
|
||||
entry.head = append(entry.head, s.buf.Bytes()...)
|
||||
}
|
||||
|
||||
if bufIsRedactable {
|
||||
// In this case, we've produced entry.head/entry.details using a
|
||||
// SafeFormatError() invocation. The strings in
|
||||
// entry.head/entry.detail contain redaction markers at this
|
||||
// point.
|
||||
if s.redactableOutput {
|
||||
// Redaction markers desired in the final output. Keep the
|
||||
// redaction markers.
|
||||
entry.redactable = true
|
||||
} else {
|
||||
// Markers not desired in the final output: strip the markers.
|
||||
entry.head = redact.RedactableBytes(entry.head).StripMarkers()
|
||||
entry.details = redact.RedactableBytes(entry.details).StripMarkers()
|
||||
}
|
||||
}
|
||||
|
||||
if withDepth {
|
||||
entry.depth = depth
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
// safeErrorPrinterFn is the type of a function that can take
|
||||
// over the safe printing of an error. This is used to inject special
|
||||
// cases into the formatting in errutil. We need this machinery to
|
||||
// prevent import cycles.
|
||||
type safeErrorPrinterFn = func(err error, p Printer, isLeaf bool) (handled bool, next error)
|
||||
|
||||
// specialCases is a list of functions to apply for special cases.
|
||||
var specialCases []safeErrorPrinterFn
|
||||
|
||||
// RegisterSpecialCasePrinter registers a handler.
|
||||
func RegisterSpecialCasePrinter(fn safeErrorPrinterFn) {
|
||||
specialCases = append(specialCases, fn)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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 {
|
||||
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.
|
||||
func (p *state) finishDisplay(verb rune) {
|
||||
if p.redactableOutput {
|
||||
// If we're rendering in redactable form, then s.finalBuf contains
|
||||
// a RedactableBytes. We can emit that directly.
|
||||
sp := p.State.(redact.SafePrinter)
|
||||
sp.Print(redact.RedactableBytes(p.finalBuf.Bytes()))
|
||||
return
|
||||
}
|
||||
// Not redactable: render depending on flags and verb.
|
||||
|
||||
width, okW := p.Width()
|
||||
_, okP := p.Precision()
|
||||
|
||||
// If `direct` is set to false, then the buffer is always
|
||||
// passed through fmt.Printf regardless of the width and alignment
|
||||
// settings. This is important for e.g. %q where quotes must be added
|
||||
// in any case.
|
||||
// If `direct` is set to true, then the detour via
|
||||
// fmt.Printf only occurs if there is a width or alignment
|
||||
// specifier.
|
||||
direct := verb == 'v' || verb == 's'
|
||||
|
||||
if !direct || (okW && width > 0) || okP {
|
||||
_, format := redact.MakeFormat(p, verb)
|
||||
fmt.Fprintf(p.State, format, p.finalBuf.String())
|
||||
} else {
|
||||
io.Copy(p.State, &p.finalBuf)
|
||||
}
|
||||
}
|
||||
|
||||
var detailSep = []byte("\n | ")
|
||||
|
||||
// state tracks error printing state. It implements fmt.State.
|
||||
type state struct {
|
||||
// state inherits fmt.State.
|
||||
//
|
||||
// If we are rendering with redactableOutput=true, then fmt.State
|
||||
// can be downcasted to redact.SafePrinter.
|
||||
fmt.State
|
||||
|
||||
// redactableOutput indicates whether we want the output
|
||||
// to use redaction markers. When set to true,
|
||||
// the fmt.State above is actually a redact.SafePrinter.
|
||||
redactableOutput bool
|
||||
|
||||
// finalBuf contains the final rendered string, prior to being
|
||||
// copied to the fmt.State above.
|
||||
//
|
||||
// If redactableOutput is true, then finalBuf contains a RedactableBytes
|
||||
// and safe redaction markers. Otherwise, it can be considered
|
||||
// an unsafe string.
|
||||
finalBuf bytes.Buffer
|
||||
|
||||
// entries collect the result of formatRecursive(). They are
|
||||
// consumed by formatSingleLineOutput() and formatEntries() to
|
||||
// produce the contents of finalBuf.
|
||||
entries []formatEntry
|
||||
|
||||
// buf collects the details of the current error object at a given
|
||||
// stage of recursion in formatRecursive().
|
||||
//
|
||||
// At each stage of recursion (level of wrapping), buf contains
|
||||
// successively:
|
||||
//
|
||||
// - at the beginning, the "simple" part of the error message --
|
||||
// either the pre-Detail() string if the error implements Formatter,
|
||||
// or the result of Error().
|
||||
//
|
||||
// - after the first call to Detail(), buf is copied to headBuf,
|
||||
// then reset, then starts collecting the "advanced" part of the
|
||||
// error message.
|
||||
//
|
||||
// At the end of an error layer, the contents of buf and headBuf
|
||||
// are collected into a formatEntry by collectEntry().
|
||||
// This collection does not touch finalBuf above.
|
||||
//
|
||||
// The entries are later consumed by formatSingleLineOutput() or
|
||||
// formatEntries() to produce the contents of finalBuf.
|
||||
//
|
||||
//
|
||||
// Notes regarding redaction markers and string safety. Throughout a
|
||||
// single "level" of error, there are three cases to consider:
|
||||
//
|
||||
// - the error level implements SafeErrorFormatter and
|
||||
// s.redactableOutput is set. In that case, the error's
|
||||
// SafeErrorFormat() is used to produce a RedactableBytes in
|
||||
// buf/headBuf via safePrinter{}, and an entry is collected at the
|
||||
// end of that with the redactable bit set on the entry.
|
||||
//
|
||||
// - the error level implements SafeErrorFormatter
|
||||
// and s.redactableOutput is *not* set. In this case,
|
||||
// for convenience we implement non-redactable output by using
|
||||
// SafeErrorFormat() to generate a RedactableBytes into
|
||||
// buf/headBuf via safePrinter{}, and then stripping the redaction
|
||||
// markers to produce the entry. The entry is not marked as
|
||||
// redactable.
|
||||
//
|
||||
// - in the remaining case (s.redactableOutput is not set or the
|
||||
// error only implements Formatter), then we use FormatError()
|
||||
// to produce a non-redactable string into buf/headBuf,
|
||||
// and mark the resulting entry as non-redactable.
|
||||
buf bytes.Buffer
|
||||
// When an error's FormatError() calls Detail(), the current
|
||||
// value of buf above is copied to headBuf, and a new
|
||||
// buf is initialized.
|
||||
headBuf []byte
|
||||
|
||||
// lastStack tracks the last stack trace observed when looking at
|
||||
// the errors from innermost to outermost. This is used to elide
|
||||
// redundant stack trace entries.
|
||||
lastStack StackTrace
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// The following attributes organize the synchronization of writes
|
||||
// to buf and headBuf, during the rendering of a single error
|
||||
// layer. They get reset between layers.
|
||||
|
||||
// hasDetail becomes true at each level of the formatRecursive()
|
||||
// recursion after the first call to .Detail(). It is used to
|
||||
// determine how to translate buf/headBuf into a formatEntry.
|
||||
hasDetail bool
|
||||
|
||||
// wantDetail is set to true when the error is formatted via %+v.
|
||||
// When false, printer.Detail() will always return false and the
|
||||
// error's .FormatError() method can perform less work. (This is an
|
||||
// optimization for the common case when an error's .Error() method
|
||||
// delegates its work to its .FormatError() via fmt.Format and
|
||||
// errors.FormatError().)
|
||||
wantDetail bool
|
||||
|
||||
// collectingRedactableString is true iff the data being accumulated
|
||||
// into buf comes from a redact string. It ensures that newline
|
||||
// characters are not included inside redaction markers.
|
||||
collectingRedactableString bool
|
||||
|
||||
// notEmpty tracks, at each level of recursion of formatRecursive(),
|
||||
// whether there were any details printed by an error's
|
||||
// .FormatError() method. It is used to properly determine whether
|
||||
// the printout should start with a newline and padding.
|
||||
notEmpty bool
|
||||
// needSpace tracks whether the next character displayed should pad
|
||||
// using a space character.
|
||||
needSpace bool
|
||||
// needNewline tracks whether the next character displayed should
|
||||
// pad using a newline and indentation.
|
||||
needNewline int
|
||||
// multiLine tracks whether the details so far contain multiple
|
||||
// lines. It is used to determine whether an enclosed stack trace,
|
||||
// if any, should be introduced with a separator.
|
||||
multiLine bool
|
||||
}
|
||||
|
||||
// formatEntry collects the textual details about one level of
|
||||
// wrapping or the leaf error in an error chain.
|
||||
type formatEntry struct {
|
||||
err error
|
||||
// redactable is true iff the data in head and details
|
||||
// are RedactableBytes. See the explanatory comments
|
||||
// on (state).buf for when this is set.
|
||||
redactable bool
|
||||
// head is the part of the text that is suitable for printing in the
|
||||
// one-liner summary, or when producing the output of .Error().
|
||||
head []byte
|
||||
// details is the part of the text produced in the advanced output
|
||||
// included for `%+v` formats.
|
||||
details []byte
|
||||
// elideShort, if true, elides the value of 'head' from concatenated
|
||||
// "short" messages produced by formatSingleLineOutput().
|
||||
elideShort bool
|
||||
|
||||
// stackTrace, if non-nil, reports the stack trace embedded at this
|
||||
// level of error.
|
||||
stackTrace StackTrace
|
||||
// elidedStackTrace, if true, indicates that the stack trace was
|
||||
// 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.
|
||||
func (e formatEntry) String() string {
|
||||
return fmt.Sprintf("formatEntry{%T, %q, %q}", e.err, e.head, e.details)
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (s *state) Write(b []byte) (n int, err error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
k := 0
|
||||
|
||||
sep := detailSep
|
||||
if !s.wantDetail {
|
||||
sep = []byte("\n")
|
||||
}
|
||||
|
||||
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
|
||||
// that the _next_ character encountered will pad with a newline.
|
||||
// This algorithm avoids terminating error details with excess
|
||||
// newline characters.
|
||||
k = i + 1
|
||||
s.needNewline++
|
||||
s.needSpace = false
|
||||
s.multiLine = true
|
||||
if s.wantDetail {
|
||||
s.switchOver()
|
||||
}
|
||||
} else {
|
||||
if s.needNewline > 0 && s.notEmpty {
|
||||
// If newline chars were pending, display them now.
|
||||
for i := 0; i < s.needNewline-1; i++ {
|
||||
s.buf.Write(detailSep[:len(sep)-1])
|
||||
}
|
||||
s.buf.Write(sep)
|
||||
s.needNewline = 0
|
||||
s.needSpace = false
|
||||
} else if s.needSpace {
|
||||
s.buf.WriteByte(' ')
|
||||
s.needSpace = false
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// printer wraps a state to implement an xerrors.Printer.
|
||||
type printer state
|
||||
|
||||
func (p *state) detail() bool {
|
||||
if !p.wantDetail {
|
||||
return false
|
||||
}
|
||||
if p.notEmpty {
|
||||
p.needNewline = 1
|
||||
}
|
||||
p.switchOver()
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *state) switchOver() {
|
||||
if p.hasDetail {
|
||||
return
|
||||
}
|
||||
p.headBuf = p.buf.Bytes()
|
||||
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 {
|
||||
return ((*state)(s)).detail()
|
||||
}
|
||||
|
||||
func (s *printer) Print(args ...interface{}) {
|
||||
s.enhanceArgs(args)
|
||||
fmt.Fprint((*state)(s), args...)
|
||||
}
|
||||
|
||||
func (s *printer) Printf(format string, args ...interface{}) {
|
||||
s.enhanceArgs(args)
|
||||
fmt.Fprintf((*state)(s), format, args...)
|
||||
}
|
||||
|
||||
func (s *printer) enhanceArgs(args []interface{}) {
|
||||
prevStack := s.lastStack
|
||||
lastSeen := prevStack
|
||||
for i := range args {
|
||||
if st, ok := args[i].(pkgErr.StackTrace); ok {
|
||||
args[i], _ = ElideSharedStackTraceSuffix(prevStack, st)
|
||||
lastSeen = st
|
||||
}
|
||||
if err, ok := args[i].(error); ok {
|
||||
args[i] = &errorFormatter{err}
|
||||
}
|
||||
}
|
||||
s.lastStack = lastSeen
|
||||
}
|
||||
|
||||
// safePrinter is a variant to printer used when the current error
|
||||
// level implements SafeFormatter.
|
||||
//
|
||||
// In any case, it uses the error's SafeFormatError() method to
|
||||
// prepare a RedactableBytes into s.buf / s.headBuf.
|
||||
// The the explanation for `buf` in the state struct.
|
||||
type safePrinter state
|
||||
|
||||
func (s *safePrinter) Detail() bool {
|
||||
return ((*state)(s)).detail()
|
||||
}
|
||||
|
||||
func (s *safePrinter) Print(args ...interface{}) {
|
||||
s.enhanceArgs(args)
|
||||
redact.Fprint((*state)(s), args...)
|
||||
}
|
||||
|
||||
func (s *safePrinter) Printf(format string, args ...interface{}) {
|
||||
s.enhanceArgs(args)
|
||||
redact.Fprintf((*state)(s), format, args...)
|
||||
}
|
||||
|
||||
func (s *safePrinter) enhanceArgs(args []interface{}) {
|
||||
prevStack := s.lastStack
|
||||
lastSeen := prevStack
|
||||
for i := range args {
|
||||
if st, ok := args[i].(pkgErr.StackTrace); ok {
|
||||
thisStack, _ := ElideSharedStackTraceSuffix(prevStack, st)
|
||||
// Stack traces are safe strings.
|
||||
args[i] = redact.Safe(thisStack)
|
||||
lastSeen = st
|
||||
}
|
||||
// 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.
|
||||
}
|
||||
s.lastStack = lastSeen
|
||||
}
|
||||
|
||||
type errorFormatter struct{ err error }
|
||||
|
||||
// Format implements the fmt.Formatter interface.
|
||||
func (ef *errorFormatter) Format(s fmt.State, verb rune) { FormatError(ef.err, s, verb) }
|
||||
|
||||
// Error implements error, so that `redact` knows what to do with it.
|
||||
func (ef *errorFormatter) Error() string { return ef.err.Error() }
|
||||
|
||||
// Unwrap makes it a wrapper.
|
||||
func (ef *errorFormatter) Unwrap() error { return ef.err }
|
||||
|
||||
// Cause makes it a wrapper.
|
||||
func (ef *errorFormatter) Cause() error { return ef.err }
|
||||
|
||||
// ElideSharedStackTraceSuffix removes the suffix of newStack that's already
|
||||
// present in prevStack. The function returns true if some entries
|
||||
// were elided.
|
||||
func ElideSharedStackTraceSuffix(prevStack, newStack StackTrace) (StackTrace, bool) {
|
||||
if len(prevStack) == 0 {
|
||||
return newStack, false
|
||||
}
|
||||
if len(newStack) == 0 {
|
||||
return newStack, false
|
||||
}
|
||||
|
||||
// Skip over the common suffix.
|
||||
var i, j int
|
||||
for i, j = len(newStack)-1, len(prevStack)-1; i > 0 && j > 0; i, j = i-1, j-1 {
|
||||
if newStack[i] != prevStack[j] {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == 0 {
|
||||
// Keep at least one entry.
|
||||
i = 1
|
||||
}
|
||||
return newStack[:i], i < len(newStack)-1
|
||||
}
|
||||
|
||||
// StackTrace is the type of the data for a call stack.
|
||||
// This mirrors the type of the same name in github.com/pkg/errors.
|
||||
type StackTrace = pkgErr.StackTrace
|
||||
|
||||
// StackFrame is the type of a single call frame entry.
|
||||
// This mirrors the type of the same name in github.com/pkg/errors.
|
||||
type StackFrame = pkgErr.Frame
|
||||
|
||||
// StackTraceProvider is a provider of StackTraces.
|
||||
// This is, intendedly, defined to be implemented by pkg/errors.stack.
|
||||
type StackTraceProvider interface {
|
||||
StackTrace() StackTrace
|
||||
}
|
||||
99
vendor/github.com/cockroachdb/errors/errbase/formatter.go
generated
vendored
Normal file
99
vendor/github.com/cockroachdb/errors/errbase/formatter.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// 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.
|
||||
|
||||
// This file is taken from golang.org/x/xerrors,
|
||||
// at commit 3ee3066db522c6628d440a3a91c4abdd7f5ef22f (2019-05-10).
|
||||
// From the original code:
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package errbase
|
||||
|
||||
// A Formatter formats error messages.
|
||||
//
|
||||
// NB: Consider implementing SafeFormatter instead. This will ensure
|
||||
// that error displays can distinguish bits that are PII-safe.
|
||||
type Formatter interface {
|
||||
error
|
||||
|
||||
// FormatError prints the receiver's first error.
|
||||
// The return value decides what happens in the case
|
||||
// FormatError() is used to produce a "short" message,
|
||||
// eg. when it is used to implement Error():
|
||||
//
|
||||
// - if it returns nil, then the short message
|
||||
// contains no more than that produced for this error,
|
||||
// even if the error has a further causal chain.
|
||||
//
|
||||
// - if it returns non-nil, then the short message
|
||||
// contains the value printed by this error,
|
||||
// followed by that of its causal chain.
|
||||
// (e.g. thiserror: itscause: furthercause)
|
||||
//
|
||||
// Note that all the causal chain is reported in verbose reports in
|
||||
// any case.
|
||||
FormatError(p Printer) (next error)
|
||||
}
|
||||
|
||||
// SafeFormatter is implemented by error leaf or wrapper types that want
|
||||
// to separate safe and non-safe information when printed out.
|
||||
//
|
||||
// When multiple errors are chained (e.g. via errors.Wrap), intermediate
|
||||
// layers in the error that do not implement SafeError are considered
|
||||
// “unsafe”
|
||||
type SafeFormatter interface {
|
||||
// SafeFormatError prints the receiver's first error.
|
||||
//
|
||||
// The provided Printer behaves like a redact.SafePrinter its
|
||||
// Print() and Printf() methods conditionally add redaction markers
|
||||
// around unsafe bits.
|
||||
//
|
||||
// The return value of SafeFormatError() decides what happens in the
|
||||
// case the method is used to produce a "short" message, eg. when it
|
||||
// is used to implement Error():
|
||||
//
|
||||
// - if it returns nil, then the short message
|
||||
// contains no more than that produced for this error,
|
||||
// even if the error has a further causal chain.
|
||||
//
|
||||
// - if it returns non-nil, then the short message
|
||||
// contains the value printed by this error,
|
||||
// followed by that of its causal chain.
|
||||
// (e.g. thiserror: itscause: furthercause)
|
||||
//
|
||||
// Note that all the causal chain is reported in verbose reports in
|
||||
// any case.
|
||||
SafeFormatError(p Printer) (next error)
|
||||
}
|
||||
|
||||
// A Printer formats error messages.
|
||||
//
|
||||
// The most common implementation of Printer is the one provided by package fmt
|
||||
// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message
|
||||
// typically provide their own implementations.
|
||||
type Printer interface {
|
||||
// Print appends args to the message output.
|
||||
Print(args ...interface{})
|
||||
|
||||
// Printf writes a formatted string.
|
||||
Printf(format string, args ...interface{})
|
||||
|
||||
// Detail reports whether error detail is requested.
|
||||
// After the first call to Detail, all text written to the Printer
|
||||
// is formatted as additional detail, or ignored when
|
||||
// detail has not been requested.
|
||||
// If Detail returns false, the caller can avoid printing the detail at all.
|
||||
Detail() bool
|
||||
}
|
||||
123
vendor/github.com/cockroachdb/errors/errbase/migrations.go
generated
vendored
Normal file
123
vendor/github.com/cockroachdb/errors/errbase/migrations.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// 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 "fmt"
|
||||
|
||||
// This file provides the library with the ability to handle cases
|
||||
// where an error type migrates, i.e. its package changes path or the
|
||||
// type name is changed.
|
||||
//
|
||||
// There are several scenarios to contend with. Assuming the error
|
||||
// type is initially called "foo", in version v1 of the code.
|
||||
//
|
||||
// Scenario 1: simple migration
|
||||
// - v2 renames foo -> bar
|
||||
// v2 calls: RegisterTypeMigration("foo", &bar{})
|
||||
// - v2 and v1 are connected
|
||||
// - v1 sends an error to v2:
|
||||
// - v2 has the migration registered, recognizes that "foo"
|
||||
// refers to bar
|
||||
// - v2 sends an error to v1
|
||||
// - v2 rewrites the error key upon send to the name known to v1
|
||||
//
|
||||
// Scenario 2: simultaneous migration
|
||||
// - vA renames foo -> bar
|
||||
// vA calls RegisterTypeMigration("foo", &bar{})
|
||||
// - vB renames foo -> qux
|
||||
// vB calls RegisterTypeMigration("foo", &qux{})
|
||||
// - vA and vB are connected
|
||||
// - vA sends an error to vB:
|
||||
// - vA translates the error key upon send from bar to foo's key
|
||||
// - vB recognizes that "foo" refers to qux
|
||||
//
|
||||
// Scenario 3: migrated error passing through
|
||||
// - v2 renames foo -> bar
|
||||
// v2 calls: RegisterTypeMigration("foo", &bar{})
|
||||
// - v2.a, v2.b and v1 are connected: v2.a -> v1 -> v2.b
|
||||
// - v2.a sends an error to v2.b via v1:
|
||||
// - v2.a encodes using foo's key, v1 receives as foo
|
||||
// - v1 encodes using foo's key
|
||||
// - v2.b receive's foo's key, knows about migration, decodes as bar
|
||||
//
|
||||
// Scenario 4: migrated error passing through node that does not know
|
||||
// about it whatsoever (the key is preserved).
|
||||
// - v2 renames foo -> bar
|
||||
// v2 calls: RegisterTypeMigration("foo", &bar{})
|
||||
// - v2.a, v2.b and v0 are connected: v2.a -> v0 -> v2.b
|
||||
// (v0 does not know about error foo at all)
|
||||
// - v2.a sends an error to v2.b via v0:
|
||||
// - v2.a encodes using foo's key, v0 receives as "unknown foo"
|
||||
// - v0 passes through unchanged
|
||||
// - v2.b receive's foo's key, knows about migration, decodes as bar
|
||||
//
|
||||
// Scenario 5: comparison between migrated and non-migrated errors
|
||||
// on 3rd party node.
|
||||
// - v2 renames foo -> bar
|
||||
// - v2 sends error bar to v0
|
||||
// - v1 sends an equivalent error with type foo to v0
|
||||
// - v0 (that doesn't know about the type) compares the two errors.
|
||||
// Here we're expecting v0 to properly ascertain the errors are equivalent.
|
||||
|
||||
// RegisterTypeMigration tells the library that the type of the error
|
||||
// given as 3rd argument was previously known with type
|
||||
// previousTypeName, located at previousPkgPath.
|
||||
//
|
||||
// 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>
|
||||
//
|
||||
// For example, Go's standard error type has name "*errors.errorString".
|
||||
// The asterisk indicates that `errorString` implements the `error`
|
||||
// interface via pointer receiver.
|
||||
//
|
||||
// Meanwhile, the singleton error type context.DeadlineExceeded
|
||||
// has name "context.deadlineExceededError", without asterisk
|
||||
// because the type implements `error` by value.
|
||||
//
|
||||
// Remember that the short package name inside the error type name and
|
||||
// the last component of the package path can be different. This is
|
||||
// why they must be specified separately.
|
||||
func RegisterTypeMigration(previousPkgPath, previousTypeName string, newType error) {
|
||||
prevKey := TypeKey(makeTypeKey(previousPkgPath, previousTypeName))
|
||||
newKey := TypeKey(getFullTypeName(newType))
|
||||
|
||||
// Register the backward migration: make the encode function
|
||||
// aware of the old name.
|
||||
if f, ok := backwardRegistry[newKey]; ok {
|
||||
panic(fmt.Errorf("migration to type %q already registered (from %q)", newKey, f))
|
||||
}
|
||||
backwardRegistry[newKey] = prevKey
|
||||
// If any other key was registered as a migration from newKey,
|
||||
// we'll forward those as well.
|
||||
// This changes X -> newKey to X -> prevKey for every X.
|
||||
for new, prev := range backwardRegistry {
|
||||
if prev == newKey {
|
||||
backwardRegistry[new] = prevKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// registry used when encoding an error, so that the receiver observes
|
||||
// the original key. This maps new keys to old keys.
|
||||
var backwardRegistry = map[TypeKey]TypeKey{}
|
||||
|
||||
// TestingWithEmptyMigrationRegistry is intended for use by tests.
|
||||
func TestingWithEmptyMigrationRegistry() (restore func()) {
|
||||
save := backwardRegistry
|
||||
backwardRegistry = map[TypeKey]TypeKey{}
|
||||
return func() { backwardRegistry = save }
|
||||
}
|
||||
137
vendor/github.com/cockroachdb/errors/errbase/opaque.go
generated
vendored
Normal file
137
vendor/github.com/cockroachdb/errors/errbase/opaque.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/errors/errorspb"
|
||||
"github.com/cockroachdb/redact"
|
||||
)
|
||||
|
||||
// opaqueLeaf is used when receiving an unknown leaf 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 opaqueLeaf struct {
|
||||
msg string
|
||||
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
|
||||
messageType MessageType
|
||||
}
|
||||
|
||||
var _ error = (*opaqueWrapper)(nil)
|
||||
var _ SafeDetailer = (*opaqueWrapper)(nil)
|
||||
var _ fmt.Formatter = (*opaqueWrapper)(nil)
|
||||
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()
|
||||
}
|
||||
return fmt.Sprintf("%s: %s", e.prefix, e.cause)
|
||||
}
|
||||
|
||||
// the opaque wrapper is a wrapper.
|
||||
func (e *opaqueWrapper) Cause() error { return e.cause }
|
||||
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 *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)
|
||||
if p.Detail() {
|
||||
p.Printf("\n(opaque error leaf)")
|
||||
p.Printf("\ntype name: %s", redact.Safe(e.details.OriginalTypeName))
|
||||
for i, d := range e.details.ReportablePayload {
|
||||
p.Printf("\nreportable %d:\n%s", redact.Safe(i), redact.Safe(d))
|
||||
}
|
||||
if e.details.FullDetails != nil {
|
||||
p.Printf("\npayload type: %s", redact.Safe(e.details.FullDetails.TypeUrl))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *opaqueWrapper) SafeFormatError(p Printer) (next error) {
|
||||
if len(e.prefix) > 0 {
|
||||
// We use the condition if len(msg) > 0 because
|
||||
// otherwise an empty string would cause a "redactable
|
||||
// empty string" to be emitted (something that looks like "<>")
|
||||
// and the error formatting code only cleanly elides
|
||||
// the prefix properly if the output string is completely empty.
|
||||
p.Print(e.prefix)
|
||||
}
|
||||
if p.Detail() {
|
||||
p.Printf("\n(opaque error wrapper)")
|
||||
p.Printf("\ntype name: %s", redact.Safe(e.details.OriginalTypeName))
|
||||
for i, d := range e.details.ReportablePayload {
|
||||
p.Printf("\nreportable %d:\n%s", redact.Safe(i), redact.Safe(d))
|
||||
}
|
||||
if e.details.FullDetails != nil {
|
||||
p.Printf("\npayload type: %s", redact.Safe(e.details.FullDetails.TypeUrl))
|
||||
}
|
||||
}
|
||||
if e.messageType == FullMessage {
|
||||
return nil
|
||||
}
|
||||
return e.cause
|
||||
}
|
||||
24
vendor/github.com/cockroachdb/errors/errbase/oserror_go116.go
generated
vendored
Normal file
24
vendor/github.com/cockroachdb/errors/errbase/oserror_go116.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
// +build go1.16
|
||||
|
||||
package errbase
|
||||
|
||||
import "io/fs"
|
||||
|
||||
func registerOsPathErrorMigration() {
|
||||
// The os.PathError type was migrated to io.fs.PathError in Go 1.16.
|
||||
RegisterTypeMigration("os", "*os.PathError", &fs.PathError{})
|
||||
}
|
||||
19
vendor/github.com/cockroachdb/errors/errbase/oserror_pre116.go
generated
vendored
Normal file
19
vendor/github.com/cockroachdb/errors/errbase/oserror_pre116.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
// +build !go1.16
|
||||
|
||||
package errbase
|
||||
|
||||
func registerOsPathErrorMigration() {}
|
||||
93
vendor/github.com/cockroachdb/errors/errbase/safe_details.go
generated
vendored
Normal file
93
vendor/github.com/cockroachdb/errors/errbase/safe_details.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/errors/errorspb"
|
||||
pkgErr "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// SafeDetailer is an interface that can be implemented by errors that
|
||||
// can provide PII-free additional strings suitable for reporting or
|
||||
// telemetry.
|
||||
type SafeDetailer interface {
|
||||
SafeDetails() []string
|
||||
}
|
||||
|
||||
// GetAllSafeDetails collects the safe details from the given error object
|
||||
// and all its causes.
|
||||
// The details are collected from outermost to innermost level of cause.
|
||||
func GetAllSafeDetails(err error) []SafeDetailPayload {
|
||||
var details []SafeDetailPayload
|
||||
for ; err != nil; err = UnwrapOnce(err) {
|
||||
details = append(details, GetSafeDetails(err))
|
||||
}
|
||||
return details
|
||||
}
|
||||
|
||||
// GetSafeDetails collects the safe details from the given error
|
||||
// object. If it is a wrapper, only the details from the wrapper are
|
||||
// returned.
|
||||
func GetSafeDetails(err error) (payload SafeDetailPayload) {
|
||||
origTypeName, famName, ext := getTypeDetails(err, false /*onlyFamily*/)
|
||||
payload.OriginalTypeName = origTypeName
|
||||
payload.ErrorTypeMark = errorspb.ErrorTypeMark{
|
||||
FamilyName: famName,
|
||||
Extension: ext,
|
||||
}
|
||||
payload.SafeDetails = getDetails(err)
|
||||
return
|
||||
}
|
||||
|
||||
func getDetails(err error) []string {
|
||||
if sd, ok := err.(SafeDetailer); ok {
|
||||
return sd.SafeDetails()
|
||||
}
|
||||
// For convenience, we also know how to extract stack traces
|
||||
// in the style of github.com/pkg/errors.
|
||||
if st, ok := err.(interface{ StackTrace() pkgErr.StackTrace }); ok {
|
||||
return []string{fmt.Sprintf("%+v", st.StackTrace())}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SafeDetailPayload captures the safe strings for one
|
||||
// level of wrapping.
|
||||
type SafeDetailPayload struct {
|
||||
// OriginalTypeName is the concrete type of the error that the details
|
||||
// are coming from.
|
||||
OriginalTypeName string
|
||||
// ErrorTypeMark is the mark of the error that the details are
|
||||
// coming from. This may contain a different type name than
|
||||
// OriginalTypeName in case an error type was migrated.
|
||||
ErrorTypeMark errorspb.ErrorTypeMark
|
||||
// SafeDetails are the PII-free strings.
|
||||
SafeDetails []string
|
||||
}
|
||||
|
||||
// Fill can be used to concatenate multiple SafeDetailPayloads.
|
||||
func (s *SafeDetailPayload) Fill(slice []string) []string {
|
||||
if len(s.SafeDetails) == 0 {
|
||||
return slice
|
||||
}
|
||||
slice = append(slice, fmt.Sprintf("details for %s::%s:",
|
||||
s.ErrorTypeMark.FamilyName, s.ErrorTypeMark.Extension))
|
||||
for _, sd := range s.SafeDetails {
|
||||
slice = append(slice, " "+sd)
|
||||
}
|
||||
return slice
|
||||
}
|
||||
71
vendor/github.com/cockroachdb/errors/errbase/unwrap.go
generated
vendored
Normal file
71
vendor/github.com/cockroachdb/errors/errbase/unwrap.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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
|
||||
|
||||
// Sadly the go 2/1.13 design for errors has promoted the name
|
||||
// `Unwrap()` for the method that accesses the cause, whilst the
|
||||
// ecosystem has already chosen `Cause()`. In order to unwrap
|
||||
// reliably, we must thus support both.
|
||||
//
|
||||
// See: https://github.com/golang/go/issues/31778
|
||||
|
||||
// UnwrapOnce accesses the direct cause of the error if any, otherwise
|
||||
// returns nil.
|
||||
//
|
||||
// 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 }:
|
||||
return e.Cause()
|
||||
case interface{ Unwrap() error }:
|
||||
return e.Unwrap()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
err = cause
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user