330 lines
6.5 KiB
Go
330 lines
6.5 KiB
Go
package klog
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"sync"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/mattn/go-isatty"
|
|
)
|
|
|
|
const (
|
|
Lerror = 1 << iota
|
|
Ldebug
|
|
Lwarn
|
|
Linfo
|
|
)
|
|
|
|
type tout int
|
|
|
|
const (
|
|
terror tout = iota
|
|
tdebug
|
|
tinfo
|
|
twarn
|
|
)
|
|
|
|
var lock sync.Mutex
|
|
|
|
var PROD = true
|
|
var LEVEL = Lerror | Linfo
|
|
|
|
const (
|
|
DEFAULT_ERR = `{{Time}} [{{"ERROR"|red}}]` +
|
|
`{{if .System}}({{.System|cyan}}){{end}} ` +
|
|
`{{.Caller}} {{.Message}}` +
|
|
`{{if .Fields}} {{.Fields|json|green}}{{end}}{{"\n"}}` +
|
|
`{{if .Stack}}{{.Stack|redl}}{{end}}`
|
|
DEFAULT_DEBUG = `{{Time}} [{{"DEBUG"|magenta}}]` +
|
|
`{{if .System}}({{.System|cyan}}){{end}} ` +
|
|
`{{.Caller}} {{.Message}}` +
|
|
`{{if .Fields}} {{.Fields|json|green}}{{end}}{{"\n"}}` +
|
|
`{{if .Stack}}{{.Stack|redl}}{{end}}`
|
|
DEFAULT_WARN = `{{Time}} [{{"WARN"|yellow}} ]` +
|
|
`{{if .System}}({{.System|cyan}}){{end}} ` +
|
|
`{{.Message}}` +
|
|
`{{if .Fields}} {{.Fields|json|green}}{{end}}{{"\n"}}`
|
|
DEFAULT_INFO = `{{Time}} [{{"INFO"|blue}} ]` +
|
|
`{{if .System}}({{.System|cyan}}){{end}} ` +
|
|
`{{.Message}}` +
|
|
`{{if .Fields}} {{.Fields|json|green}}{{end}}{{"\n"}}`
|
|
)
|
|
|
|
type LogFormater struct {
|
|
ErrTmplStr string
|
|
WarnTmplStr string
|
|
InfoTmplStr string
|
|
DebugTmplStr string
|
|
}
|
|
|
|
type H map[string]interface{}
|
|
|
|
type Ldata struct {
|
|
Message string
|
|
System string
|
|
|
|
// 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
|
|
|
|
formatter *LogFormater
|
|
funcMap template.FuncMap
|
|
err_tmpl *template.Template
|
|
debug_tmpl *template.Template
|
|
warn_tmpl *template.Template
|
|
info_tmpl *template.Template
|
|
subs []*Logger
|
|
}
|
|
|
|
func New(name string) *Logger {
|
|
l := &Logger{
|
|
system: name,
|
|
err: os.Stderr,
|
|
out: os.Stdout,
|
|
color: true,
|
|
formatter: NewLogFormater(),
|
|
}
|
|
l.Reload()
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Sub(sys string) *Logger {
|
|
ret := &Logger{
|
|
system: sys,
|
|
err: l.err,
|
|
out: l.out,
|
|
color: l.color,
|
|
formatter: l.formatter,
|
|
|
|
err_tmpl: l.err_tmpl,
|
|
debug_tmpl: l.debug_tmpl,
|
|
warn_tmpl: l.warn_tmpl,
|
|
info_tmpl: l.info_tmpl,
|
|
}
|
|
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
|
|
}
|
|
|
|
func (l *Logger) SetErrOutputAll(err io.Writer) {
|
|
for _, v := range getAllOffsprings(l) {
|
|
v.err = err
|
|
}
|
|
}
|
|
|
|
func (l *Logger) SetOutput(out io.Writer) {
|
|
l.out = out
|
|
}
|
|
|
|
func (l *Logger) SetOutputAll(out io.Writer) {
|
|
for _, v := range getAllOffsprings(l) {
|
|
v.out = out
|
|
}
|
|
}
|
|
|
|
func (l *Logger) Reload() error {
|
|
if !l.explicit {
|
|
l.guessColor()
|
|
}
|
|
return l.ParseTmpl()
|
|
}
|
|
|
|
func (l *Logger) ReloadAll() error {
|
|
for _, v := range getAllOffsprings(l) {
|
|
if err := v.Reload(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func NewLogFormater() *LogFormater {
|
|
return &LogFormater{
|
|
ErrTmplStr: DEFAULT_ERR,
|
|
WarnTmplStr: DEFAULT_WARN,
|
|
InfoTmplStr: DEFAULT_INFO,
|
|
DebugTmplStr: DEFAULT_DEBUG,
|
|
}
|
|
}
|
|
|
|
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) ParseTmpl() error {
|
|
if l.funcMap == nil {
|
|
l.funcMap = l.DefaultFuncMap()
|
|
}
|
|
funcMap := copyFuncMap(l.funcMap)
|
|
l.setColorMap(funcMap)
|
|
|
|
err_tmpl, err := template.New("err_tmpl").Funcs(funcMap).Parse(l.formatter.ErrTmplStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.err_tmpl = err_tmpl
|
|
warn_tmpl, err := template.New("warn_tmpl").Funcs(funcMap).Parse(l.formatter.WarnTmplStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.warn_tmpl = warn_tmpl
|
|
info_tmpl, err := template.New("info_tmpl").Funcs(funcMap).Parse(l.formatter.InfoTmplStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.info_tmpl = info_tmpl
|
|
debug_tmpl, err := template.New("debug_tmpl").Funcs(funcMap).Parse(l.formatter.DebugTmplStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.debug_tmpl = debug_tmpl
|
|
return nil
|
|
}
|
|
|
|
func (l *Logger) SetTmpl(formatter *LogFormater, funcMap template.FuncMap) {
|
|
l.formatter = formatter
|
|
if funcMap != nil {
|
|
l.funcMap = funcMap
|
|
}
|
|
}
|
|
|
|
func (l *Logger) output(t tout, depth int, stack string, fields H, v ...interface{}) {
|
|
msg := fmt.Sprint(v...)
|
|
data := Ldata{
|
|
Fields: fields,
|
|
Message: msg,
|
|
System: l.system,
|
|
Caller: caller(depth),
|
|
Color: l.color,
|
|
Stack: stack,
|
|
}
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
var err error
|
|
switch t {
|
|
case terror:
|
|
if LEVEL&Lerror == 0 {
|
|
return
|
|
}
|
|
err = l.err_tmpl.Execute(l.err, data)
|
|
case tdebug:
|
|
if LEVEL&Ldebug == 0 {
|
|
return
|
|
}
|
|
err = l.debug_tmpl.Execute(l.err, data)
|
|
case twarn:
|
|
if LEVEL&Lwarn == 0 {
|
|
return
|
|
}
|
|
err = l.warn_tmpl.Execute(l.out, data)
|
|
case tinfo:
|
|
if LEVEL&Linfo == 0 {
|
|
return
|
|
}
|
|
err = l.info_tmpl.Execute(l.out, data)
|
|
}
|
|
if err != nil {
|
|
fmt.Fprintln(l.err, "[FATAL] Logger error:", err)
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func (l *Logger) ErrorF(fields H, v ...interface{}) {
|
|
if PROD {
|
|
l.output(terror, 3, "", fields, v...)
|
|
} else {
|
|
l.output(terror, 3, stack(), fields, v...)
|
|
}
|
|
}
|
|
|
|
func (l *Logger) Error(v ...interface{}) {
|
|
if PROD {
|
|
l.output(terror, 3, "", H{}, v...)
|
|
} else {
|
|
l.output(terror, 3, stack(), H{}, v...)
|
|
}
|
|
}
|
|
|
|
func (l *Logger) DebugF(fields H, v ...interface{}) {
|
|
if PROD {
|
|
l.output(tdebug, 3, "", fields, v...)
|
|
} else {
|
|
l.output(tdebug, 3, stack(), fields, v...)
|
|
}
|
|
}
|
|
|
|
func (l *Logger) Debug(v ...interface{}) {
|
|
if PROD {
|
|
l.output(tdebug, 3, "", H{}, v...)
|
|
} else {
|
|
l.output(tdebug, 3, stack(), H{}, v...)
|
|
}
|
|
}
|
|
|
|
func (l *Logger) WarnF(fields H, v ...interface{}) {
|
|
l.output(twarn, 3, "", fields, v...)
|
|
}
|
|
|
|
func (l *Logger) Warn(v ...interface{}) {
|
|
l.output(twarn, 3, "", H{}, v...)
|
|
}
|
|
|
|
func (l *Logger) InfoF(fields H, v ...interface{}) {
|
|
l.output(tinfo, 3, "", fields, v...)
|
|
}
|
|
|
|
func (l *Logger) Info(v ...interface{}) {
|
|
l.output(tinfo, 3, "", H{}, v...)
|
|
}
|