1
0

logger.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. package test
  2. import (
  3. "context"
  4. "io"
  5. "log/slog"
  6. "os"
  7. "time"
  8. "github.com/slackhq/nebula/logging"
  9. )
  10. // NewLogger returns a *slog.Logger suitable for use in tests. Output goes to
  11. // io.Discard by default; set TEST_LOGS=1 (info), 2 (debug), or 3 (trace) to
  12. // stream output to stderr for local debugging.
  13. func NewLogger() *slog.Logger {
  14. v := os.Getenv("TEST_LOGS")
  15. if v == "" {
  16. return slog.New(slog.DiscardHandler)
  17. }
  18. level := slog.LevelInfo
  19. switch v {
  20. case "2":
  21. level = slog.LevelDebug
  22. case "3":
  23. level = logging.LevelTrace
  24. }
  25. return slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level}))
  26. }
  27. // NewLoggerWithOutput returns a *slog.Logger whose text output is captured by
  28. // w. Timestamps are suppressed so tests can assert on exact output without
  29. // baking the current time into expected strings.
  30. func NewLoggerWithOutput(w io.Writer) *slog.Logger {
  31. return slog.New(&stripTimeHandler{inner: slog.NewTextHandler(w, nil)})
  32. }
  33. // NewLoggerWithOutputAndLevel is NewLoggerWithOutput with an explicit level
  34. // so tests can exercise Enabled-gated paths.
  35. func NewLoggerWithOutputAndLevel(w io.Writer, level slog.Level) *slog.Logger {
  36. return slog.New(&stripTimeHandler{inner: slog.NewTextHandler(w, &slog.HandlerOptions{Level: level})})
  37. }
  38. // NewJSONLoggerWithOutput returns a *slog.Logger emitting JSON to w with
  39. // timestamps suppressed, for tests that pin the JSON shape.
  40. func NewJSONLoggerWithOutput(w io.Writer, level slog.Level) *slog.Logger {
  41. return slog.New(&stripTimeHandler{inner: slog.NewJSONHandler(w, &slog.HandlerOptions{Level: level})})
  42. }
  43. // stripTimeHandler zeros each record's time before delegating so slog's
  44. // built-in handlers skip emitting the time attribute. Used to avoid
  45. // timestamp-dependent assertions in tests without resorting to ReplaceAttr.
  46. type stripTimeHandler struct {
  47. inner slog.Handler
  48. }
  49. func (h *stripTimeHandler) Enabled(ctx context.Context, l slog.Level) bool {
  50. return h.inner.Enabled(ctx, l)
  51. }
  52. func (h *stripTimeHandler) Handle(ctx context.Context, r slog.Record) error {
  53. r.Time = time.Time{}
  54. return h.inner.Handle(ctx, r)
  55. }
  56. func (h *stripTimeHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
  57. return &stripTimeHandler{inner: h.inner.WithAttrs(attrs)}
  58. }
  59. func (h *stripTimeHandler) WithGroup(name string) slog.Handler {
  60. return &stripTimeHandler{inner: h.inner.WithGroup(name)}
  61. }