Skip to content

Fix: Slog Sublogger #6

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

Merged
merged 2 commits into from
Aug 30, 2024
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
2 changes: 1 addition & 1 deletion loggers/noop/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type Context struct {
l *Logger
}

func (c *Context) Logger() types.SubLogger {
func (c *Context) Logger() types.Logger {
return c.l
}

Expand Down
4 changes: 2 additions & 2 deletions loggers/noop/noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package noop

import "github.com/loopholelabs/logging/types"

var _ types.Logger = (*Logger)(nil)
var _ types.RootLogger = (*Logger)(nil)

type Logger struct {
level types.Level
Expand All @@ -22,7 +22,7 @@ func (s *Logger) Level() types.Level {
return s.level
}

func (s *Logger) SubLogger(string) types.SubLogger { return s }
func (s *Logger) SubLogger(string) types.Logger { return s }

func (s *Logger) With() types.Context {
return &Context{l: s}
Expand Down
7 changes: 5 additions & 2 deletions loggers/slog/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ type Context struct {
attrs []any
}

func (c *Context) Logger() types.SubLogger {
func (c *Context) Logger() types.Logger {
l := New(c.l.source, c.l.level, c.l.output)
l.logger = c.l.logger.With(c.attrs...)
if c.attrs != nil {
l.logger = l.logger.With(c.attrs...)
l.attrs = c.attrs
}
return l
}

Expand Down
18 changes: 12 additions & 6 deletions loggers/slog/slog.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/loopholelabs/logging/types"
)

var _ types.Logger = (*Logger)(nil)
var _ types.RootLogger = (*Logger)(nil)

var (
ReplaceAttr = func(_ []string, a slog.Attr) slog.Attr {
Expand All @@ -28,6 +28,7 @@ type Logger struct {
slogLevel *slog.LevelVar
output io.Writer
source string
attrs []any
}

func New(source string, level types.Level, output io.Writer) *Logger {
Expand All @@ -44,13 +45,12 @@ func newSlog(source string, slogLevel *slog.LevelVar, output io.Writer) *Logger
}).WithAttrs([]slog.Attr{
{Key: types.SourceKey, Value: slog.StringValue(source)},
}))
s := &Logger{
return &Logger{
logger: sl,
output: output,
slogLevel: slogLevel,
source: source,
}
return s
}

func (s *Logger) Level() types.Level {
Expand All @@ -77,14 +77,20 @@ func (s *Logger) SetLevel(level types.Level) {
s.slogLevel.Set(slogLevel)
}

func (s *Logger) SubLogger(source string) types.SubLogger {
func (s *Logger) SubLogger(source string) types.Logger {
sloglevel := new(slog.LevelVar)
sloglevel.Set(s.slogLevel.Level())
return newSlog(fmt.Sprintf("%s:%s", s.source, source), sloglevel, s.output)
l := newSlog(fmt.Sprintf("%s:%s", s.source, source), sloglevel, s.output)
l.level = s.level
if s.attrs != nil {
l.logger = l.logger.With(s.attrs...)
l.attrs = s.attrs
}
return l
}

func (s *Logger) With() types.Context {
return &Context{l: s}
return &Context{l: s, attrs: s.attrs}
}

func (s *Logger) Fatal() types.Event {
Expand Down
2 changes: 1 addition & 1 deletion loggers/zerolog/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type Context struct {
zeroCtx zerolog.Context
}

func (c *Context) Logger() types.SubLogger {
func (c *Context) Logger() types.Logger {
return &Logger{
logger: c.zeroCtx.Logger(),
source: c.l.source,
Expand Down
4 changes: 2 additions & 2 deletions loggers/zerolog/zerolog.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/loopholelabs/logging/types"
)

var _ types.Logger = (*Logger)(nil)
var _ types.RootLogger = (*Logger)(nil)

type Logger struct {
level types.Level
Expand Down Expand Up @@ -58,7 +58,7 @@ func (z *Logger) SetLevel(level types.Level) {
z.logger.Level(zerologLevel)
}

func (z *Logger) SubLogger(source string) types.SubLogger {
func (z *Logger) SubLogger(source string) types.Logger {
return &Logger{
logger: z.logger,
source: fmt.Sprintf("%s:%s", z.source, source),
Expand Down
4 changes: 2 additions & 2 deletions logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const (

// New creates a new logger based on the given kind, source, and output
// and a default level of Info.
func New(kind Kind, source string, output io.Writer) types.Logger {
func New(kind Kind, source string, output io.Writer) types.RootLogger {
switch kind {
case Noop:
return noop.New(types.InfoLevel)
Expand All @@ -38,7 +38,7 @@ func New(kind Kind, source string, output io.Writer) types.Logger {
}
}

func Test(t testing.TB, kind Kind, source string) types.Logger {
func Test(t testing.TB, kind Kind, source string) types.RootLogger {
switch kind {
case Noop:
return noop.New(types.InfoLevel)
Expand Down
159 changes: 159 additions & 0 deletions logging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,28 @@ func fillZerologTestFields(t *testing.T, format string) string {
return fmt.Sprintf(format, zeroTime.Format(zerolog.TimeFieldFormat), t.Name())
}

func fillZerologSubloggerTestFields(t *testing.T, format string, depth int) string {
args := make([]interface{}, 0, depth+2)
args = append(args, zeroTime.Format(zerolog.TimeFieldFormat), t.Name())
for i := 0; i < depth; i++ {
args = append(args, t.Name())
}
return fmt.Sprintf(format, args...)
}

func fillSlogTestFields(t *testing.T, format string) string {
return fmt.Sprintf(format, t.Name())
}

func fillSlogSubloggerTestFields(t *testing.T, format string, depth int) string {
args := make([]interface{}, 0, depth+1)
args = append(args, t.Name())
for i := 0; i < depth; i++ {
args = append(args, t.Name())
}
return fmt.Sprintf(format, args...)
}

func TestInfo(t *testing.T) {
t.Run("empty", func(t *testing.T) {
t.Run("noop", func(t *testing.T) {
Expand Down Expand Up @@ -199,3 +217,144 @@ func TestInfo(t *testing.T) {
})
})
}

func TestSubLoggers(t *testing.T) {
t.Run("depth=1", func(t *testing.T) {
t.Run("zerolog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Zerolog, t.Name(), out)
sublogger := log.SubLogger(t.Name())
sublogger.Info().Str("foo", "bar").Msg("")
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"time\":\"%s\",\"source\":\"%s:%s\",\"foo\":\"bar\"}\n", 1), out.String())
})

t.Run("slog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Slog, t.Name(), out)
sublogger := log.SubLogger(t.Name())
sublogger.Info().Str("foo", "bar").Msg("")
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s\" foo=bar\n", 1), out.String())
})
})

t.Run("depth=2", func(t *testing.T) {
t.Run("zerolog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Zerolog, t.Name(), out)
sublogger0 := log.SubLogger(t.Name())
sublogger1 := sublogger0.SubLogger(t.Name())
sublogger1.Info().Str("foo", "bar").Msg("")
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"time\":\"%s\",\"source\":\"%s:%s:%s\",\"foo\":\"bar\"}\n", 2), out.String())
})

t.Run("slog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Slog, t.Name(), out)
sublogger0 := log.SubLogger(t.Name())
sublogger1 := sublogger0.SubLogger(t.Name())
sublogger1.Info().Str("foo", "bar").Msg("")
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s:%s\" foo=bar\n", 2), out.String())
})
})

t.Run("depth=3", func(t *testing.T) {
t.Run("zerolog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Zerolog, t.Name(), out)
sublogger0 := log.SubLogger(t.Name())
sublogger1 := sublogger0.SubLogger(t.Name())
sublogger2 := sublogger1.SubLogger(t.Name())
sublogger2.Info().Str("foo", "bar").Msg("")
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"time\":\"%s\",\"source\":\"%s:%s:%s:%s\",\"foo\":\"bar\"}\n", 3), out.String())
})

t.Run("slog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Slog, t.Name(), out)
sublogger0 := log.SubLogger(t.Name())
sublogger1 := sublogger0.SubLogger(t.Name())
sublogger2 := sublogger1.SubLogger(t.Name())
sublogger2.Info().Str("foo", "bar").Msg("")
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s:%s:%s\" foo=bar\n", 3), out.String())
})
})

t.Run("with", func(t *testing.T) {

t.Run("before-depth=1", func(t *testing.T) {
t.Run("zerolog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Zerolog, t.Name(), out).With().Str("foo", "bar").Logger()
sublogger := log.SubLogger(t.Name())
sublogger.Info().Str("foo1", "bar1").Msg("")
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"foo\":\"bar\",\"time\":\"%s\",\"source\":\"%s:%s\",\"foo1\":\"bar1\"}\n", 1), out.String())
})

t.Run("slog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Slog, t.Name(), out).With().Str("foo", "bar").Logger()
sublogger := log.SubLogger(t.Name())
sublogger.Info().Str("foo1", "bar1").Msg("")
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s\" foo=bar foo1=bar1\n", 1), out.String())
})
})

