98 lines
2.0 KiB
Go
98 lines
2.0 KiB
Go
|
package errors
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"runtime"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"unsafe"
|
||
|
)
|
||
|
|
||
|
// Callers ...
|
||
|
type Callers []runtime.Frame
|
||
|
|
||
|
// MarshalJSON implements json.Marshaler to provide an easy, simple default.
|
||
|
func (c Callers) MarshalJSON() ([]byte, error) {
|
||
|
// JSON-able frame type
|
||
|
type jsonFrame struct {
|
||
|
Func string `json:"func"`
|
||
|
File string `json:"file"`
|
||
|
Line int `json:"line"`
|
||
|
}
|
||
|
|
||
|
// Allocate expected size jsonFrame slice
|
||
|
jsonFrames := make([]jsonFrame, len(c))
|
||
|
|
||
|
// Convert each to jsonFrame object
|
||
|
for i := 0; i < len(c); i++ {
|
||
|
frame := c[i]
|
||
|
jsonFrames[i] = jsonFrame{
|
||
|
Func: funcName(frame.Func),
|
||
|
File: frame.File,
|
||
|
Line: frame.Line,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// marshal converted frames
|
||
|
return json.Marshal(jsonFrames)
|
||
|
}
|
||
|
|
||
|
// String will return a simple string representation of receiving Callers slice.
|
||
|
func (c Callers) String() string {
|
||
|
// Guess-timate to reduce allocs
|
||
|
buf := make([]byte, 0, 64*len(c))
|
||
|
|
||
|
for i := 0; i < len(c); i++ {
|
||
|
frame := c[i]
|
||
|
|
||
|
// Append formatted caller info
|
||
|
fn := funcName(frame.Func)
|
||
|
buf = append(buf, fn+"()\n\t"+frame.File+":"...)
|
||
|
buf = strconv.AppendInt(buf, int64(frame.Line), 10)
|
||
|
buf = append(buf, '\n')
|
||
|
}
|
||
|
|
||
|
return *(*string)(unsafe.Pointer(&buf))
|
||
|
}
|
||
|
|
||
|
// funcName formats a function name to a quickly-readable string.
|
||
|
func funcName(fn *runtime.Func) string {
|
||
|
if fn == nil {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// Get func name
|
||
|
// for formatting.
|
||
|
name := fn.Name()
|
||
|
|
||
|
// Drop all but the package name and function name, no mod path
|
||
|
if idx := strings.LastIndex(name, "/"); idx >= 0 {
|
||
|
name = name[idx+1:]
|
||
|
}
|
||
|
|
||
|
const params = `[...]`
|
||
|
|
||
|
// Drop any generic type parameter markers
|
||
|
if idx := strings.Index(name, params); idx >= 0 {
|
||
|
name = name[:idx] + name[idx+len(params):]
|
||
|
}
|
||
|
|
||
|
return name
|
||
|
}
|
||
|
|
||
|
// gatherFrames collates runtime frames from a frame iterator.
|
||
|
func gatherFrames(iter *runtime.Frames, n int) Callers {
|
||
|
if iter == nil {
|
||
|
return nil
|
||
|
}
|
||
|
frames := make([]runtime.Frame, 0, n)
|
||
|
for {
|
||
|
f, ok := iter.Next()
|
||
|
if !ok {
|
||
|
break
|
||
|
}
|
||
|
frames = append(frames, f)
|
||
|
}
|
||
|
return frames
|
||
|
}
|