package caller import ( "context" "log/slog" "os" "runtime" "strings" ) type Caller struct { PC uintptr Frame runtime.Frame } func (ca Caller) File() string { return ca.Frame.File } func (ca Caller) ShortFile() string { wd, err := os.Getwd() if err != nil { return ca.Frame.File } if after, found := strings.CutPrefix(ca.Frame.File, wd); found { return strings.TrimPrefix(after, string(os.PathSeparator)) } return ca.Frame.File } func (ca Caller) Line() int { return ca.Frame.Line } func (ca Caller) Function() string { return ca.Frame.Function } func (ca Caller) ShortFunction() string { split := strings.Split(ca.Frame.Function, string(os.PathSeparator)) return split[len(split)-1] } func (ca Caller) LogValue() slog.Value { if ca.PC == 0 { return slog.AnyValue(nil) } return slog.GroupValue( slog.String("file", ca.ShortFile()), slog.Int("line", ca.Line()), slog.String("function", ca.ShortFunction()), ) } func New(skip int) Caller { var c Caller pcs := make([]uintptr, 1) n := runtime.Callers(skip, pcs) if n == 0 { return c } c.PC = pcs[0] c.Frame, _ = runtime.CallersFrames(pcs).Next() return c } func From(pc uintptr) Caller { var c Caller c.PC = pc c.Frame, _ = runtime.CallersFrames([]uintptr{pc}).Next() return c } type contextKey struct{} func WithContext(ctx context.Context, caller Caller) context.Context { return context.WithValue(ctx, contextKey{}, caller) } func FromContext(ctx context.Context) (Caller, bool) { call, ok := ctx.Value(contextKey{}).(Caller) return call, ok }