nebula/test/logger.go
2026-04-27 09:41:47 -05:00

73 lines
2.3 KiB
Go

package test
import (
"context"
"io"
"log/slog"
"os"
"time"
"github.com/slackhq/nebula/logging"
)
// NewLogger returns a *slog.Logger suitable for use in tests. Output goes to
// io.Discard by default; set TEST_LOGS=1 (info), 2 (debug), or 3 (trace) to
// stream output to stderr for local debugging.
func NewLogger() *slog.Logger {
v := os.Getenv("TEST_LOGS")
if v == "" {
return slog.New(slog.DiscardHandler)
}
level := slog.LevelInfo
switch v {
case "2":
level = slog.LevelDebug
case "3":
level = logging.LevelTrace
}
return slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level}))
}
// NewLoggerWithOutput returns a *slog.Logger whose text output is captured by
// w. Timestamps are suppressed so tests can assert on exact output without
// baking the current time into expected strings.
func NewLoggerWithOutput(w io.Writer) *slog.Logger {
return slog.New(&stripTimeHandler{inner: slog.NewTextHandler(w, nil)})
}
// NewLoggerWithOutputAndLevel is NewLoggerWithOutput with an explicit level
// so tests can exercise Enabled-gated paths.
func NewLoggerWithOutputAndLevel(w io.Writer, level slog.Level) *slog.Logger {
return slog.New(&stripTimeHandler{inner: slog.NewTextHandler(w, &slog.HandlerOptions{Level: level})})
}
// NewJSONLoggerWithOutput returns a *slog.Logger emitting JSON to w with
// timestamps suppressed, for tests that pin the JSON shape.
func NewJSONLoggerWithOutput(w io.Writer, level slog.Level) *slog.Logger {
return slog.New(&stripTimeHandler{inner: slog.NewJSONHandler(w, &slog.HandlerOptions{Level: level})})
}
// stripTimeHandler zeros each record's time before delegating so slog's
// built-in handlers skip emitting the time attribute. Used to avoid
// timestamp-dependent assertions in tests without resorting to ReplaceAttr.
type stripTimeHandler struct {
inner slog.Handler
}
func (h *stripTimeHandler) Enabled(ctx context.Context, l slog.Level) bool {
return h.inner.Enabled(ctx, l)
}
func (h *stripTimeHandler) Handle(ctx context.Context, r slog.Record) error {
r.Time = time.Time{}
return h.inner.Handle(ctx, r)
}
func (h *stripTimeHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return &stripTimeHandler{inner: h.inner.WithAttrs(attrs)}
}
func (h *stripTimeHandler) WithGroup(name string) slog.Handler {
return &stripTimeHandler{inner: h.inner.WithGroup(name)}
}