From 0d8e0cf31f8a4e645f21ede3ee95e690fb077bbd Mon Sep 17 00:00:00 2001 From: k-simons Date: Tue, 4 Jun 2019 13:29:25 -0700 Subject: [PATCH] Add werror.GenerateErrorString for generating the stacktrace param string (#51) --- Gopkg.lock | 6 +- Gopkg.toml | 4 + .../palantir/witchcraft-go-error/Gopkg.lock | 8 +- .../palantir/witchcraft-go-error/README.md | 2 + .../palantir/witchcraft-go-error/werror.go | 15 ++- .../witchcraft-go-error/werror_printer.go | 104 ++++++++++++++++++ wlog/svclog/svc1log/params.go | 13 +-- 7 files changed, 129 insertions(+), 23 deletions(-) create mode 100644 vendor/github.com/palantir/witchcraft-go-error/werror_printer.go diff --git a/Gopkg.lock b/Gopkg.lock index b53959cd..56451f34 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -55,15 +55,15 @@ version = "0.9.4" [[projects]] - branch = "develop" - digest = "1:89b7c2365c6b481a9e1c83a203c817d117376f48330811a977d84ec198281c8b" + digest = "1:5728d88b803739d853e607869383fee9699b1521153d3f8a76a0fce179263098" name = "github.com/palantir/witchcraft-go-error" packages = [ ".", "internal/errors", ] pruneopts = "UT" - revision = "adb1d93c5741a300abbcce7883ad61f825a4f1a7" + revision = "c915c4fc8543324aa5d13086e2dc70ce08d71cd5" + version = "1.1.0" [[projects]] branch = "develop" diff --git a/Gopkg.toml b/Gopkg.toml index a4882ee2..0680c27d 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -32,6 +32,10 @@ name = "github.com/palantir/pkg" version = ">=0.7.0" +[[constraint]] + name = "github.com/palantir/witchcraft-go-error" + version = ">=1.1.0" + [[constraint]] name = "github.com/rs/zerolog" version = "1.10.3" diff --git a/vendor/github.com/palantir/witchcraft-go-error/Gopkg.lock b/vendor/github.com/palantir/witchcraft-go-error/Gopkg.lock index 6d8f3df0..51ccdd6f 100644 --- a/vendor/github.com/palantir/witchcraft-go-error/Gopkg.lock +++ b/vendor/github.com/palantir/witchcraft-go-error/Gopkg.lock @@ -8,10 +8,10 @@ version = "v1.1.1" [[projects]] - branch = "develop" name = "github.com/palantir/witchcraft-go-params" packages = ["."] - revision = "169dfa093927392e08fde093fee47226d251a11d" + revision = "d5c8ded2ab5588d1a81f306247162cb54d2504d7" + version = "1.0.0" [[projects]] name = "github.com/pmezard/go-difflib" @@ -25,8 +25,8 @@ "assert", "require", ] - revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" - version = "v1.2.2" + revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" + version = "v1.3.0" [solve-meta] analyzer-name = "dep" diff --git a/vendor/github.com/palantir/witchcraft-go-error/README.md b/vendor/github.com/palantir/witchcraft-go-error/README.md index 48047bdd..b6500f7b 100644 --- a/vendor/github.com/palantir/witchcraft-go-error/README.md +++ b/vendor/github.com/palantir/witchcraft-go-error/README.md @@ -1,5 +1,7 @@ witchcraft-go-error =================== +[![](https://godoc.org/github.com/palantir/witchcraft-go-error?status.svg)](http://godoc.org/github.com/palantir/witchcraft-go-error) + `witchcraft-error-go` defines the `werror` package, which provides an implementation of the `error` interface that stores safe and unsafe parameters and has the ability to specify another error as a cause. diff --git a/vendor/github.com/palantir/witchcraft-go-error/werror.go b/vendor/github.com/palantir/witchcraft-go-error/werror.go index 5a813a29..21e054ea 100644 --- a/vendor/github.com/palantir/witchcraft-go-error/werror.go +++ b/vendor/github.com/palantir/witchcraft-go-error/werror.go @@ -62,7 +62,7 @@ func Convert(err error) error { case *werror: return err default: - return Error(err.Error()) + return newWerror("", err) } } @@ -192,6 +192,9 @@ func (e *werror) Error() string { if e.cause == nil { return e.message } + if e.message == "" { + return e.cause.Error() + } return e.message + ": " + e.cause.Error() } @@ -270,16 +273,20 @@ func (e *werror) formatCause(state fmt.State, verb rune) { if e.cause == nil { return } + var prefix string + if e.message != "" || (verb == 'v' && len(e.params) > 0) { + prefix = ": " + } switch verb { case 'v': if state.Flag('+') { fmt.Fprintf(state, "%+v\n", e.cause) } else { - fmt.Fprintf(state, ": %v", e.cause) + fmt.Fprintf(state, "%s%v", prefix, e.cause) } case 's': - fmt.Fprintf(state, ": %s", e.cause) + fmt.Fprintf(state, "%s%s", prefix, e.cause) case 'q': - fmt.Fprintf(state, ": %q", e.cause) + fmt.Fprintf(state, "%s%q", prefix, e.cause) } } diff --git a/vendor/github.com/palantir/witchcraft-go-error/werror_printer.go b/vendor/github.com/palantir/witchcraft-go-error/werror_printer.go new file mode 100644 index 00000000..5fe4b4f3 --- /dev/null +++ b/vendor/github.com/palantir/witchcraft-go-error/werror_printer.go @@ -0,0 +1,104 @@ +package werror + +import ( + "bytes" + "fmt" + "sort" +) + +// GenerateErrorString will attempt to pretty print an error depending on its underlying type +// If it is a werror then: +// 1) Each message and params will be groups together on a separate line +// 2) Only the deepest werror stacktrace will be printed +// 3) GenerateErrorString will be called recursively to pretty print underlying errors as well +// If the error implements the fmt.Formatter interface, then it will be printed verbosely +// Otherwise, the error's underlying Error() function will be called and returned +func GenerateErrorString(err error, outputEveryCallingStack bool) string { + if werror, ok := err.(*werror); ok { + return generateWerrorString(werror, outputEveryCallingStack) + } + if fancy, ok := err.(fmt.Formatter); ok { + // This is a rich error type, like those produced by github.com/pkg/errors. + return fmt.Sprintf("%+v", fancy) + } + return err.Error() +} + +func generateWerrorString(err *werror, outputEveryCallingStack bool) string { + var buffer bytes.Buffer + writeMessage(err, &buffer) + writeParams(err, &buffer) + writeCause(err, &buffer, outputEveryCallingStack) + writeStack(err, &buffer, outputEveryCallingStack) + return buffer.String() +} + +func writeMessage(err *werror, buffer *bytes.Buffer) { + if err.message == "" { + return + } + buffer.WriteString(err.message) +} + +func writeParams(err *werror, buffer *bytes.Buffer) { + safeParams := getSafeParamsAtCurrentLevel(err) + var safeKeys []string + for k := range safeParams { + safeKeys = append(safeKeys, k) + } + sort.Strings(safeKeys) + messageAndParams := err.message != "" && len(safeParams) != 0 + messageOrParams := err.message != "" || len(safeParams) != 0 + if messageAndParams { + buffer.WriteString(" ") + } + for _, safeKey := range safeKeys { + buffer.WriteString(fmt.Sprintf("%+v:%+v", safeKey, safeParams[safeKey])) + // If it is not the last param, add a separator + if !(safeKeys[len(safeKeys)-1] == safeKey) { + buffer.WriteString(", ") + } + } + if messageOrParams { + buffer.WriteString("\n") + } +} + +func getSafeParamsAtCurrentLevel(err *werror) map[string]interface{} { + safeParamsAtThisLevel := make(map[string]interface{}, 0) + childSafeParams := getChildSafeParams(err) + for k, v := range err.SafeParams() { + _, ok := childSafeParams[k] + if ok { + continue + } + safeParamsAtThisLevel[k] = v + } + return safeParamsAtThisLevel +} + +func getChildSafeParams(err *werror) map[string]interface{} { + if err.cause == nil { + return make(map[string]interface{}, 0) + } + causeAsWerror, ok := err.cause.(*werror) + if !ok { + return make(map[string]interface{}, 0) + } + return causeAsWerror.SafeParams() +} + +func writeCause(err *werror, buffer *bytes.Buffer, outputEveryCallingStack bool) { + if err.cause != nil { + buffer.WriteString(GenerateErrorString(err.cause, outputEveryCallingStack)) + } +} + +func writeStack(err *werror, buffer *bytes.Buffer, outputEveryCallingStack bool) { + if _, ok := err.cause.(*werror); ok { + if !outputEveryCallingStack { + return + } + } + buffer.WriteString(fmt.Sprintf("%+v", err.stack)) +} diff --git a/wlog/svclog/svc1log/params.go b/wlog/svclog/svc1log/params.go index 8b3f442d..14e5e82e 100644 --- a/wlog/svclog/svc1log/params.go +++ b/wlog/svclog/svc1log/params.go @@ -15,7 +15,6 @@ package svc1log import ( - "fmt" "path" "runtime" "strconv" @@ -177,17 +176,7 @@ func Stacktrace(err error) Param { if err == nil { return } - - // set the value of the error - errStr := err.Error() - if fancy, ok := err.(fmt.Formatter); ok { - verbose := fmt.Sprintf("%+v", fancy) - if verbose != errStr { - // this is a rich error type, like those produced by github.com/pkg/errors - errStr = verbose - } - } - entry.StringValue(StacktraceKey, errStr) + entry.StringValue(StacktraceKey, werror.GenerateErrorString(err, false)) // add all safe and unsafe parameters stored in error safeParams, unsafeParams := werror.ParamsFromError(err)