Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions log/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,16 @@ func PrintOrigins(print bool) {
} else {
atomic.StoreUint32(&locationEnabled, 0)
}
if print {
stackEnabled.Store(true)
}
}

// stackEnabled is an atomic flag controlling whether the log handler needs
// to store the callsite stack. This is needed in case any handler wants to
// print locations (locationEnabled), use vmodule, or print full stacks (BacktraceAt).
var stackEnabled atomic.Bool

// locationEnabled is an atomic flag controlling whether the terminal formatter
// should append the log locations too when printing entries.
var locationEnabled uint32
Expand Down
6 changes: 6 additions & 0 deletions log/handler_glog.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ func (h *GlogHandler) Vmodule(ruleset string) error {
h.siteCache = make(map[uintptr]Lvl)
atomic.StoreUint32(&h.override, uint32(len(filter)))

// Enable location storage (globally)
if len(h.patterns) > 0 {
stackEnabled.Store(true)
}
return nil
}

Expand Down Expand Up @@ -173,6 +177,8 @@ func (h *GlogHandler) BacktraceAt(location string) error {
h.location = location
atomic.StoreUint32(&h.backtrace, uint32(len(location)))

// Enable location storage (globally)
stackEnabled.Store(true)
return nil
}

Expand Down
9 changes: 6 additions & 3 deletions log/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,22 @@ type logger struct {
}

func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) {
l.h.Log(&Record{
record := &Record{
Time: time.Now(),
Lvl: lvl,
Msg: msg,
Ctx: newContext(l.ctx, ctx),
Call: stack.Caller(skip),
KeyNames: RecordKeyNames{
Time: timeKey,
Msg: msgKey,
Lvl: lvlKey,
Ctx: ctxKey,
},
})
}
if stackEnabled.Load() {
record.Call = stack.Caller(skip)
}
l.h.Log(record)
}

func (l *logger) New(ctx ...interface{}) Logger {
Expand Down
67 changes: 67 additions & 0 deletions log/logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package log

import (
"bytes"
"os"
"strings"
"testing"
)

// TestLoggingWithTrace checks that if BackTraceAt is set, then the
// gloghandler is capable of spitting out a stacktrace
func TestLoggingWithTrace(t *testing.T) {
defer stackEnabled.Store(stackEnabled.Load())
out := new(bytes.Buffer)
logger := New()
{
glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false)))
glog.Verbosity(LvlTrace)
if err := glog.BacktraceAt("logger_test.go:24"); err != nil {
t.Fatal(err)
}
logger.SetHandler(glog)
}
logger.Trace("a message", "foo", "bar") // Will be bumped to INFO
have := out.String()
if !strings.HasPrefix(have, "INFO") {
t.Fatalf("backtraceat should bump level to info: %s", have)
}
// The timestamp is locale-dependent, so we want to trim that off
// "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..."
have = strings.Split(have, "]")[1]
wantPrefix := " a message\n\ngoroutine"
if !strings.HasPrefix(have, wantPrefix) {
t.Errorf("\nhave: %q\nwant: %q\n", have, wantPrefix)
}
}

// TestLoggingWithVmodule checks that vmodule works.
func TestLoggingWithVmodule(t *testing.T) {
defer stackEnabled.Store(stackEnabled.Load())
out := new(bytes.Buffer)
logger := New()
{
glog := NewGlogHandler(StreamHandler(out, TerminalFormat(false)))
glog.Verbosity(LvlCrit)
logger.SetHandler(glog)
logger.Warn("This should not be seen", "ignored", "true")
glog.Vmodule("logger_test.go=5")
}
logger.Trace("a message", "foo", "bar")
have := out.String()
// The timestamp is locale-dependent, so we want to trim that off
// "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..."
have = strings.Split(have, "]")[1]
want := " a message foo=bar\n"
if have != want {
t.Errorf("\nhave: %q\nwant: %q\n", have, want)
}
}

func BenchmarkTraceLogging(b *testing.B) {
Root().SetHandler(LvlFilterHandler(LvlInfo, StreamHandler(os.Stderr, TerminalFormat(true))))
b.ResetTimer()
for i := 0; i < b.N; i++ {
Trace("a message", "v", i)
}
}