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

Logging: detect log level through layers of wrappers #10646

Open
wants to merge 2 commits into
base: main
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions pkg/util/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,16 @@ const (
errorLevel
)

type leveledLogger interface {
level() logLevel
type privateLevelDetector struct {
string
logLevel
}

var _ leveledLogger = levelFilter{}
func (x *privateLevelDetector) String() string {
return x.string
}

var _ log.Logger = levelFilter{}

// Pass through Logger and implement the DebugEnabled interface that spanlogger looks for.
type levelFilter struct {
Expand All @@ -93,8 +98,15 @@ func newFilter(logger log.Logger, lvl dslog.Level) log.Logger {
}
}

func (f levelFilter) level() logLevel {
return f.lvl
// If we are called with a special magic struct, use it to pass back the logging level.
func (f levelFilter) Log(keyvals ...interface{}) error {
if len(keyvals) > 0 {
if x, ok := keyvals[len(keyvals)-1].(*privateLevelDetector); ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm shocked the type assertion doesn't make it significantly slower

x.logLevel = f.lvl
return nil
}
}
return f.Logger.Log(keyvals...)
}

func (f *levelFilter) DebugEnabled() bool {
Expand Down
26 changes: 13 additions & 13 deletions pkg/util/log/slogadapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ import (
// SlogFromGoKit returns slog adapter for logger.
func SlogFromGoKit(logger log.Logger) *slog.Logger {
var sl slog.Level
x, ok := logger.(leveledLogger)
if !ok {
x := privateLevelDetector{
string: "This struct is expected to be used with levelLogger only",
logLevel: debugLevel,
}
logger.Log("test", &x)
switch x.logLevel {
case infoLevel:
sl = slog.LevelInfo
case warnLevel:
sl = slog.LevelWarn
case errorLevel:
sl = slog.LevelError
default:
sl = slog.LevelDebug
} else {
switch x.level() {
case infoLevel:
sl = slog.LevelInfo
case warnLevel:
sl = slog.LevelWarn
case errorLevel:
sl = slog.LevelError
default:
sl = slog.LevelDebug
}
}

lvl := slog.LevelVar{}
Expand Down
29 changes: 29 additions & 0 deletions pkg/util/log/slogadapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"log/slog"
"testing"

"github.com/go-kit/log"
"github.com/go-kit/log/level"
dslog "github.com/grafana/dskit/log"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -61,6 +62,7 @@ func TestSlogFromGoKit(t *testing.T) {

t.Run("enabled for the right slog levels when go-kit level not configured", func(t *testing.T) {
mLogger := &mockLogger{}
mLogger.On("Log", "test", mock.AnythingOfType("*log.privateLevelDetector")).Return(nil) // Probing call from SlogFromGoKit.
slogger := SlogFromGoKit(mLogger)

for _, sl := range slogLevels {
Expand All @@ -70,6 +72,7 @@ func TestSlogFromGoKit(t *testing.T) {

t.Run("wraps go-kit logger", func(*testing.T) {
mLogger := &mockLogger{}
mLogger.On("Log", "test", mock.AnythingOfType("*log.privateLevelDetector")).Return(nil) // Probing call from SlogFromGoKit.
slogger := SlogFromGoKit(mLogger)

for _, l := range levels {
Expand All @@ -90,3 +93,29 @@ func TestSlogFromGoKit(t *testing.T) {
}
})
}

func BenchmarkSlogFromGoKit(b *testing.B) {
// Set up a logger with a stack of wrappers, similar to what tsdb gets.
var lvl dslog.Level
_ = lvl.Set("info")
var logger log.Logger
logger = noopLogger{}
logger = newFilter(logger, lvl)
logger = WithUserID("userID", logger)

sl := SlogFromGoKit(logger)
b.Run("log", func(b *testing.B) {
for i := 0; i < b.N; i++ {
sl.Info("msg", "foo", "bar", "more", "data")
}
})
b.Run("debuglog", func(b *testing.B) {
for i := 0; i < b.N; i++ {
sl.Debug("msg", "foo", "bar", "more", "data")
}
})
}

type noopLogger struct{}

func (noopLogger) Log(...interface{}) error { return nil }
Loading