package logging import ( "fmt" "os" "sync" "time" ) type Level int const ( FATAL Level = iota ERROR WARNING INFO DEBUG ) type Record struct { Level Level Component string Message string } const ( CLEARLINE = "\x1b[2K" ) func Debugf(msg string, args ...interface{}) { defaultLogBroker.Records <- Record{DEBUG, "", fmt.Sprintf(msg, args...)} } func Infof(msg string, args ...interface{}) { defaultLogBroker.Records <- Record{INFO, "", fmt.Sprintf(msg, args...)} } func Warnf(msg string, args ...interface{}) { defaultLogBroker.Records <- Record{WARNING, "", fmt.Sprintf(msg, args...)} } func Errorf(msg string, args ...interface{}) { defaultLogBroker.Records <- Record{ERROR, "", fmt.Sprintf(msg, args...)} } func Fatalf(msg string, args ...interface{}) { defaultLogBroker.Records <- Record{FATAL, "", fmt.Sprintf(msg, args...)} Shutdown() os.Exit(1) } func Progress(msg string) { defaultLogBroker.Progress <- msg } func SetQuiet(quiet bool) { defaultLogBroker.SetQuiet(quiet) } type Logger struct { Component string } func (l *Logger) Print(args ...interface{}) { defaultLogBroker.Records <- Record{INFO, l.Component, fmt.Sprint(args...)} } func (l *Logger) Printf(msg string, args ...interface{}) { defaultLogBroker.Records <- Record{INFO, l.Component, fmt.Sprintf(msg, args...)} } func (l *Logger) Fatal(args ...interface{}) { defaultLogBroker.Records <- Record{FATAL, l.Component, fmt.Sprint(args...)} Shutdown() os.Exit(1) } func (l *Logger) Fatalf(msg string, args ...interface{}) { defaultLogBroker.Records <- Record{FATAL, l.Component, fmt.Sprintf(msg, args...)} Shutdown() os.Exit(1) } func (l *Logger) Error(args ...interface{}) { defaultLogBroker.Records <- Record{ERROR, l.Component, fmt.Sprint(args...)} } func (l *Logger) Errorf(msg string, args ...interface{}) { defaultLogBroker.Records <- Record{ERROR, l.Component, fmt.Sprintf(msg, args...)} } func (l *Logger) Warn(args ...interface{}) { defaultLogBroker.Records <- Record{WARNING, l.Component, fmt.Sprint(args...)} } func (l *Logger) Warnf(msg string, args ...interface{}) { defaultLogBroker.Records <- Record{WARNING, l.Component, fmt.Sprintf(msg, args...)} } func (l *Logger) Printfl(level Level, msg string, args ...interface{}) { defaultLogBroker.Records <- Record{level, l.Component, fmt.Sprintf(msg, args...)} } func (l *Logger) StartStep(msg string) string { defaultLogBroker.StepStart <- Step{l.Component, msg} return msg } func (l *Logger) StopStep(msg string) { defaultLogBroker.StepStop <- Step{l.Component, msg} } func NewLogger(component string) *Logger { return &Logger{component} } type Step struct { Component string Name string } type LogBroker struct { Records chan Record Progress chan string StepStart chan Step StepStop chan Step quiet bool quit chan bool wg *sync.WaitGroup newline bool lastProgress string } func (l *LogBroker) SetQuiet(quiet bool) { l.quiet = quiet } func (l *LogBroker) loop() { l.wg.Add(1) steps := make(map[Step]time.Time) For: for { select { case record := <-l.Records: l.printRecord(record) case progress := <-l.Progress: if !l.quiet { l.printProgress(progress) } case step := <-l.StepStart: steps[step] = time.Now() l.printProgress(step.Name) case step := <-l.StepStop: startTime := steps[step] delete(steps, step) duration := time.Since(startTime) l.lastProgress = "" l.printRecord(Record{INFO, step.Component, step.Name + " took: " + duration.String()}) case <-l.quit: break For } } Flush: // after quit, print all records from chan for { select { case record := <-l.Records: l.printRecord(record) default: break Flush } } l.wg.Done() } func (l *LogBroker) printPrefix() { fmt.Print("[", time.Now().Format(time.Stamp), "] ") } func (l *LogBroker) printComponent(component string) { if component != "" { fmt.Print("[", component, "] ") } } func (l *LogBroker) printLevel(level Level) { switch level { case INFO: fmt.Print("[INFO] ") case WARNING: fmt.Print("[WARN] ") case ERROR: fmt.Print("[ERR] ") } } func (l *LogBroker) printRecord(record Record) { if !l.newline { fmt.Print(CLEARLINE) } l.printPrefix() l.printLevel(record.Level) l.printComponent(record.Component) fmt.Println(record.Message) l.newline = true if l.lastProgress != "" { l.printProgress(l.lastProgress) l.newline = false } } func (l *LogBroker) printProgress(progress string) { l.printPrefix() fmt.Print(progress) if l.quiet { fmt.Print("\n") l.lastProgress = "" l.newline = true } else { fmt.Print("\r") l.lastProgress = progress l.newline = false } } func Shutdown() { defaultLogBroker.quit <- true defaultLogBroker.wg.Wait() } var defaultLogBroker LogBroker func init() { defaultLogBroker = LogBroker{ Records: make(chan Record, 8), Progress: make(chan string), StepStart: make(chan Step), StepStop: make(chan Step), quit: make(chan bool), wg: &sync.WaitGroup{}, } go defaultLogBroker.loop() } // func init() { // go func() { // log := NewLogger("Tourette") // for { // time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) // log.Printfl(Level(rand.Intn(5)), "Bazinga") // } // }() // }