80 lines
2.4 KiB
Go
80 lines
2.4 KiB
Go
package logger
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
)
|
|
|
|
// LoggerLevel expresses the desired logging verbosity. Unlike a full log-level
|
|
// enum, it only distinguishes between a verbose development mode and a quieter
|
|
// production mode; it is converted to an slog.Level by New.
|
|
type LoggerLevel int
|
|
|
|
const (
|
|
// Development enables debug-level output, suitable for local development.
|
|
Development LoggerLevel = iota
|
|
// Production limits output to info level and above.
|
|
Production
|
|
)
|
|
|
|
// toSlogLevel converts a LoggerLevel into the corresponding slog.Level,
|
|
// defaulting to info for any unrecognized value.
|
|
func toSlogLevel(level LoggerLevel) slog.Level {
|
|
switch level {
|
|
case Development:
|
|
return slog.LevelDebug
|
|
case Production:
|
|
return slog.LevelInfo
|
|
default:
|
|
return slog.LevelInfo
|
|
}
|
|
}
|
|
|
|
// New creates a *slog.Logger that writes to os.Stderr in the bracketed format
|
|
// "[<time>] [<LEVEL>] <message> [key=value ...]". Its verbosity is driven by the
|
|
// given LoggerLevel. This replaces the legacy module-level singleton LOGGER;
|
|
// callers own the returned logger and thread it through to sub-modules.
|
|
func New(level LoggerLevel) *slog.Logger {
|
|
return slog.New(&bracketHandler{w: os.Stderr, level: toSlogLevel(level)})
|
|
}
|
|
|
|
// bracketHandler is a custom slog.Handler emitting the bracketed format
|
|
// "[<time>] [<LEVEL>] <message>" followed by any attributes as "key=value",
|
|
// matching the legacy "[LEVEL] message" style with an added timestamp.
|
|
type bracketHandler struct {
|
|
w io.Writer
|
|
level slog.Level
|
|
}
|
|
|
|
func (h *bracketHandler) Enabled(_ context.Context, l slog.Level) bool {
|
|
return l >= h.level
|
|
}
|
|
|
|
func (h *bracketHandler) Handle(_ context.Context, r slog.Record) error {
|
|
var b []byte
|
|
b = append(b, '[')
|
|
b = r.Time.AppendFormat(b, "2006-01-02T15:04:05.000-07:00")
|
|
b = append(b, "] ["...)
|
|
b = append(b, r.Level.String()...)
|
|
b = append(b, "] "...)
|
|
b = append(b, r.Message...)
|
|
r.Attrs(func(a slog.Attr) bool {
|
|
b = append(b, ' ')
|
|
b = append(b, a.Key...)
|
|
b = append(b, '=')
|
|
b = append(b, a.Value.String()...)
|
|
return true
|
|
})
|
|
b = append(b, '\n')
|
|
_, err := h.w.Write(b)
|
|
return err
|
|
}
|
|
|
|
// WithAttrs and WithGroup are no-ops: this project does not derive sub-loggers
|
|
// via Logger.With, so they simply return the receiver.
|
|
func (h *bracketHandler) WithAttrs(_ []slog.Attr) slog.Handler { return h }
|
|
|
|
func (h *bracketHandler) WithGroup(_ string) slog.Handler { return h }
|