144 lines
3.3 KiB
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:]...)
|
|
}
|