mirror of
https://github.com/chainguard-dev/clog
synced 2026-07-06 22:12:46 +00:00
Because we were using attr.With, we were setting the logger attributes, and then appending the log context attributes again later for any contextf functions. This changes the behavior to use the context for any log.Info functions.
245 lines
5 KiB
Go
245 lines
5 KiB
Go
package clog
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log/slog"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
var (
|
|
testopts = &slog.HandlerOptions{
|
|
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
|
// Ignore time to make testing easier.
|
|
if a.Key == "time" {
|
|
return slog.Attr{}
|
|
}
|
|
return a
|
|
},
|
|
}
|
|
)
|
|
|
|
func TestLogger(t *testing.T) {
|
|
ctx := context.Background()
|
|
b := new(bytes.Buffer)
|
|
base := slog.New(NewHandler(slog.NewJSONHandler(b, testopts)))
|
|
log := NewLogger(base).With("a", "b")
|
|
log.InfoContext(ctx, "")
|
|
t.Log(b.String())
|
|
|
|
want := map[string]any{
|
|
"level": "INFO",
|
|
"msg": "",
|
|
"a": "b",
|
|
}
|
|
|
|
var got map[string]any
|
|
if err := json.Unmarshal(b.Bytes(), &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(want, got) {
|
|
t.Errorf("want %v, got %v", want, got)
|
|
}
|
|
}
|
|
|
|
func TestLoggerNilBase(t *testing.T) {
|
|
log := NewLogger(nil)
|
|
log.Info("")
|
|
}
|
|
|
|
func TestLoggerFromContext(t *testing.T) {
|
|
b := new(bytes.Buffer)
|
|
base := slog.New(NewHandler(slog.NewJSONHandler(b, testopts)))
|
|
log := NewLogger(base).With("a", "b")
|
|
|
|
ctx := WithLogger(context.Background(), log)
|
|
FromContext(ctx).Info("")
|
|
|
|
want := map[string]any{
|
|
"level": "INFO",
|
|
"msg": "",
|
|
"a": "b",
|
|
}
|
|
|
|
t.Run("FromContext.Info", func(t *testing.T) {
|
|
var got map[string]any
|
|
if err := json.Unmarshal(b.Bytes(), &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(want, got) {
|
|
t.Errorf("want %v, got %v", want, got)
|
|
}
|
|
})
|
|
|
|
b.Reset()
|
|
|
|
t.Run("clog.Info", func(t *testing.T) {
|
|
InfoContext(ctx, "")
|
|
var got map[string]any
|
|
if err := json.Unmarshal(b.Bytes(), &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(want, got) {
|
|
t.Errorf("want %v, got %v", want, got)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLoggerPC(t *testing.T) {
|
|
b := new(bytes.Buffer)
|
|
log := NewLogger(slog.New(NewHandler(slog.NewJSONHandler(b, &slog.HandlerOptions{
|
|
AddSource: true,
|
|
ReplaceAttr: testopts.ReplaceAttr,
|
|
}))))
|
|
|
|
log.Info("")
|
|
t.Log(b.String())
|
|
|
|
var got struct {
|
|
Source struct {
|
|
File string `json:"file"`
|
|
Function string `json:"function"`
|
|
} `json:"source"`
|
|
}
|
|
if err := json.Unmarshal(b.Bytes(), &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Knowing that the PC is from this test is good enough.
|
|
want := fmt.Sprintf("github.com/chainguard-dev/clog.%s", t.Name())
|
|
if got.Source.Function != want {
|
|
t.Errorf("want %v, got %v", want, got)
|
|
}
|
|
}
|
|
|
|
func TestWith(t *testing.T) {
|
|
ctx := context.WithValue(context.Background(), "test", "test")
|
|
log := NewLoggerWithContext(ctx, nil)
|
|
withed := log.With("a", "b")
|
|
if want := withed.ctx; want != ctx {
|
|
t.Errorf("want %v, got %v", want, ctx)
|
|
}
|
|
withed = log.WithGroup("a")
|
|
if want := withed.ctx; want != ctx {
|
|
t.Errorf("want %v, got %v", want, ctx)
|
|
}
|
|
}
|
|
|
|
func TestDefaultHandler(t *testing.T) {
|
|
old := slog.Default()
|
|
defer func() {
|
|
slog.SetDefault(old)
|
|
}()
|
|
|
|
b := new(bytes.Buffer)
|
|
slog.SetDefault(slog.New(slog.NewJSONHandler(b, testopts)))
|
|
|
|
t.Run("Info", func(t *testing.T) {
|
|
FromContext(WithValues(context.Background(), "a", "b")).Info("")
|
|
want := map[string]any{
|
|
"level": "INFO",
|
|
"msg": "",
|
|
"a": "b",
|
|
}
|
|
var got map[string]any
|
|
if err := json.Unmarshal(b.Bytes(), &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(want, got) {
|
|
t.Errorf("want %v, got %v", want, got)
|
|
}
|
|
})
|
|
|
|
b.Reset()
|
|
|
|
t.Run("InfoContext", func(t *testing.T) {
|
|
// Set logger with original value
|
|
ctx := WithValues(context.Background(), "a", "b")
|
|
logger := FromContext(ctx)
|
|
|
|
// Override value in request context - we expect this to overwrite the original value set in the logger
|
|
logger.InfoContext(WithValues(ctx, "a", "c"), "")
|
|
|
|
want := map[string]any{
|
|
"level": "INFO",
|
|
"msg": "",
|
|
"a": "c",
|
|
}
|
|
var got map[string]any
|
|
if err := json.Unmarshal(b.Bytes(), &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(want, got) {
|
|
t.Errorf("want %v, got %v", want, got)
|
|
}
|
|
})
|
|
|
|
b.Reset()
|
|
|
|
t.Run("Debug - no log", func(t *testing.T) {
|
|
logger := FromContext(context.Background())
|
|
logger.Debug("asdf")
|
|
|
|
if b.Len() != 0 {
|
|
t.Errorf("want empty, got %q", b.String())
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestContext(t *testing.T) {
|
|
old := slog.Default()
|
|
t.Cleanup(func() {
|
|
slog.SetDefault(old)
|
|
})
|
|
|
|
b := new(bytes.Buffer)
|
|
slog.SetDefault(slog.New(slog.NewJSONHandler(b, testopts)))
|
|
|
|
msg := "hello world"
|
|
want := map[string]any{
|
|
"level": "INFO",
|
|
"msg": msg,
|
|
"a": "b",
|
|
}
|
|
|
|
ctx := WithValues(context.Background(), "a", "b")
|
|
// These should all give the same output and be functionally equivalent.
|
|
for _, tc := range []struct {
|
|
name string
|
|
fn func()
|
|
}{
|
|
{
|
|
name: "clog.InfoContext",
|
|
fn: func() {
|
|
InfoContext(ctx, msg)
|
|
},
|
|
},
|
|
{
|
|
name: "clog.FromContext.Info",
|
|
fn: func() {
|
|
FromContext(ctx).Info(msg)
|
|
},
|
|
},
|
|
{
|
|
name: "clog.FromContext.InfoContext",
|
|
fn: func() {
|
|
FromContext(ctx).InfoContext(ctx, msg)
|
|
},
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
b.Reset()
|
|
tc.fn()
|
|
|
|
var got map[string]any
|
|
if err := json.Unmarshal(b.Bytes(), &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(want, got) {
|
|
t.Errorf("want %v, got %v", want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|