package colog import ( "fmt" "log" "path/filepath" "time" ) var colorLabels = LevelMap{ LDebug: []byte("[\x1b[0;36mDBG\x1b[0m] "), LInfo: []byte("[\x1b[0;32mINF\x1b[0m] "), LWarn: []byte("\x1b[0;35m[WARN]\x1b[0m "), LError: []byte("\x1b[0;31m[ERR]\x1b[0m "), } var plainLabels = LevelMap{ LDebug: []byte("[DBG] "), LInfo: []byte("[INF] "), LWarn: []byte("[WARN] "), LError: []byte("[ERR] "), } // StdFormatter supports plain and color level headers // and bold/padded fields type StdFormatter struct { Flag int Colors bool // Force enable colors NoColors bool // Force disable colors (has preference) colorSupported bool } // Format takes and entry and returns the formatted output in bytes func (sf *StdFormatter) Format(e *Entry) ([]byte, error) { // Normal headers. time, file, etc var header, message []byte sf.stdHeader(&header, e.Time, e.File, e.Line) // Level headers headers := sf.levelHeaders() message = append(headers[e.Level], append(header, e.Message...)...) return append(message, '\n'), nil } // levelHeaders returns plain or color level headers // depending on user preference and output support func (sf *StdFormatter) levelHeaders() LevelMap { switch { case sf.NoColors: return plainLabels case sf.Colors: return colorLabels case sf.colorSupported: return colorLabels } return plainLabels } // Flags returns the output flags for the formatter. func (sf *StdFormatter) Flags() int { return sf.Flag } // SetFlags sets the output flags for the formatter. func (sf *StdFormatter) SetFlags(flags int) { sf.Flag = flags } // ColorSupported enables or disables the colors, this will be called on every func (sf *StdFormatter) ColorSupported(supp bool) { sf.Colors = supp } // Adapted replica of log.Logger.formatHeader func (sf *StdFormatter) stdHeader(buf *[]byte, t time.Time, file string, line int) { if sf.Flag&(log.Ldate|log.Ltime|log.Lmicroseconds) != 0 { if sf.Flag&log.Ldate != 0 { year, month, day := t.Date() itoa(buf, year, 4) *buf = append(*buf, '/') itoa(buf, int(month), 2) *buf = append(*buf, '/') itoa(buf, day, 2) *buf = append(*buf, ' ') } if sf.Flag&(log.Ltime|log.Lmicroseconds) != 0 { hour, min, sec := t.Clock() itoa(buf, hour, 2) *buf = append(*buf, ':') itoa(buf, min, 2) *buf = append(*buf, ':') itoa(buf, sec, 2) if sf.Flag&log.Lmicroseconds != 0 { *buf = append(*buf, '.') itoa(buf, t.Nanosecond()/1e3, 6) } *buf = append(*buf, ' ') } } if sf.Flag&(log.Lshortfile|log.Llongfile) != 0 { if sf.Flag&log.Lshortfile != 0 { short := file for i := len(file) - 1; i > 0; i-- { if file[i] == '/' { short = file[i+1:] break } } file = short } else { file = filepath.Base(filepath.Dir(file)) + "/" + filepath.Base(file) } if sf.Colors { file = fmt.Sprintf("\x1b[1;30m%s:%d:\x1b[0m ", file, line) } else { file = fmt.Sprintf("%s:%d: ", file, line) } *buf = append(*buf, file...) } } // Replica of log.Logger.itoa func itoa(buf *[]byte, i int, wid int) { var u = uint(i) if u == 0 && wid <= 1 { *buf = append(*buf, '0') return } // Assemble decimal in reverse order. var b [32]byte bp := len(b) for ; u > 0 || wid > 0; u /= 10 { bp-- wid-- b[bp] = byte(u%10) + '0' } *buf = append(*buf, b[bp:]...) }