tile38/controller/log/log.go

347 lines
8.2 KiB
Go

package log
import (
"bytes"
"fmt"
"io"
"log"
"os"
"strings"
"sync"
"time"
)
// Default is the default log
var Default = New(os.Stderr, &Config{})
const (
clear = "\x1b[0m"
bright = "\x1b[1m"
dim = "\x1b[2m"
black = "\x1b[30m"
red = "\x1b[31m"
green = "\x1b[32m"
yellow = "\x1b[33m"
blue = "\x1b[34m"
magenta = "\x1b[35m"
cyan = "\x1b[36m"
white = "\x1b[37m"
)
// Config is the log configuration
type Config struct {
HideInfo bool
HideTime bool
HideNotice bool
HideWarn bool
HideDebug bool
HideError bool
HideFatal bool
HideHTTP bool
NoColors bool
}
// Log is log
type Log struct {
mu sync.RWMutex
w io.Writer
ib []byte
ob []byte
cfg *Config
l *log.Logger
st time.Time
tth time.Duration
}
// New creates a new Log and outputs to w.
func New(w io.Writer, cfg *Config) *Log {
if cfg == nil {
cfg = &Config{}
}
lc := &Log{w: w, cfg: cfg}
lc.l = log.New(lc, "", log.LstdFlags)
lc.st = time.Now()
return lc
}
func (w *Log) format(b []byte) []byte {
s := string(b)
if strings.Contains(s, "!RESET_TIME!") {
w.st = time.Now()
return nil
}
var useRed bool
if strings.Contains(s, "!RED!") {
useRed = true
s = strings.Replace(s, "!RED!", "", 1)
}
si := strings.Index(s, "[")
if si != -1 {
ei := strings.Index(s[si+1:], "]")
if ei != -1 {
tag := s[si+1 : ei+si+1]
format, otag, color, hide := true, "", clear, false
switch tag {
case "TIME":
otag, color, hide = "[TIME]", magenta, w.cfg.HideTime
case "INFO":
otag, color, hide = "[INFO]", cyan, w.cfg.HideInfo
case "HTTP":
otag, color, hide = "[HTTP]", bright+black, w.cfg.HideHTTP
case "FATAL", "FATA":
otag, color, hide = "[FATA]", red, w.cfg.HideFatal
case "ERR", "ERRO", "ERROR":
otag, color, hide = "[ERRO]", bright+red, w.cfg.HideError
case "WARN":
otag, color, hide = "[WARN]", yellow, w.cfg.HideWarn
case "NOTI":
otag, color, hide = "[NOTI]", bright, w.cfg.HideInfo
case "DEBUG", "DEBU":
otag, color, hide = "[DEBU]", magenta, w.cfg.HideDebug
default:
format = false
}
if format {
if hide {
s = ""
} else if w.cfg.NoColors || color == clear {
s = s[:si] + otag + s[ei+si+2:]
} else {
if useRed {
color = red
}
s = s[:si] + color + otag + clear + s[ei+si+2:]
}
}
if otag == "[TIME]" {
st := time.Now()
df := st.Sub(w.st)
if df < w.tth {
return nil
}
w.st = st
var str string
if w.cfg.NoColors {
str = fmt.Sprintf(" %s", df)
} else {
str = fmt.Sprintf(" %s%s%s", bright+black, df, clear)
}
s = s[:len(s)-1] + str + s[len(s)-1:]
}
if !w.cfg.NoColors {
s = strings.Replace(s, "[Leader]", green+"[Leader]"+clear, -1)
s = strings.Replace(s, "[Follower]", red+"[Follower]"+clear, -1)
s = strings.Replace(s, "[Candidate]", yellow+"[Candidate]"+clear, -1)
}
}
}
return []byte(s)
}
// Write writes data directly to the log
func (w *Log) Write(p []byte) (n int, err error) {
w.mu.Lock()
defer w.mu.Unlock()
w.ib = append(w.ib, p...)
for {
idx := bytes.Index(w.ib, []byte{'\n'})
if idx == -1 {
break
}
w.ob = append(w.ob, w.format(w.ib[:idx+1])...)
w.ib = w.ib[idx+1:]
}
if len(w.ob) > 0 {
n, err := w.w.Write(w.ob)
if err != nil {
return len(p), err
}
w.ob = w.ob[n:]
}
return len(p), nil
}
func expand(v []interface{}) string {
var b bytes.Buffer
for i, v := range v {
if i > 0 {
b.WriteByte(' ')
}
switch v := v.(type) {
default:
b.WriteString(fmt.Sprintf("%v", v))
case error:
b.WriteString(v.Error())
case fmt.Stringer:
b.WriteString(v.String())
}
}
return b.String()
}
// Print is equivlent to Info
func (w *Log) Print(v ...interface{}) {
w.Info(expand(v))
}
// Printf is equivlent to Infof
func (w *Log) Printf(format string, args ...interface{}) {
w.Infof(format, args...)
}
// Info prints variables with [INFO] tag
func (w *Log) Info(v ...interface{}) {
w.l.Printf("[INFO] %s", expand(v))
}
// Infof prints format [INFO] tag
func (w *Log) Infof(format string, args ...interface{}) {
w.Info(fmt.Sprintf(format, args...))
}
// Notice prints variables [NOTI] tag
func (w *Log) Notice(v ...interface{}) {
w.l.Printf("[NOTI] %s", expand(v))
}
// Noticef prints format [NOTI] tag
func (w *Log) Noticef(format string, args ...interface{}) {
w.Notice(fmt.Sprintf(format, args...))
}
// Warn prints variables [WARN] tag
func (w *Log) Warn(v ...interface{}) {
w.l.Printf("[WARN] %s", expand(v))
}
// Warnf prints format [WARN] tag
func (w *Log) Warnf(format string, args ...interface{}) {
w.Warn(fmt.Sprintf(format, args...))
}
// Debug prints variables [DEBU] tag
func (w *Log) Debug(v ...interface{}) {
w.l.Printf("[DEBU] %s", expand(v))
}
// Debugf prints format [DEBU] tag
func (w *Log) Debugf(format string, args ...interface{}) {
w.Debug(fmt.Sprintf(format, args...))
}
// Error prints variables [ERRO] tag
func (w *Log) Error(v ...interface{}) {
w.l.Printf("[ERRO] %s", expand(v))
}
// Errorf prints format [ERRO] tag
func (w *Log) Errorf(format string, args ...interface{}) {
w.Error(fmt.Sprintf(format, args...))
}
// Fatal prints variables [FATA] tag followed by an os.Exit(-1).
func (w *Log) Fatal(v ...interface{}) {
w.l.Printf("[FATA] %s", expand(v))
os.Exit(-1)
}
// Fatalf prints format [FATA] tag followed by an os.Exit(-1).
func (w *Log) Fatalf(format string, args ...interface{}) {
w.Fatal(fmt.Sprintf(format, args...))
}
// HTTP prints variables [HTTP] tag
func (w *Log) HTTP(v ...interface{}) {
w.l.Printf("[HTTP] %s", expand(v))
}
// HTTPf prints format [HTTP] tag
func (w *Log) HTTPf(format string, args ...interface{}) {
w.HTTP(fmt.Sprintf(format, args...))
}
// Time prints variables [TIME] tag
func (w *Log) Time(v ...interface{}) {
w.l.Printf("[TIME] %s", expand(v))
}
// Timef prints format [TIME] tag
func (w *Log) Timef(format string, args ...interface{}) {
w.Time(fmt.Sprintf(format, args...))
}
// ResetTime reset the start time
func (w *Log) ResetTime() {
w.mu.Lock()
w.st = time.Now()
w.mu.Unlock()
}
// TimeMinimum sets the minimum duration before the time elapsed text appears
func (w *Log) TimeMinimum(min time.Duration) {
w.mu.Lock()
w.tth = min
w.mu.Unlock()
}
// ResetTime reset the start time
func ResetTime() { Default.ResetTime() }
// TimeMinimum sets the minimum duration before the time elapsed text appears
func TimeMinimum(min time.Duration) { Default.TimeMinimum(min) }
// Info prints variables [INFO] tag
func Info(v ...interface{}) { Default.Info(v...) }
// Infof prints format [INFO] tag
func Infof(format string, args ...interface{}) { Default.Infof(format, args...) }
// Notice prints variables [NOTI] tag
func Notice(v ...interface{}) { Default.Notice(v...) }
// Noticef prints format [NOTI] tag
func Noticef(format string, args ...interface{}) { Default.Noticef(format, args...) }
// Warn prints variables [WARN] tag
func Warn(v ...interface{}) { Default.Warn(v...) }
// Warnf prints format [WARN] tag
func Warnf(format string, args ...interface{}) { Default.Warnf(format, args...) }
// Debug prints variables [DEBU] tag
func Debug(v ...interface{}) { Default.Debug(v...) }
// Debugf prints format [DEBU] tag
func Debugf(format string, args ...interface{}) { Default.Debugf(format, args...) }
// Error prints variables [ERRO] tag
func Error(v ...interface{}) { Default.Error(v...) }
// Errorf prints format [ERRO] tag
func Errorf(format string, args ...interface{}) { Default.Errorf(format, args...) }
// Fatal prints variables [FATA] tag followed by an os.Exit(-1).
func Fatal(v ...interface{}) { Default.Fatal(v...) }
// Fatalf prints format [FATA] tag followed by an os.Exit(-1).
func Fatalf(format string, args ...interface{}) { Default.Fatalf(format, args...) }
// Print is equivlent to Info
func Print(v ...interface{}) { Default.Print(v...) }
// Printf is equivlent to Infof
func Printf(format string, args ...interface{}) { Default.Printf(format, args...) }
// HTTP prints variables [HTTP] tag
func HTTP(v ...interface{}) { Default.HTTP(v...) }
// HTTPf prints format [HTTP] tag
func HTTPf(format string, args ...interface{}) { Default.HTTPf(format, args...) }
// Time prints variables [TIME] tag
func Time(v ...interface{}) { Default.Time(v...) }
// Timef prints format [TIME] tag
func Timef(format string, args ...interface{}) { Default.Timef(format, args...) }