zen/core/zcaller/zcaller.go

89 lines
1.9 KiB
Go

package zcaller
import (
"log/slog"
"os"
"runtime"
"strings"
)
// Current returns the ProgramCounter of the caller.
//
// It gives information about the function that called
// this.
//
// Returns 0 if caller information is not available.
func Current() uintptr {
return Get(3)
}
// Get works just like Current, but it allows you to
// specify the number of frames to skip.
//
// For reference, current is equivalent to Get(3).
// (use Get(2) because Current wraps this function and has to add +1 to the skip value)
//
// Returns 0 if caller information is not available.
func Get(skip int) uintptr {
pcs := make([]uintptr, 1)
n := runtime.Callers(skip, pcs)
if n == 0 {
return 0
}
return pcs[0]
}
// Frame creates a runtime.Frame from a ProgramCounter.
//
// Return zero value if the frame is not available.
func Frame(pc uintptr) runtime.Frame {
frames := runtime.CallersFrames([]uintptr{pc})
frame, _ := frames.Next()
return frame
}
// Caller represents the caller of a function.
//
// It gives helper methods to get the caller's information.
type Caller uintptr
func (ca Caller) LogValue() slog.Value {
if ca == 0 {
return slog.AnyValue(nil)
}
return ca.Frame().LogValue()
}
func (ca Caller) Frame() RuntimeFrame {
return RuntimeFrame{Frame(uintptr(ca))}
}
type RuntimeFrame struct {
runtime.Frame
}
func (ru RuntimeFrame) LogValue() slog.Value {
return slog.GroupValue(
slog.String("file", ru.ShortFile()),
slog.Int("line", ru.Line),
slog.String("function", ru.ShortFunction()),
)
}
func (f RuntimeFrame) ShortFile() string {
wd, err := os.Getwd()
if err != nil {
return f.File
}
if after, found := strings.CutPrefix(f.File, wd); found {
return strings.TrimPrefix(after, string(os.PathSeparator))
}
return f.File
}
func (f RuntimeFrame) ShortFunction() string {
split := strings.Split(f.Function, string(os.PathSeparator))
return split[len(split)-1]
}