Skip to content

Commit

Permalink
Merge pull request #89 from nplanel/format-callpath-depth
Browse files Browse the repository at this point in the history
[callpath] add depth (optional) parameter
  • Loading branch information
op committed Feb 17, 2016
2 parents f9dfb79 + 845ad39 commit d2e44aa
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 15 deletions.
31 changes: 22 additions & 9 deletions format.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -81,7 +82,7 @@ var defaultVerbsLayout = []string{
"s",
"s",
"s",
"s",
"0",
"",
}

Expand Down Expand Up @@ -162,7 +163,7 @@ type stringFormatter struct {
// %{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
Expand All @@ -179,6 +180,9 @@ type stringFormatter struct {
// "%{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.
//
Expand Down Expand Up @@ -216,12 +220,12 @@ func NewStringFormatter(format string) (Formatter, error) {
}

// 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
}

Expand Down Expand Up @@ -275,6 +279,12 @@ func (f *stringFormatter) Format(calldepth int, r *Record, output io.Writer) err
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 {
Expand Down Expand Up @@ -314,8 +324,6 @@ func (f *stringFormatter) Format(calldepth int, r *Record, output io.Writer) err
v = formatFuncName(part.verb, f.Name())
}
}
case fmtVerbCallpath:
v = formatCallpath(calldepth + 1)
default:
panic("unhandled format part")
}
Expand Down Expand Up @@ -351,14 +359,19 @@ func formatFuncName(v fmtVerb, f string) string {
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
Expand All @@ -369,7 +382,7 @@ func formatCallpath(calldepth int) string {
recursiveCall = false
v += ".."
}
if i < n-3 {
if i < start {
v += "."
}
if f := runtime.FuncForPC(pc); f != nil {
Expand Down
20 changes: 14 additions & 6 deletions log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,13 @@ func rec(log *Logger, r int) {
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)

Expand All @@ -60,7 +59,7 @@ func TestLogCallpath(t *testing.T) {
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
Expand All @@ -69,6 +68,15 @@ func TestLogCallpath(t *testing.T) {
}
}

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, "")
Expand Down

0 comments on commit d2e44aa

Please sign in to comment.