t.Run("before-depth=2", func(t *testing.T) {
t.Run("zerolog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Zerolog, t.Name(), out).With().Str("foo", "bar").Logger()
sublogger0 := log.SubLogger(t.Name())
sublogger1 := sublogger0.SubLogger(t.Name())
sublogger1.Info().Str("foo1", "bar1").Msg("")
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"foo\":\"bar\",\"time\":\"%s\",\"source\":\"%s:%s:%s\",\"foo1\":\"bar1\"}\n", 2), out.String())
})

t.Run("slog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Slog, t.Name(), out).With().Str("foo", "bar").Logger()
sublogger0 := log.SubLogger(t.Name())
sublogger1 := sublogger0.SubLogger(t.Name())
sublogger1.Info().Str("foo1", "bar1").Msg("")
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s:%s\" foo=bar foo1=bar1\n", 2), out.String())
})
})

t.Run("after-depth=1", func(t *testing.T) {
t.Run("zerolog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Zerolog, t.Name(), out)
sublogger := log.SubLogger(t.Name()).With().Str("foo", "bar").Logger()
sublogger.Info().Str("foo1", "bar1").Msg("")
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"foo\":\"bar\",\"time\":\"%s\",\"source\":\"%s:%s\",\"foo1\":\"bar1\"}\n", 1), out.String())
})

