ceph-csi/vendor/github.com/ansel1/merry/v2/print.go

145 lines
3.0 KiB
Go
Raw Normal View History

package merry
import (
"fmt"
"io"
"path"
"runtime"
"sort"
"strings"
)
// Location returns zero values if e has no stacktrace
func Location(err error) (file string, line int) {
s := Stack(err)
if len(s) > 0 {
fnc, _ := runtime.CallersFrames(s[:1]).Next()
return fnc.File, fnc.Line
}
return "", 0
}
// SourceLine returns the string representation of
// Location's result or an empty string if there's
// no stracktrace.
func SourceLine(err error) string {
s := Stack(err)
if len(s) > 0 {
fnc, _ := runtime.CallersFrames(s[:1]).Next()
_, f := path.Split(fnc.File)
return fmt.Sprintf("%s (%s:%d)", fnc.Function, f, fnc.Line)
}
return ""
}
// FormattedStack returns the stack attached to an error, formatted as a slice of strings.
// Each string represents a frame in the stack, newest first. The strings may
// have internal newlines.
//
// Returns nil if no formatted stack and no stack is associated, or err is nil.
func FormattedStack(err error) []string {
formattedStack, _ := Value(err, errKeyStack).([]string)
if len(formattedStack) > 0 {
return formattedStack
}
s := Stack(err)
if len(s) > 0 {
lines := make([]string, 0, len(s))
frames := runtime.CallersFrames(s)
for {
frame, more := frames.Next()
lines = append(lines, fmt.Sprintf("%s\n\t%s:%d", frame.Function, frame.File, frame.Line))
if !more {
break
}
}
return lines
}
return nil
}
// Stacktrace returns the error's stacktrace as a string formatted.
// If e has no stacktrace, returns an empty string.
func Stacktrace(err error) string {
return strings.Join(FormattedStack(err), "\n")
}
// Details returns e.Error(), e's stacktrace, and any additional details which have
// be registered with RegisterDetail. User message and HTTP code are already registered.
//
// The details of each error in e's cause chain will also be printed.
func Details(e error) string {
if e == nil {
return ""
}
msg := e.Error()
var dets []string
detailsLock.Lock()
for label, f := range detailFields {
v := f(e)
if v != nil {
dets = append(dets, fmt.Sprintf("%s: %v", label, v))
}
}
detailsLock.Unlock()
if len(dets) > 0 {
// sort so output is predictable
sort.Strings(dets)
msg += "\n" + strings.Join(dets, "\n")
}
s := Stacktrace(e)
if s != "" {
msg += "\n\n" + s
}
if c := Cause(e); c != nil {
msg += "\n\nCaused By: " + Details(c)
}
return msg
}
// Format adapts errors to fmt.Formatter interface. It's intended to be used
// help error impls implement fmt.Formatter, e.g.:
//
// func (e *myErr) Format(f fmt.State, verb rune) {
// Format(f, verb, e)
// }
//
func Format(s fmt.State, verb rune, err error) {
switch verb {
case 'v':
if s.Flag('+') {
io.WriteString(s, Details(err))
return
}
fallthrough
case 's':
io.WriteString(s, msgWithCauses(err))
case 'q':
fmt.Fprintf(s, "%q", err.Error())
}
}
func msgWithCauses(err error) string {
messages := make([]string, 0, 5)
for err != nil {
if ce := err.Error(); ce != "" {
messages = append(messages, ce)
}
err = Cause(err)
}
return strings.Join(messages, ": ")
}