0

Initial commit

This commit is contained in:
eleksir
2023-09-17 12:18:09 +03:00
commit 843dd418b0
1514 changed files with 643531 additions and 0 deletions

View 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)
}

View 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
View 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
View 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{}

View 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
}

View 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
}

View 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
View 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
}

View 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{})
}

View 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() {}

View 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
View 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
}