From a7f793bd4672a4a9faf7c6bd37545dff0082bdda Mon Sep 17 00:00:00 2001 From: zonescape <44441590+zonescape@users.noreply.github.com> Date: Fri, 15 Mar 2019 02:40:59 +0300 Subject: [PATCH] Refactor caller reporting (#4) * refactor caller reporting * simplify handling of the inability to request caller info --- logger.go | 97 +++++++++++++++++++++++++++++++++++--------------- logger_test.go | 17 +++++++++ 2 files changed, 86 insertions(+), 28 deletions(-) diff --git a/logger.go b/logger.go index 5521b53..7558041 100644 --- a/logger.go +++ b/logger.go @@ -74,34 +74,10 @@ func (l *Logger) logf(format string, args ...interface{}) { bld.WriteString(l.formatLevel(lv)) bld.WriteString(" ") - if l.callerFile || l.callerFunc || l.callerPkg { - if pc, file, line, ok := runtime.Caller(2); ok { - - funcName, fileInfo := "", "" - - if l.callerFunc { - funcNameElems := strings.Split(runtime.FuncForPC(pc).Name(), "/") - funcName = funcNameElems[len(funcNameElems)-1] - } - - if l.callerFile { - fnameElems := strings.Split(file, "/") - fileInfo = fmt.Sprintf("%s:%d", strings.Join(fnameElems[len(fnameElems)-2:], "/"), line) - if l.callerFunc { - fileInfo += " " - } - } - // callerPkg only if no other callers - if l.callerPkg && !l.callerFile && !l.callerFunc { - file = l.ignoreCaller(file) - _, fileInfo = path.Split(path.Dir(file)) - if l.callerFunc { - fileInfo += " " - } - } - srcFileInfo := fmt.Sprintf("{%s%s} ", fileInfo, funcName) - bld.WriteString(srcFileInfo) - } + if caller := l.reportCaller(2); caller != "" { + bld.WriteString("{") + bld.WriteString(caller) + bld.WriteString("} ") } bld.WriteString(msg) //nolint @@ -124,6 +100,71 @@ func (l *Logger) logf(format string, args ...interface{}) { l.lock.Unlock() } +// calldepth 0 identifying the caller of reportCaller() +func (l *Logger) reportCaller(calldepth int) string { + if !(l.callerFile || l.callerFunc || l.callerPkg) { + return "" + } + + filePath, line, funcName := l.caller(calldepth + 1) + if (filePath == "") || (line <= 0) || (funcName == "") { + return "???" + } + + // callerPkg only if no other callers + if l.callerPkg && !l.callerFile && !l.callerFunc { + pkgInfo := l.ignoreCaller(filePath) + _, pkgInfo = path.Split(path.Dir(pkgInfo)) + return pkgInfo + } + + res := "" + + if l.callerFile { + fileInfo := filePath + if pathElems := strings.Split(filePath, "/"); len(pathElems) > 2 { + fileInfo = strings.Join(pathElems[len(pathElems)-2:], "/") + } + res += fmt.Sprintf("%s:%d", fileInfo, line) + if l.callerFunc { + res += " " + } + } + + if l.callerFunc { + funcNameElems := strings.Split(funcName, "/") + funcInfo := funcNameElems[len(funcNameElems)-1] + res += funcInfo + } + + return res +} + +// caller is a modified version of runtime.Caller() +// calldepth 0 identifying the caller of caller() +// file looks like: +// /go/src/github.com/go-pkgz/lgr/logger.go +// file is an empty string if not known. +// funcName looks like: +// main.Test +// foo/bar.Test +// foo/bar.Test.func1 +// foo/bar.(*Bar).Test +// foo/bar.glob..func1 +// funcName is an empty string if not known. +// line is a zero if not known. +func (l *Logger) caller(calldepth int) (file string, line int, funcName string) { + pcs := make([]uintptr, 1) + n := runtime.Callers(calldepth+2, pcs) + if n != 1 { + return "", 0, "" + } + + frame, _ := runtime.CallersFrames(pcs).Next() + + return frame.File, frame.Line, frame.Function +} + func (l *Logger) ignoreCaller(p string) string { for _, s := range l.ignoredPkgCallers { if strings.Contains(p, "/"+s+"/") { diff --git a/logger_test.go b/logger_test.go index 38ddd5d..5df196e 100644 --- a/logger_test.go +++ b/logger_test.go @@ -216,6 +216,23 @@ func TestLoggerConcurrent(t *testing.T) { assert.Equal(t, "", rerr.String()) } +func TestCaller(t *testing.T) { + var l *Logger + + filePath, line, funcName := l.caller(0) + assert.True(t, strings.HasSuffix(filePath, "go-pkgz/lgr/logger_test.go"), filePath) + assert.Equal(t, 222, line) + assert.Equal(t, funcName, "github.com/go-pkgz/lgr.TestCaller") + + f := func() { + filePath, line, funcName = l.caller(1) + } + f() + assert.True(t, strings.HasSuffix(filePath, "go-pkgz/lgr/logger_test.go"), filePath) + assert.Equal(t, 230, line) + assert.Equal(t, funcName, "github.com/go-pkgz/lgr.TestCaller") +} + func BenchmarkNoDbg(b *testing.B) { rout, rerr := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}) l := New(Out(rout), Err(rerr))