254 lines
4.5 KiB
Go
254 lines
4.5 KiB
Go
package klog
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/mattn/go-isatty"
|
|
"kumoly.io/lib/klog/color"
|
|
)
|
|
|
|
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", color.FgHiRed)
|
|
caller = d.Caller
|
|
case Ldebug:
|
|
level = l.M("DEBUG", color.FgHiMagenta)
|
|
caller = d.Caller
|
|
case Lwarn:
|
|
level = l.M("WARN ", color.FgHiYellow)
|
|
case Linfo:
|
|
level = l.M("INFO ", color.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, color.FgHiCyan),
|
|
caller, l.M(d.Message, color.FgHiGreen), fields,
|
|
l.M(d.Stack, color.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()
|
|
}
|
|
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) 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...)
|
|
}
|