colog/std_formatter.go

144 lines
3.3 KiB
Go

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:]...)
}