diff --git a/pkg/shp/logger/logger.go b/pkg/shp/logger/logger.go new file mode 100644 index 00000000..224bad0c --- /dev/null +++ b/pkg/shp/logger/logger.go @@ -0,0 +1,154 @@ +package logger + +import ( + "fmt" + "io" + "os" + + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +// Level represents the logging level +type Level int + +const ( + DebugLevel Level = iota + InfoLevel + WarnLevel + ErrorLevel +) + +type Logger struct { + ioStreams *genericclioptions.IOStreams + level Level + verbose bool + quiet bool + color bool +} + +// NewLogger creates a new logger instance +func NewLogger(ioStreams *genericclioptions.IOStreams) *Logger { + return &Logger{ + ioStreams: ioStreams, + level: InfoLevel, + color: isTerminal(ioStreams.Out), + } +} + +func (l *Logger) SetLevel(level Level) { + l.level = level +} + +// SetVerbose enables verbose mode +func (l *Logger) SetVerbose(verbose bool) { + l.verbose = verbose + if verbose { + l.level = DebugLevel + } +} + +// SetQuiet enables quiet mode +func (l *Logger) SetQuiet(quiet bool) { + l.quiet = quiet +} + +func (l *Logger) Info(msg string, args ...interface{}) { + if l.level <= InfoLevel && !l.quiet { + l.logWithLevel("INFO", blueColor, msg, args...) + } +} + +func (l *Logger) Warn(msg string, args ...interface{}) { + if l.level <= WarnLevel { + l.logWithLevel("WARNING", yellowColor, msg, args...) + } +} + +func (l *Logger) Error(msg string, args ...interface{}) { + if l.level <= ErrorLevel { + l.logWithLevel("ERROR", redColor, msg, args...) + } +} + +func (l *Logger) Debug(msg string, args ...interface{}) { + if l.level <= DebugLevel { + l.logWithLevel("DEBUG", grayColor, msg, args...) + } +} + +func (l *Logger) Success(msg string, args ...interface{}) { + if !l.quiet { + l.logWithSymbol("✓", greenColor, msg, args...) + } +} + +func (l *Logger) Progress(msg string, args ...interface{}) { + if !l.quiet { + l.logWithLevel("INFO", blueColor, msg, args...) + } +} + +func (l *Logger) Output(msg string, args ...interface{}) { + if !l.quiet { + formattedMsg := fmt.Sprintf(msg, args...) + fmt.Fprintln(l.ioStreams.Out, formattedMsg) + } +} + +func (l *Logger) Outputf(msg string, args ...interface{}) { + if !l.quiet { + fmt.Fprintf(l.ioStreams.Out, msg, args...) + } +} + +// errors shouldn't be suppressed in quite mode +func (l *Logger) OutputErr(msg string, args ...interface{}) { + formattedMsg := fmt.Sprintf(msg, args...) + fmt.Fprintln(l.ioStreams.ErrOut, formattedMsg) +} + +func (l *Logger) OutputErrf(msg string, args ...interface{}) { + fmt.Fprintf(l.ioStreams.ErrOut, msg, args...) +} + +// logWithLevel logs with a log level prefix and color +func (l *Logger) logWithLevel(level string, color string, msg string, args ...interface{}) { + formattedMsg := fmt.Sprintf(msg, args...) + if l.color { + fmt.Fprintf(l.ioStreams.Out, "%s%s:%s %s\n", color, level, resetColor, formattedMsg) + } else { + fmt.Fprintf(l.ioStreams.Out, "%s: %s\n", level, formattedMsg) + } +} + +// logWithSymbol logs with a symbol prefix and color +func (l *Logger) logWithSymbol(symbol string, color string, msg string, args ...interface{}) { + formattedMsg := fmt.Sprintf(msg, args...) + if l.color { + fmt.Fprintf(l.ioStreams.Out, "%s%s%s %s\n", color, symbol, resetColor, formattedMsg) + } else { + fmt.Fprintf(l.ioStreams.Out, "%s %s\n", symbol, formattedMsg) + } +} + +const ( + resetColor = "\033[0m" + redColor = "\033[31m" + greenColor = "\033[32m" + yellowColor = "\033[33m" + blueColor = "\033[34m" + grayColor = "\033[90m" +) + +func isTerminal(w io.Writer) bool { + // Check if stdout is a terminal + if f, ok := w.(*os.File); ok { + return isTerminalFile(f) + } + return false +} + +func isTerminalFile(f *os.File) bool { + stat, _ := f.Stat() + return (stat.Mode() & os.ModeCharDevice) != 0 +} \ No newline at end of file