package d2util import ( "fmt" "io" "log" "os" "runtime" ) // LogLevel determines how verbose the logging is (higher is more verbose) type LogLevel = int // Log levels const ( LogLevelNone LogLevel = iota LogLevelFatal LogLevelError LogLevelWarning LogLevelInfo LogLevelDebug LogLevelUnspecified ) // LogLevelDefault is the default log level const LogLevelDefault = LogLevelInfo const ( red = 1 green = 2 yellow = 3 magenta = 5 cyan = 6 ) const fmtColorEscape = "\033[3%dm" const colorEscapeReset = "\033[0m" // Log format strings for log levels const ( fmtPrefix = "[%s]" LogFmtDebug = "[DEBUG]" + colorEscapeReset + " %s\r\n" LogFmtInfo = "[INFO]" + colorEscapeReset + " %s\r\n" LogFmtWarning = "[WARNING]" + colorEscapeReset + " %s\r\n" LogFmtError = "[ERROR]" + colorEscapeReset + " %s\r\n" LogFmtFatal = "[FATAL]" + colorEscapeReset + " %s\r\n" ) // NewLogger creates a new logger with a default func NewLogger() *Logger { l := &Logger{ level: LogLevelDefault, colorEnabled: true, } l.Writer = log.Writer() return l } // Logger is used to write log messages, and can have a log level to determine verbosity type Logger struct { prefix string io.Writer level LogLevel colorEnabled bool } // SetPrefix sets a prefix for the message. // example: // logger.SetPrefix("XYZ") // logger.Debug("ABC") will print "[XYZ] [DEBUG] ABC" func (l *Logger) SetPrefix(s string) { l.prefix = s } // SetLevel sets the log level func (l *Logger) SetLevel(level LogLevel) { if level == LogLevelUnspecified { level = LogLevelDefault } l.level = level } // SetColorEnabled adds color escape-sequences to the logging output func (l *Logger) SetColorEnabled(b bool) { if runtime.GOOS == "windows" { b = false } l.colorEnabled = b } // Info logs an info message func (l *Logger) Info(msg string) { if l == nil || l.level < LogLevelInfo { return } l.print(LogLevelInfo, msg) } // Infof formats and then logs an info message func (l *Logger) Infof(fmtMsg string, args ...interface{}) { l.Info(fmt.Sprintf(fmtMsg, args...)) } // Warning logs a warning message func (l *Logger) Warning(msg string) { if l == nil || l.level < LogLevelWarning { return } l.print(LogLevelWarning, msg) } // Warningf formats and then logs a warning message func (l *Logger) Warningf(fmtMsg string, args ...interface{}) { l.Warning(fmt.Sprintf(fmtMsg, args...)) } // Error logs an error message func (l *Logger) Error(msg string) { if l == nil || l.level < LogLevelError { return } l.print(LogLevelError, msg) } // Errorf formats and then logs a error message func (l *Logger) Errorf(fmtMsg string, args ...interface{}) { l.Error(fmt.Sprintf(fmtMsg, args...)) } // Fatal logs an fatal error message and exits programm func (l *Logger) Fatal(msg string) { defer os.Exit(1) if l == nil || l.level < LogLevelFatal { return } l.print(LogLevelFatal, msg) } // Fatalf formats and then logs a error message func (l *Logger) Fatalf(fmtMsg string, args ...interface{}) { l.Fatal(fmt.Sprintf(fmtMsg, args...)) } // Debug logs a debug message func (l *Logger) Debug(msg string) { if l == nil || l.level < LogLevelDebug { return } l.print(LogLevelDebug, msg) } // Debugf formats and then logs a debug message func (l *Logger) Debugf(fmtMsg string, args ...interface{}) { l.Debug(fmt.Sprintf(fmtMsg, args...)) } func (l *Logger) print(level LogLevel, msg string) { if l == nil || l.level < level { return } colors := map[LogLevel]int{ LogLevelDebug: cyan, LogLevelInfo: green, LogLevelWarning: yellow, LogLevelError: red, LogLevelFatal: red, } fmtString := "" if l.prefix != "" { if l.colorEnabled { fmtString = fmt.Sprintf(fmtColorEscape, magenta) } fmtString += fmt.Sprintf(fmtPrefix, l.prefix) } if l.colorEnabled { fmtString += fmt.Sprintf(fmtColorEscape, colors[level]) } switch level { case LogLevelDebug: fmtString += LogFmtDebug case LogLevelInfo: fmtString += LogFmtInfo case LogLevelWarning: fmtString += LogFmtWarning case LogLevelError: fmtString += LogFmtError case LogLevelFatal: fmtString += LogFmtFatal case LogLevelNone: default: return } _, err := l.Write(format(fmtString, []byte(msg))) if err != nil { log.Print(err) } } func format(fmtStr string, fmtInput []byte) []byte { return []byte(fmt.Sprintf(fmtStr, string(fmtInput))) } func (l *Logger) Write(p []byte) (n int, err error) { return l.Writer.Write(p) }