package main import ( "context" "fmt" "io" "log/slog" "os" "github.com/urfave/cli/v2" ) type logLevel string const ( logLevelDebug logLevel = "debug" logLevelInfo logLevel = "info" logLevelWarn logLevel = "warn" logLevelError logLevel = "error" ) func (l logLevel) slogLevel() slog.Level { switch l { case logLevelDebug: return slog.LevelDebug case logLevelInfo: return slog.LevelInfo case logLevelWarn: return slog.LevelWarn case logLevelError: return slog.LevelError default: panic("unknown log level: " + l) } } func (l logLevel) String() string { return string(l) } type logFormat string const ( logFormatText logFormat = "text" logFormatJSON logFormat = "json" ) func (f logFormat) newHandler(w io.Writer, opts *slog.HandlerOptions) slog.Handler { switch f { case logFormatText: return slog.NewTextHandler(w, opts) case logFormatJSON: return slog.NewJSONHandler(w, opts) default: panic("unknown log format: " + f) } } func (f logFormat) String() string { return string(f) } var ( logFlagLevel = &cli.GenericFlag{ Name: "log.level", Value: &EnumValue{ Enum: []string{ logLevelDebug.String(), logLevelInfo.String(), logLevelWarn.String(), logLevelError.String(), }, Default: logLevelInfo.String(), }, Usage: fmt.Sprintf("%s, %s, %s or %s", logLevelDebug, logLevelInfo, logLevelWarn, logLevelError), EnvVars: []string{"LOG_LEVEL"}, } logFlagFormat = &cli.GenericFlag{ Name: "log.format", Value: &EnumValue{ Enum: []string{ logFormatJSON.String(), logFormatText.String(), }, Default: logFormatText.String(), }, Usage: fmt.Sprintf("%s or %s", logFormatText, logFormatJSON), EnvVars: []string{"LOG_FORMAT"}, } logFlags = []cli.Flag{ logFlagLevel, logFlagFormat, } ) func newLoggerFromFlags(c *cli.Context) *slog.Logger { return newLogger(loggerConfig{ level: logLevel(c.String(logFlagLevel.Name)), format: logFormat(c.String(logFlagFormat.Name)), }) } type loggerConfig struct { level logLevel format logFormat } func newLogger(cfg loggerConfig) *slog.Logger { return slog.New(cfg.format.newHandler(os.Stderr, &slog.HandlerOptions{ Level: cfg.level.slogLevel(), })) } type loggerCtxKey struct{} func loggerToCtx(ctx context.Context, l *slog.Logger) context.Context { return context.WithValue(ctx, loggerCtxKey{}, l) } func loggerFromCtx(ctx context.Context) *slog.Logger { logger, _ := ctx.Value(loggerCtxKey{}).(*slog.Logger) return logger }