package log import ( "fmt" "os" "strings" "sync" "time" ) type Handler func(format string, args ...interface{}) // https://misc.flogisoft.com/bash/tip_colors_and_formatting const ( BOLD = "\033[1m" DIM = "\033[2m" RED = "\033[31m" GREEN = "\033[32m" BLUE = "\033[34m" YELLOW = "\033[33m" FG_BLACK = "\033[30m" FG_WHITE = "\033[97m" BG_PURPLE = "\033[45m" BG_RED = "\033[41m" BG_GREEN = "\033[42m" BG_YELLOW = "\033[43m" BG_DGRAY = "\033[100m" BG_LBLUE = "\033[104m" RESET = "\033[0m" ) // log level constants const ( DEBUG = iota INFO IMPORTANT WARNING ERROR FATAL TRACE = -1 ) // var ( WithColors = true Output = os.Stdout StdoutFile = "/dev/stdout" DateFormat = "2006-01-02 15:04:05" MinLevel = int(INFO) LogUTC = true LogMicro = false mutex = &sync.RWMutex{} labels = map[int]string{ TRACE: "TRC", DEBUG: "DBG", INFO: "INF", IMPORTANT: "IMP", WARNING: "WAR", ERROR: "ERR", FATAL: "!!!", } colors = map[int]string{ TRACE: FG_WHITE + BG_PURPLE, DEBUG: DIM + FG_BLACK + BG_DGRAY, INFO: FG_WHITE + BG_GREEN, IMPORTANT: FG_WHITE + BG_LBLUE, WARNING: FG_WHITE + BG_YELLOW, ERROR: FG_WHITE + BG_RED, FATAL: FG_WHITE + BG_RED + BOLD, } ) // Wrap wraps a text with effects func Wrap(s, effect string) string { if WithColors == true { s = effect + s + RESET } return s } // Dim dims a text func Dim(s string) string { return Wrap(s, DIM) } // Bold bolds a text func Bold(s string) string { return Wrap(s, BOLD) } // Red reds the text func Red(s string) string { return Wrap(s, RED) } // Green greens the text func Green(s string) string { return Wrap(s, GREEN) } // Blue blues the text func Blue(s string) string { return Wrap(s, BLUE) } // Yellow yellows the text func Yellow(s string) string { return Wrap(s, YELLOW) } // Raw prints out a text without colors func Raw(format string, args ...interface{}) { mutex.RLock() defer mutex.RUnlock() fmt.Fprintf(Output, format, args...) } // SetLogLevel sets the log level func SetLogLevel(newLevel int) { mutex.Lock() defer mutex.Unlock() MinLevel = newLevel } // GetLogLevel returns the current log level configured. func GetLogLevel() int { mutex.RLock() defer mutex.RUnlock() return MinLevel } // SetLogUTC configures UTC timestamps func SetLogUTC(newLogUTC bool) { mutex.Lock() defer mutex.Unlock() LogUTC = newLogUTC } // GetLogUTC returns the current config. func GetLogUTC() bool { mutex.RLock() defer mutex.RUnlock() return LogUTC } // SetLogMicro configures microsecond timestamps func SetLogMicro(newLogMicro bool) { mutex.Lock() defer mutex.Unlock() LogMicro = newLogMicro } // GetLogMicro returns the current config. func GetLogMicro() bool { mutex.Lock() defer mutex.Unlock() return LogMicro } // Log prints out a text with the given color and format func Log(level int, format string, args ...interface{}) { mutex.RLock() defer mutex.RUnlock() if level >= MinLevel { label := labels[level] color := colors[level] datefmt := DateFormat if LogMicro { datefmt = DateFormat + ".000000" } when := time.Now().UTC().Format(datefmt) if LogUTC == false { when = time.Now().Local().Format(datefmt) } what := fmt.Sprintf(format, args...) if strings.HasSuffix(what, "\n") == false { what += "\n" } l := Dim("[%s]") r := Wrap(" %s ", color) + " %s" fmt.Fprintf(Output, l+" "+r, when, label, what) } } func setDefaultLogOutput() { mutex.Lock() Output = os.Stdout mutex.Unlock() } // OpenFile opens a file to print out the logs func OpenFile(logFile string) (err error) { if logFile == StdoutFile { setDefaultLogOutput() return } if Output, err = os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil { Error("Error opening log: %s %s", logFile, err) //fallback to stdout setDefaultLogOutput() } Important("Start writing logs to %s", logFile) return err } // Close closes the current output file descriptor func Close() { if Output != os.Stdout { Output.Close() } } // Trace is the log level for tracing purposes func Trace(format string, args ...interface{}) { Log(TRACE, format, args...) } // Debug is the log level for debugging purposes func Debug(format string, args ...interface{}) { Log(DEBUG, format, args...) } // Info is the log level for informative messages func Info(format string, args ...interface{}) { Log(INFO, format, args...) } // Important is the log level for things that must pay attention func Important(format string, args ...interface{}) { Log(IMPORTANT, format, args...) } // Warning is the log level for non-critical errors func Warning(format string, args ...interface{}) { Log(WARNING, format, args...) } // Error is the log level for errors that should be corrected func Error(format string, args ...interface{}) { Log(ERROR, format, args...) } // Fatal is the log level for errors that must be corrected before continue func Fatal(format string, args ...interface{}) { Log(FATAL, format, args...) os.Exit(1) }