mirror of https://github.com/tidwall/tile38.git
347 lines
8.2 KiB
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...) }
|