t.Run("slog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Slog, t.Name(), out)
sublogger := log.SubLogger(t.Name()).With().Str("foo", "bar").Logger()
sublogger.Info().Str("foo1", "bar1").Msg("")
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s\" foo=bar foo1=bar1\n", 1), out.String())
})
})

t.Run("after-depth=2", func(t *testing.T) {
t.Run("zerolog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Zerolog, t.Name(), out)
sublogger0 := log.SubLogger(t.Name())
sublogger1 := sublogger0.SubLogger(t.Name()).With().Str("foo", "bar").Logger()
sublogger1.Info().Str("foo1", "bar1").Msg("")
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"foo\":\"bar\",\"time\":\"%s\",\"source\":\"%s:%s:%s\",\"foo1\":\"bar1\"}\n", 2), out.String())
})

t.Run("slog", func(t *testing.T) {
out := &bytes.Buffer{}
log := New(Slog, t.Name(), out)
sublogger0 := log.SubLogger(t.Name())
sublogger1 := sublogger0.SubLogger(t.Name()).With().Str("foo", "bar").Logger()
sublogger1.Info().Str("foo1", "bar1").Msg("")
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s:%s\" foo=bar foo1=bar1\n", 2), out.String())
})
})
})
}
10 changes: 5 additions & 5 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ const (
SourceKey = "source"
)

type Logger interface {
type RootLogger interface {
SetLevel(level Level)
SubLogger
Logger
}

type SubLogger interface {
type Logger interface {
Level() Level
SubLogger(source string) SubLogger
SubLogger(source string) Logger
With() Context

Fatal() Event
Expand All @@ -49,7 +49,7 @@ type Event interface {
type Context interface {
taggable[Context]

Logger() SubLogger
Logger() Logger
}

// taggable represents values that can receive structured fields.
Expand Down