mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-15 19:00:19 +00:00
145 lines
3.0 KiB
Go
145 lines
3.0 KiB
Go
|
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, ": ")
|
||
|
}
|