// Package logger provides a unified structured logger for all Cyrene services. // It wraps Go's log/slog with opinionated defaults (JSON output, caller info, // service name tagging) and printf-style convenience methods compatible with // the standard log package for easy migration. package logger import ( "fmt" "log/slog" "os" ) // Logger wraps slog.Logger with printf-style convenience methods. type Logger struct { inner *slog.Logger } // New creates a new Logger for the named service. // By default it writes JSON to stderr at Info level with caller info. func New(service string, opts ...Option) *Logger { cfg := config{ level: slog.LevelInfo, format: FormatJSON, output: os.Stderr, service: service, } for _, o := range opts { o(&cfg) } h := slog.NewJSONHandler(cfg.output, &slog.HandlerOptions{ Level: cfg.level, AddSource: true, }) return &Logger{ inner: slog.New(h).With(slog.String("svc", cfg.service)), } } // Slog returns the underlying slog.Logger for direct structured logging. func (l *Logger) Slog() *slog.Logger { return l.inner } // Debug logs a debug-level message (printf style). func (l *Logger) Debug(format string, args ...any) { l.inner.Debug(fmt.Sprintf(format, args...)) } // Info logs an info-level message (printf style). func (l *Logger) Info(format string, args ...any) { l.inner.Info(fmt.Sprintf(format, args...)) } // Warn logs a warn-level message (printf style). func (l *Logger) Warn(format string, args ...any) { l.inner.Warn(fmt.Sprintf(format, args...)) } // Error logs an error-level message (printf style). func (l *Logger) Error(format string, args ...any) { l.inner.Error(fmt.Sprintf(format, args...)) } // Fatal logs an error-level message and exits with code 1. func (l *Logger) Fatal(format string, args ...any) { l.inner.Error(fmt.Sprintf(format, args...)) os.Exit(1) } // Format controls log output format. type Format int const ( FormatJSON Format = iota FormatText ) type config struct { level slog.Level format Format output *os.File service string } // Option configures a logger. type Option func(*config) // WithLevel sets the minimum log level. func WithLevel(l slog.Level) Option { return func(c *config) { c.level = l } } // WithFormat sets the output format (JSON or Text). func WithFormat(f Format) Option { return func(c *config) { c.format = f } } // WithOutput sets the output writer (default stderr). func WithOutput(f *os.File) Option { return func(c *config) { c.output = f } } // WithDebug enables debug-level logging and text format (for development). func WithDebug() Option { return func(c *config) { c.level = slog.LevelDebug c.format = FormatText } } // --- global default logger (drop-in replacement for log package) --- var defaultLogger *Logger // SetDefault sets the global default logger. Call once from main(). func SetDefault(l *Logger) { defaultLogger = l } func get() *Logger { if defaultLogger == nil { defaultLogger = New("unknown") } return defaultLogger } // Printf logs at info level (drop-in for log.Printf). func Printf(format string, args ...any) { get().Info(format, args...) } // Println logs at info level (drop-in for log.Println). func Println(args ...any) { get().Info(fmt.Sprint(args...)) } // Fatalf logs at error level and exits (drop-in for log.Fatalf). func Fatalf(format string, args ...any) { get().Fatal(format, args...) }