[callpath] add depth (optional) parameter
For the 'callpath' verb, the output can be adjusted to limit the printing
the stack depth.
i.e. '%{callpath:3}' will print '~.a.b.c'
diff --git a/format.go b/format.go
index 1b11655..7160674 100644
--- a/format.go
+++ b/format.go
@@ -14,6 +14,7 @@
"path/filepath"
"regexp"
"runtime"
+ "strconv"
"strings"
"sync"
"time"
@@ -81,7 +82,7 @@
"s",
"s",
"s",
- "s",
+ "0",
"",
}
@@ -162,7 +163,7 @@
// %{message} Message (string)
// %{longfile} Full file name and line number: /a/b/c/d.go:23
// %{shortfile} Final file name element and line number: d.go:23
-// %{callpath} Callpath like main.a.b.c...c "..." meaning recursive call
+// %{callpath} Callpath like main.a.b.c...c "..." meaning recursive call ~. meaning truncated path
// %{color} ANSI color based on log level
//
// For normal types, the output can be customized by using the 'verbs' defined
@@ -179,6 +180,9 @@
// "%{color:bold}%{time:15:04:05} %{level:-8s}%{color:reset} %{message}" will
// just colorize the time and level, leaving the message uncolored.
//
+// For the 'callpath' verb, the output can be adjusted to limit the printing
+// the stack depth. i.e. '%{callpath:3}' will print '~.a.b.c'
+//
// Colors on Windows is unfortunately not supported right now and is currently
// a no-op.
//
@@ -216,12 +220,12 @@
}
// Handle layout customizations or use the default. If this is not for the
- // time or color formatting, we need to prefix with %.
+ // time, color formatting or callpath, we need to prefix with %.
layout := defaultVerbsLayout[verb]
if m[4] != -1 {
layout = format[m[4]:m[5]]
}
- if verb != fmtVerbTime && verb != fmtVerbLevelColor {
+ if verb != fmtVerbTime && verb != fmtVerbLevelColor && verb != fmtVerbCallpath {
layout = "%" + layout
}
@@ -275,6 +279,12 @@
output.Write([]byte(r.Time.Format(part.layout)))
} else if part.verb == fmtVerbLevelColor {
doFmtVerbLevelColor(part.layout, r.Level, output)
+ } else if part.verb == fmtVerbCallpath {
+ depth, err := strconv.Atoi(part.layout)
+ if err != nil {
+ depth = 0
+ }
+ output.Write([]byte(formatCallpath(calldepth+1, depth)))
} else {
var v interface{}
switch part.verb {
@@ -314,8 +324,6 @@
v = formatFuncName(part.verb, f.Name())
}
}
- case fmtVerbCallpath:
- v = formatCallpath(calldepth + 1)
default:
panic("unhandled format part")
}
@@ -351,14 +359,19 @@
panic("unexpected func formatter")
}
-func formatCallpath(calldepth int) string {
+func formatCallpath(calldepth int, depth int) string {
v := ""
callers := make([]uintptr, 64)
n := runtime.Callers(calldepth+2, callers)
oldPc := callers[n-1]
+ start := n - 3
+ if depth > 0 && start >= depth {
+ start = depth - 1
+ v += "~."
+ }
recursiveCall := false
- for i := n - 3; i >= 0; i-- {
+ for i := start; i >= 0; i-- {
pc := callers[i]
if oldPc == pc {
recursiveCall = true
@@ -369,7 +382,7 @@
recursiveCall = false
v += ".."
}
- if i < n-3 {
+ if i < start {
v += "."
}
if f := runtime.FuncForPC(pc); f != nil {
diff --git a/log_test.go b/log_test.go
index 9e1d18c..c7a645f 100644
--- a/log_test.go
+++ b/log_test.go
@@ -44,14 +44,13 @@
rec(log, r-1)
}
-func TestLogCallpath(t *testing.T) {
+func testCallpath(t *testing.T, format string, expect string) {
buf := &bytes.Buffer{}
SetBackend(NewLogBackend(buf, "", log.Lshortfile))
- SetFormatter(MustStringFormatter("%{callpath} %{message}"))
- // SetFormatter(MustStringFormatter("%{callpath} %{message}"))
+ SetFormatter(MustStringFormatter(format))
- log := MustGetLogger("test")
- rec(log, 6)
+ logger := MustGetLogger("test")
+ rec(logger, 6)
parts := strings.SplitN(buf.String(), " ", 3)
@@ -60,7 +59,7 @@
t.Errorf("incorrect filename: %s", parts[0])
}
// Verify that the correct callpath is registered by go-logging
- if !strings.HasPrefix(parts[1], "TestLogCallpath.rec...rec.a.b.c") {
+ if !strings.HasPrefix(parts[1], expect) {
t.Errorf("incorrect callpath: %s", parts[1])
}
// Verify that the correct message is registered by go-logging
@@ -69,6 +68,15 @@
}
}
+func TestLogCallpath(t *testing.T) {
+ testCallpath(t, "%{callpath} %{message}", "TestLogCallpath.testCallpath.rec...rec.a.b.c")
+ testCallpath(t, "%{callpath:-1} %{message}", "TestLogCallpath.testCallpath.rec...rec.a.b.c")
+ testCallpath(t, "%{callpath:0} %{message}", "TestLogCallpath.testCallpath.rec...rec.a.b.c")
+ testCallpath(t, "%{callpath:1} %{message}", "~.c")
+ testCallpath(t, "%{callpath:2} %{message}", "~.b.c")
+ testCallpath(t, "%{callpath:3} %{message}", "~.a.b.c")
+}
+
func BenchmarkLogMemoryBackendIgnored(b *testing.B) {
backend := SetBackend(NewMemoryBackend(1024))
backend.SetLevel(INFO, "")