klog/log.go

258 lines
4.6 KiB
Go

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 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 := []byte{}
if d.Fields != nil {
b, err := json.Marshal(d.Fields)
if err != nil {
fields = b
}
}
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, l.M(fields, FgHiGreen),
l.M(d.Stack, FgRed),
)
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(),
Caller: caller(depth),
Color: l.color,
}
switch lev {
case Lerror:
if !PROD {
data.Stack = stack()
}
// disable stack trace on debug
// case Ldebug:
// if !PROD {
// 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...)
}