2024-08-23 12:29:47 +07:00
|
|
|
package zcaller
|
|
|
|
|
|
|
|
import (
|
2024-08-28 11:01:20 +07:00
|
|
|
"log/slog"
|
|
|
|
"os"
|
2024-08-23 12:29:47 +07:00
|
|
|
"runtime"
|
2024-08-28 11:01:20 +07:00
|
|
|
"strings"
|
2024-08-23 12:29:47 +07:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
2024-08-28 11:01:20 +07:00
|
|
|
|
|
|
|
// 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]
|
|
|
|
}
|