Skip to content

Commit ef47fb8

Browse files
authored
Fix: Slog Sublogger (#6)
* Fixing slog sublogger and adding comprehensive test cases Signed-off-by: Shivansh Vij <shivanshvij@loopholelabs.io> * Renaming `SubLogger` to `Logger` and original `Logger` to `RootLogger` Signed-off-by: Shivansh Vij <shivanshvij@loopholelabs.io> --------- Signed-off-by: Shivansh Vij <shivanshvij@loopholelabs.io>
1 parent ba1e898 commit ef47fb8

File tree

9 files changed

+189
-21
lines changed

9 files changed

+189
-21
lines changed

loggers/noop/context.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type Context struct {
1414
l *Logger
1515
}
1616

17-
func (c *Context) Logger() types.SubLogger {
17+
func (c *Context) Logger() types.Logger {
1818
return c.l
1919
}
2020

loggers/noop/noop.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package noop
44

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

7-
var _ types.Logger = (*Logger)(nil)
7+
var _ types.RootLogger = (*Logger)(nil)
88

99
type Logger struct {
1010
level types.Level
@@ -22,7 +22,7 @@ func (s *Logger) Level() types.Level {
2222
return s.level
2323
}
2424

25-
func (s *Logger) SubLogger(string) types.SubLogger { return s }
25+
func (s *Logger) SubLogger(string) types.Logger { return s }
2626

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

loggers/slog/context.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ type Context struct {
1616
attrs []any
1717
}
1818

19-
func (c *Context) Logger() types.SubLogger {
19+
func (c *Context) Logger() types.Logger {
2020
l := New(c.l.source, c.l.level, c.l.output)
21-
l.logger = c.l.logger.With(c.attrs...)
21+
if c.attrs != nil {
22+
l.logger = l.logger.With(c.attrs...)
23+
l.attrs = c.attrs
24+
}
2225
return l
2326
}
2427

loggers/slog/slog.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"github.com/loopholelabs/logging/types"
1111
)
1212

13-
var _ types.Logger = (*Logger)(nil)
13+
var _ types.RootLogger = (*Logger)(nil)
1414

1515
var (
1616
ReplaceAttr = func(_ []string, a slog.Attr) slog.Attr {
@@ -28,6 +28,7 @@ type Logger struct {
2828
slogLevel *slog.LevelVar
2929
output io.Writer
3030
source string
31+
attrs []any
3132
}
3233

3334
func New(source string, level types.Level, output io.Writer) *Logger {
@@ -44,13 +45,12 @@ func newSlog(source string, slogLevel *slog.LevelVar, output io.Writer) *Logger
4445
}).WithAttrs([]slog.Attr{
4546
{Key: types.SourceKey, Value: slog.StringValue(source)},
4647
}))
47-
s := &Logger{
48+
return &Logger{
4849
logger: sl,
4950
output: output,
5051
slogLevel: slogLevel,
5152
source: source,
5253
}
53-
return s
5454
}
5555

5656
func (s *Logger) Level() types.Level {
@@ -77,14 +77,20 @@ func (s *Logger) SetLevel(level types.Level) {
7777
s.slogLevel.Set(slogLevel)
7878
}
7979

80-
func (s *Logger) SubLogger(source string) types.SubLogger {
80+
func (s *Logger) SubLogger(source string) types.Logger {
8181
sloglevel := new(slog.LevelVar)
8282
sloglevel.Set(s.slogLevel.Level())
83-
return newSlog(fmt.Sprintf("%s:%s", s.source, source), sloglevel, s.output)
83+
l := newSlog(fmt.Sprintf("%s:%s", s.source, source), sloglevel, s.output)
84+
l.level = s.level
85+
if s.attrs != nil {
86+
l.logger = l.logger.With(s.attrs...)
87+
l.attrs = s.attrs
88+
}
89+
return l
8490
}
8591

8692
func (s *Logger) With() types.Context {
87-
return &Context{l: s}
93+
return &Context{l: s, attrs: s.attrs}
8894
}
8995

9096
func (s *Logger) Fatal() types.Event {

loggers/zerolog/context.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type Context struct {
1616
zeroCtx zerolog.Context
1717
}
1818

19-
func (c *Context) Logger() types.SubLogger {
19+
func (c *Context) Logger() types.Logger {
2020
return &Logger{
2121
logger: c.zeroCtx.Logger(),
2222
source: c.l.source,

loggers/zerolog/zerolog.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"github.com/loopholelabs/logging/types"
1212
)
1313

14-
var _ types.Logger = (*Logger)(nil)
14+
var _ types.RootLogger = (*Logger)(nil)
1515

1616
type Logger struct {
1717
level types.Level
@@ -58,7 +58,7 @@ func (z *Logger) SetLevel(level types.Level) {
5858
z.logger.Level(zerologLevel)
5959
}
6060

61-
func (z *Logger) SubLogger(source string) types.SubLogger {
61+
func (z *Logger) SubLogger(source string) types.Logger {
6262
return &Logger{
6363
logger: z.logger,
6464
source: fmt.Sprintf("%s:%s", z.source, source),

logging.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const (
2525

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

41-
func Test(t testing.TB, kind Kind, source string) types.Logger {
41+
func Test(t testing.TB, kind Kind, source string) types.RootLogger {
4242
switch kind {
4343
case Noop:
4444
return noop.New(types.InfoLevel)

logging_test.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,28 @@ func fillZerologTestFields(t *testing.T, format string) string {
3838
return fmt.Sprintf(format, zeroTime.Format(zerolog.TimeFieldFormat), t.Name())
3939
}
4040

41+
func fillZerologSubloggerTestFields(t *testing.T, format string, depth int) string {
42+
args := make([]interface{}, 0, depth+2)
43+
args = append(args, zeroTime.Format(zerolog.TimeFieldFormat), t.Name())
44+
for i := 0; i < depth; i++ {
45+
args = append(args, t.Name())
46+
}
47+
return fmt.Sprintf(format, args...)
48+
}
49+
4150
func fillSlogTestFields(t *testing.T, format string) string {
4251
return fmt.Sprintf(format, t.Name())
4352
}
4453

54+
func fillSlogSubloggerTestFields(t *testing.T, format string, depth int) string {
55+
args := make([]interface{}, 0, depth+1)
56+
args = append(args, t.Name())
57+
for i := 0; i < depth; i++ {
58+
args = append(args, t.Name())
59+
}
60+
return fmt.Sprintf(format, args...)
61+
}
62+
4563
func TestInfo(t *testing.T) {
4664
t.Run("empty", func(t *testing.T) {
4765
t.Run("noop", func(t *testing.T) {
@@ -199,3 +217,144 @@ func TestInfo(t *testing.T) {
199217
})
200218
})
201219
}
220+
221+
func TestSubLoggers(t *testing.T) {
222+
t.Run("depth=1", func(t *testing.T) {
223+
t.Run("zerolog", func(t *testing.T) {
224+
out := &bytes.Buffer{}
225+
log := New(Zerolog, t.Name(), out)
226+
sublogger := log.SubLogger(t.Name())
227+
sublogger.Info().Str("foo", "bar").Msg("")
228+
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"time\":\"%s\",\"source\":\"%s:%s\",\"foo\":\"bar\"}\n", 1), out.String())
229+
})
230+
231+
t.Run("slog", func(t *testing.T) {
232+
out := &bytes.Buffer{}
233+
log := New(Slog, t.Name(), out)
234+
sublogger := log.SubLogger(t.Name())
235+
sublogger.Info().Str("foo", "bar").Msg("")
236+
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s\" foo=bar\n", 1), out.String())
237+
})
238+
})
239+
240+
t.Run("depth=2", func(t *testing.T) {
241+
t.Run("zerolog", func(t *testing.T) {
242+
out := &bytes.Buffer{}
243+
log := New(Zerolog, t.Name(), out)
244+
sublogger0 := log.SubLogger(t.Name())
245+
sublogger1 := sublogger0.SubLogger(t.Name())
246+
sublogger1.Info().Str("foo", "bar").Msg("")
247+
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"time\":\"%s\",\"source\":\"%s:%s:%s\",\"foo\":\"bar\"}\n", 2), out.String())
248+
})
249+
250+
t.Run("slog", func(t *testing.T) {
251+
out := &bytes.Buffer{}
252+
log := New(Slog, t.Name(), out)
253+
sublogger0 := log.SubLogger(t.Name())
254+
sublogger1 := sublogger0.SubLogger(t.Name())
255+
sublogger1.Info().Str("foo", "bar").Msg("")
256+
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s:%s\" foo=bar\n", 2), out.String())
257+
})
258+
})
259+
260+
t.Run("depth=3", func(t *testing.T) {
261+
t.Run("zerolog", func(t *testing.T) {
262+
out := &bytes.Buffer{}
263+
log := New(Zerolog, t.Name(), out)
264+
sublogger0 := log.SubLogger(t.Name())
265+
sublogger1 := sublogger0.SubLogger(t.Name())
266+
sublogger2 := sublogger1.SubLogger(t.Name())
267+
sublogger2.Info().Str("foo", "bar").Msg("")
268+
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"time\":\"%s\",\"source\":\"%s:%s:%s:%s\",\"foo\":\"bar\"}\n", 3), out.String())
269+
})
270+
271+
t.Run("slog", func(t *testing.T) {
272+
out := &bytes.Buffer{}
273+
log := New(Slog, t.Name(), out)
274+
sublogger0 := log.SubLogger(t.Name())
275+
sublogger1 := sublogger0.SubLogger(t.Name())
276+
sublogger2 := sublogger1.SubLogger(t.Name())
277+
sublogger2.Info().Str("foo", "bar").Msg("")
278+
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s:%s:%s\" foo=bar\n", 3), out.String())
279+
})
280+
})
281+
282+
t.Run("with", func(t *testing.T) {
283+
284+
t.Run("before-depth=1", func(t *testing.T) {
285+
t.Run("zerolog", func(t *testing.T) {
286+
out := &bytes.Buffer{}
287+
log := New(Zerolog, t.Name(), out).With().Str("foo", "bar").Logger()
288+
sublogger := log.SubLogger(t.Name())
289+
sublogger.Info().Str("foo1", "bar1").Msg("")
290+
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"foo\":\"bar\",\"time\":\"%s\",\"source\":\"%s:%s\",\"foo1\":\"bar1\"}\n", 1), out.String())
291+
})
292+
293+
t.Run("slog", func(t *testing.T) {
294+
out := &bytes.Buffer{}
295+
log := New(Slog, t.Name(), out).With().Str("foo", "bar").Logger()
296+
sublogger := log.SubLogger(t.Name())
297+
sublogger.Info().Str("foo1", "bar1").Msg("")
298+
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s\" foo=bar foo1=bar1\n", 1), out.String())
299+
})
300+
})
301+
302+
t.Run("before-depth=2", func(t *testing.T) {
303+
t.Run("zerolog", func(t *testing.T) {
304+
out := &bytes.Buffer{}
305+
log := New(Zerolog, t.Name(), out).With().Str("foo", "bar").Logger()
306+
sublogger0 := log.SubLogger(t.Name())
307+
sublogger1 := sublogger0.SubLogger(t.Name())
308+
sublogger1.Info().Str("foo1", "bar1").Msg("")
309+
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"foo\":\"bar\",\"time\":\"%s\",\"source\":\"%s:%s:%s\",\"foo1\":\"bar1\"}\n", 2), out.String())
310+
})
311+
312+
t.Run("slog", func(t *testing.T) {
313+
out := &bytes.Buffer{}
314+
log := New(Slog, t.Name(), out).With().Str("foo", "bar").Logger()
315+
sublogger0 := log.SubLogger(t.Name())
316+
sublogger1 := sublogger0.SubLogger(t.Name())
317+
sublogger1.Info().Str("foo1", "bar1").Msg("")
318+
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s:%s\" foo=bar foo1=bar1\n", 2), out.String())
319+
})
320+
})
321+
322+
t.Run("after-depth=1", func(t *testing.T) {
323+
t.Run("zerolog", func(t *testing.T) {
324+
out := &bytes.Buffer{}
325+
log := New(Zerolog, t.Name(), out)
326+
sublogger := log.SubLogger(t.Name()).With().Str("foo", "bar").Logger()
327+
sublogger.Info().Str("foo1", "bar1").Msg("")
328+
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"foo\":\"bar\",\"time\":\"%s\",\"source\":\"%s:%s\",\"foo1\":\"bar1\"}\n", 1), out.String())
329+
})
330+
331+
t.Run("slog", func(t *testing.T) {
332+
out := &bytes.Buffer{}
333+
log := New(Slog, t.Name(), out)
334+
sublogger := log.SubLogger(t.Name()).With().Str("foo", "bar").Logger()
335+
sublogger.Info().Str("foo1", "bar1").Msg("")
336+
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s\" foo=bar foo1=bar1\n", 1), out.String())
337+
})
338+
})
339+
340+
t.Run("after-depth=2", func(t *testing.T) {
341+
t.Run("zerolog", func(t *testing.T) {
342+
out := &bytes.Buffer{}
343+
log := New(Zerolog, t.Name(), out)
344+
sublogger0 := log.SubLogger(t.Name())
345+
sublogger1 := sublogger0.SubLogger(t.Name()).With().Str("foo", "bar").Logger()
346+
sublogger1.Info().Str("foo1", "bar1").Msg("")
347+
assert.Equal(t, fillZerologSubloggerTestFields(t, "{\"level\":\"info\",\"foo\":\"bar\",\"time\":\"%s\",\"source\":\"%s:%s:%s\",\"foo1\":\"bar1\"}\n", 2), out.String())
348+
})
349+
350+
t.Run("slog", func(t *testing.T) {
351+
out := &bytes.Buffer{}
352+
log := New(Slog, t.Name(), out)
353+
sublogger0 := log.SubLogger(t.Name())
354+
sublogger1 := sublogger0.SubLogger(t.Name()).With().Str("foo", "bar").Logger()
355+
sublogger1.Info().Str("foo1", "bar1").Msg("")
356+
assert.Equal(t, fillSlogSubloggerTestFields(t, "level=INFO msg=\"\" source=\"%s:%s:%s\" foo=bar foo1=bar1\n", 2), out.String())
357+
})
358+
})
359+
})
360+
}

types/types.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ const (
2121
SourceKey = "source"
2222
)
2323

24-
type Logger interface {
24+
type RootLogger interface {
2525
SetLevel(level Level)
26-
SubLogger
26+
Logger
2727
}
2828

29-
type SubLogger interface {
29+
type Logger interface {
3030
Level() Level
31-
SubLogger(source string) SubLogger
31+
SubLogger(source string) Logger
3232
With() Context
3333

3434
Fatal() Event
@@ -49,7 +49,7 @@ type Event interface {
4949
type Context interface {
5050
taggable[Context]
5151

52-
Logger() SubLogger
52+
Logger() Logger
5353
}
5454

5555
// taggable represents values that can receive structured fields.

0 commit comments

Comments
 (0)