package klog import ( "encoding/json" "fmt" "io" "os" "sync" "time" "github.com/mattn/go-isatty" ) type Llevel int const ( Lerror Llevel = 1 << iota Ldebug Lwarn Linfo ) var lock sync.Mutex var PROD = true var STACKSKIP = 0 var LEVEL = Lerror | Linfo type H map[string]interface{} type Ldata struct { Message string Time time.Time Level Llevel // Caller only evaluates in DEBUG, and ERROR calls Caller string // Stack only eval when !PROD and {DEBUG, ERROR} Stack string Fields H Color bool } type Logger struct { System string color bool explicit bool err io.Writer out io.Writer printer Printer subs []*Logger } type Printer func(io.Writer, *Ldata, *Logger) func DefaultPrinter() Printer { return func(w io.Writer, d *Ldata, l *Logger) { var level string caller := "" sys := "" if l.System != "" { sys = fmt.Sprintf("(%s)", l.System) } fields := "" if d.Fields != nil { b, err := json.Marshal(d.Fields) if err != nil { fields = l.M(string(b), FgHiGreen) } } stack := "" if d.Stack != "" { stack = l.M(d.Stack, FgRed) } switch d.Level { case Lerror: level = l.M("ERROR", FgHiRed) caller = d.Caller case Ldebug: level = l.M("DEBUG", FgHiMagenta) caller = d.Caller case Lwarn: level = l.M("WARN ", FgHiYellow) case Linfo: level = l.M("INFO ", FgHiBlue) } _, err := fmt.Fprintf(w, "%s [%s]%s%s %s%s\n%s", d.Time.Format("2006/01/02 15:04:05"), level, l.M(sys, FgHiCyan), caller, d.Message, fields, stack, ) if err != nil { fmt.Println(err) } } } func (l *Logger) output(lev Llevel, depth int, fields H, v ...interface{}) { if LEVEL&lev == 0 { return } msg := fmt.Sprint(v...) data := &Ldata{ Fields: fields, Message: msg, Level: lev, Time: time.Now(), Color: l.color, } if !PROD { switch lev { case Lerror: if STACKSKIP == 0 { data.Stack = Stack() } else if STACKSKIP > 0 { data.Stack = StackSkip(STACKSKIP) } data.Caller = Caller(depth) case Ldebug: data.Caller = Caller(depth) // disable stack trace on debug // data.Stack = Stack() } } lock.Lock() defer lock.Unlock() if lev == Lerror || lev == Ldebug { l.printer(l.err, data, l) } else { l.printer(l.out, data, l) } } func New(name string) *Logger { l := &Logger{ System: name, err: os.Stderr, out: os.Stdout, color: true, printer: DefaultPrinter(), } l.guessColor() return l } func (l *Logger) Sub(sys string) *Logger { ret := &Logger{ System: sys, err: l.err, out: l.out, color: l.color, printer: l.printer, } if l.subs == nil { l.subs = make([]*Logger, 0) } l.subs = append(l.subs, ret) return ret } func (l *Logger) guessColor() { l.color = true if w, ok := l.out.(*os.File); !ok || os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) { l.color = false } if w, ok := l.err.(*os.File); !ok || os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) { l.color = false } } func (l *Logger) SetColor(c bool) { l.explicit = true l.color = c } func (l *Logger) SetColorAll(c bool) { for _, v := range getAllOffsprings(l) { v.SetColor(c) } } func (l *Logger) SetErrOutput(err io.Writer) { l.err = err l.guessColor() } func (l *Logger) SetErrOutputAll(err io.Writer) { for _, v := range getAllOffsprings(l) { v.SetErrOutput(err) } } func (l *Logger) SetOutput(out io.Writer) { l.out = out l.guessColor() } func (l *Logger) SetOutputAll(out io.Writer) { for _, v := range getAllOffsprings(l) { v.SetOutput(out) } } func (l *Logger) GetPrinter() Printer { return l.printer } func (l *Logger) SetPrinter(p Printer) { l.printer = p } func (l *Logger) SetPrinterAll(p Printer) { for _, v := range getAllOffsprings(l) { v.SetPrinter(p) } } // func (l *Logger) DefaultFuncMap() template.FuncMap { // funcMap := template.FuncMap{ // "Time": func() string { return time.Now().Format("2006/01/02 15:04:05") }, // "json": func(i interface{}) (string, error) { r, err := json.Marshal(i); return string(r), err }, // } // return funcMap // } func (l *Logger) ErrorF(fields H, v ...interface{}) { l.output(Lerror, 3, fields, v...) } func (l *Logger) Error(v ...interface{}) { l.output(Lerror, 3, H{}, v...) } func (l *Logger) DebugF(fields H, v ...interface{}) { l.output(Ldebug, 3, fields, v...) } func (l *Logger) Debug(v ...interface{}) { l.output(Ldebug, 3, H{}, v...) } func (l *Logger) WarnF(fields H, v ...interface{}) { l.output(Lwarn, 3, fields, v...) } func (l *Logger) Warn(v ...interface{}) { l.output(Lwarn, 3, H{}, v...) } func (l *Logger) InfoF(fields H, v ...interface{}) { l.output(Linfo, 3, fields, v...) } func (l *Logger) Info(v ...interface{}) { l.output(Linfo, 3, H{}, v...) }