Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Entry PrintDepth family functions. #1297

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
93 changes: 84 additions & 9 deletions entry.go
Expand Up @@ -176,7 +176,7 @@ func getPackageName(f string) string {
}

// getCaller retrieves the name of the first non-logrus calling function
func getCaller() *runtime.Frame {
func getCaller(skip int) *runtime.Frame {
// cache this package's fully-qualified name
callerInitOnce.Do(func() {
pcs := make([]uintptr, maximumCallerDepth)
Expand All @@ -199,12 +199,22 @@ func getCaller() *runtime.Frame {
depth := runtime.Callers(minimumCallerDepth, pcs)
frames := runtime.CallersFrames(pcs[:depth])

var skippedAllLogrus bool
for f, again := frames.Next(); again; f, again = frames.Next() {
pkg := getPackageName(f.Function)
if !skippedAllLogrus {
pkg := getPackageName(f.Function)
// If the caller isn't part of this package, we're done skilled all logrus frames
if pkg != logrusPackage {
skippedAllLogrus = true
}
}

// If the caller isn't part of this package, we're done
if pkg != logrusPackage {
return &f //nolint:scopelint
// skip non-logurs frames
if skippedAllLogrus {
if skip <= 0 {
return &f //nolint:scopelint
}
skip--
}
}

Expand All @@ -218,7 +228,7 @@ func (entry Entry) HasCaller() (has bool) {
entry.Caller != nil
}

func (entry *Entry) log(level Level, msg string) {
func (entry *Entry) log(level Level, skip int, msg string) {
var buffer *bytes.Buffer

newEntry := entry.Dup()
Expand All @@ -235,8 +245,8 @@ func (entry *Entry) log(level Level, msg string) {
bufPool := newEntry.getBufferPool()
newEntry.Logger.mu.Unlock()

if reportCaller {
newEntry.Caller = getCaller()
if reportCaller && newEntry.Caller == nil {
newEntry.Caller = getCaller(skip)
}

newEntry.fireHooks()
Expand Down Expand Up @@ -301,7 +311,7 @@ func (entry *Entry) write() {
// For this behaviour Entry.Panic or Entry.Fatal should be used instead.
func (entry *Entry) Log(level Level, args ...interface{}) {
if entry.Logger.IsLevelEnabled(level) {
entry.log(level, fmt.Sprint(args...))
entry.log(level, 0, fmt.Sprint(args...))
}
}

Expand Down Expand Up @@ -432,6 +442,71 @@ func (entry *Entry) Panicln(args ...interface{}) {
entry.Logln(PanicLevel, args...)
}

// Entry PrintDepth family functions

// LogDepth acts as Info but uses depth to determine which call frame to log.
// LogDepth(InfoLevel, 0, "msg") is the same as Log(InfoLevel, "msg").
func (entry *Entry) LogDepth(level Level, depth int, args ...interface{}) {
if entry.Logger.IsLevelEnabled(level) {
entry.log(level, depth, fmt.Sprint(args...))
}
}

// TraceDepth acts as Trace but uses depth to determine which call frame to log.
// TraceDepth(0, "msg") is the same as Trace("msg").
func (entry *Entry) TraceDepth(depth int, args ...interface{}) {
entry.LogDepth(TraceLevel, depth+1, args...)
}

// DebugDepth acts as Debug but uses depth to determine which call frame to log.
// DebugDepth(0, "msg") is the same as Debug("msg").
func (entry *Entry) DebugDepth(depth int, args ...interface{}) {
entry.LogDepth(DebugLevel, depth+1, args...)
}

// PrintDepth acts as Print but uses depth to determine which call frame to log.
// PrintDepth(0, "msg") is the same as Print("msg").
func (entry *Entry) PrintDepth(depth int, args ...interface{}) {
entry.InfoDepth(depth+1, args...)
}

// InfoDepth acts as Info but uses depth to determine which call frame to log.
// InfoDepth(0, "msg") is the same as Info("msg").
func (entry *Entry) InfoDepth(depth int, args ...interface{}) {
entry.LogDepth(InfoLevel, depth+1, args...)
}

// WarnDepth acts as Warn but uses depth to determine which call frame to log.
// WarnDepth(0, "msg") is the same as Warn("msg").
func (entry *Entry) WarnDepth(depth int, args ...interface{}) {
entry.LogDepth(WarnLevel, depth+1, args...)
}

// WarningDepth acts as Warning but uses depth to determine which call frame to log.
// WarningDepth(0, "msg") is the same as Warning("msg").
func (entry *Entry) WarningDepth(depth int, args ...interface{}) {
entry.WarnDepth(depth+1, args...)
}

// ErrorDepth acts as Trace but uses depth to determine which call frame to log.
// ErrorDepth(0, "msg") is the same as Error("msg").
func (entry *Entry) ErrorDepth(depth int, args ...interface{}) {
entry.LogDepth(ErrorLevel, depth+1, args...)
}

// FatalDepth acts as Fatal but uses depth to determine which call frame to log.
// FatalDepth(0, "msg") is the same as Fatal("msg").
func (entry *Entry) FatalDepth(depth int, args ...interface{}) {
entry.LogDepth(FatalLevel, depth+1, args...)
entry.Logger.Exit(1)
}

// PanicDepth acts as Panic but uses depth to determine which call frame to log.
// PanicDepth(0, "msg") is the same as Trace("msg").
func (entry *Entry) PanicDepth(depth int, args ...interface{}) {
entry.LogDepth(PanicLevel, depth+1, args...)
}

// Sprintlnn => Sprint no newline. This is to get the behavior of how
// fmt.Sprintln where spaces are always added between operands, regardless of
// their type. Instead of vendoring the Sprintln implementation to spare a
Expand Down
81 changes: 81 additions & 0 deletions entry_test.go
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"sync"
"testing"
"time"

Expand Down Expand Up @@ -276,3 +277,83 @@ func TestEntryReportCallerRace(t *testing.T) {
entry.Info("should not race")
}()
}

func TestEntryLogDepthLevel(t *testing.T) {
logger := New()
buffer := &bytes.Buffer{}
logger.Out = buffer
logger.SetLevel(InfoLevel)
logger.SetReportCaller(true)
entry := NewEntry(logger)

entry.LogDepth(DebugLevel, 0, "debug")
assert.NotContains(t, buffer.String(), "debug")
assert.NotContains(t, buffer.String(), "testing.tRunner")

buffer.Reset()
entry.LogDepth(WarnLevel, 0, "warn")
assert.Contains(t, buffer.String(), "warn")
assert.Contains(t, buffer.String(), "testing.tRunner")

{
var once sync.Once
once.Do(func() {
buffer.Reset()
entry.LogDepth(WarnLevel, 0, "warn")
})
assert.Contains(t, buffer.String(), "warn")
assert.NotContains(t, buffer.String(), "testing.tRunner")
assert.Contains(t, buffer.String(), "sync.(*Once).doSlow")
}
{
// (0) sync.(*Once).doSlow at once.go:68
// (1) sync.(*Once).Do at once.go:59
// (2) github.com/sirupsen/logrus.TestEntryLogDepthLevel at entry_test.go:321
// (3) testing.tRunner at testing.go:1259
// (4) testing.(*T).Run·dwrap·21 at testing.go:1306
// (5) runtime.goexit at asm_amd64.s:1581
var once sync.Once
once.Do(func() {
buffer.Reset()
entry.LogDepth(WarnLevel, 1, "warn")
})
assert.Contains(t, buffer.String(), "warn")
assert.Contains(t, buffer.String(), "sync.(*Once).Do")
assert.NotContains(t, buffer.String(), "testing.tRunner")
}

{
// (0) sync.(*Once).doSlow at once.go:68
// (1) sync.(*Once).Do at once.go:59
// (2) github.com/sirupsen/logrus.TestEntryLogDepthLevel at entry_test.go:321
// (3) testing.tRunner at testing.go:1259
// (4) testing.(*T).Run·dwrap·21 at testing.go:1306
// (5) runtime.goexit at asm_amd64.s:1581
var once sync.Once
once.Do(func() {
buffer.Reset()
entry.LogDepth(WarnLevel, 1, "warn")
})
assert.Contains(t, buffer.String(), "warn")
assert.Contains(t, buffer.String(), "sync.(*Once).Do")
assert.NotContains(t, buffer.String(), "testing.tRunner")
}

{
// (0) sync.(*Once).doSlow at once.go:68
// (1) sync.(*Once).Do at once.go:59
// (2) github.com/sirupsen/logrus.TestEntryLogDepthLevel at entry_test.go:321
// (3) testing.tRunner at testing.go:1259
// (4) testing.(*T).Run·dwrap·21 at testing.go:1306
// (5) runtime.goexit at asm_amd64.s:1581
var once sync.Once
once.Do(func() {
buffer.Reset()
entry.LogDepth(WarnLevel, 2, "warn")
})
assert.Contains(t, buffer.String(), "warn")
assert.Contains(t, buffer.String(), "TestEntryLogDepthLevel")
assert.NotContains(t, buffer.String(), "testing.tRunner")
}

}