diff --git a/go/log/slog/record.go b/go/log/slog/record.go index 6dcaa511..d7ff4a7f 100644 --- a/go/log/slog/record.go +++ b/go/log/slog/record.go @@ -33,19 +33,9 @@ func source(r slog.Record) *slog.Source { func ShortSource(r slog.Record) *slog.Source { fs := runtime.CallersFrames([]uintptr{r.PC}) f, _ := fs.Next() - - function := path.Base(f.Function) - slash := strings.LastIndex(function, ".") - if slash >= 0 && slash+1 < len(function) { - function = function[slash+1:] - } - file := path.Base(f.File) - if file == "" { - file = "???" - } return &slog.Source{ - Function: function, - File: path.Base(f.File), + Function: shortFunction(f.Function), + File: shortFile(f.File), Line: f.Line, } } @@ -54,11 +44,34 @@ func ShortSource(r slog.Record) *slog.Source { // file keys in the data when ReportCaller is activated. // INFO[0000] main.go:23 main() hello world var ShortCallerPrettyfier = func(f *runtime.Frame) (function string, file string) { - funcname := path.Base(f.Function) - filename := path.Base(f.File) + funcname := shortFunction(f.Function) + filename := shortFile(f.File) return fmt.Sprintf("%s()", funcname), fmt.Sprintf("%s:%d", filename, f.Line) } +func shortFunction(function string) string { + function = path.Base(function) + prefix := function + // the shape name in generic function is replaced with "..." + // See: https://github.com/golang/go/blob/go1.22.5/src/runtime/traceback.go#L755 + if i := strings.Index(function, "[...]"); i >= 0 { + prefix = function[:i] + } + slash := strings.LastIndex(prefix, ".") + if slash < 0 { + return function + } + return function[slash+1:] +} + +func shortFile(file string) string { + _, name := path.Split(file) + if name == "" { + name = "???" + } + return name +} + // attrs returns the non-zero fields of s as a slice of attrs. // It is similar to a LogValue method, but we don't want Source // to implement LogValuer because it would be resolved before diff --git a/go/log/slog/record_test.go b/go/log/slog/record_test.go new file mode 100644 index 00000000..11f94b47 --- /dev/null +++ b/go/log/slog/record_test.go @@ -0,0 +1,75 @@ +// Copyright 2024 The searKing Author. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package slog + +import ( + "testing" +) + +func TestShortFunction(t *testing.T) { + for i, tt := range []struct { + function string + want string + }{ + { + "a", + "a", + }, + { + "a.b", + "b", + }, + { + "a.b.", + "", + }, + { + "a.b.c[...]", + "c[...]", + }, + { + "a.b.c[...].[...]....", + "c[...].[...]....", + }, + } { + + if got := shortFunction(tt.function); got != tt.want { + t.Errorf("#%d: got %s\nwant %s", i, got, tt.want) + } + } +} + +func TestShortFile(t *testing.T) { + for i, tt := range []struct { + file string + want string + }{ + { + "a.go", + "a.go", + }, + { + "a/b/c.go", + "c.go", + }, + { + "", + "???", + }, + { + "/", + "???", + }, + { + "a/b/", + "???", + }, + } { + + if got := shortFile(tt.file); got != tt.want { + t.Errorf("#%d: got %s\nwant %s", i, got, tt.want) + } + } +}