klog/log.go

284 lines
5.8 KiB
Go

package log
import (
"fmt"
"io"
"os"
"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 PROD = true
var LEVEL = Lerror | Linfo
const (
DEFAULT_ERR = `{{Time}} [{{red}}ERROR{{reset}}]` +
`{{if .System}}({{cyan}}{{.System}}{{reset}}){{end}} ` +
`{{.Caller}} {{.Message}}{{"\n"}}` +
`{{if .Fields}}{{printf "%v\n" .Fields}}{{end}}` +
`{{if .Stack}}{{redl}}{{.Stack}}{{reset}}{{end}}`
DEFAULT_DEBUG = `{{Time}} [{{magenta}}DEBUG{{reset}}]` +
`{{if .System}}({{cyan}}{{.System}}{{reset}}){{end}} ` +
`{{.Caller}} {{.Message}}{{"\n"}}` +
`{{if .Fields}}{{green}}{{printf "%v\n" .Fields}}{{reset}}{{end}}` +
`{{if .Stack}}{{redl}}{{.Stack}}{{reset}}{{end}}`
DEFAULT_WARN = `{{Time}} [{{yellow}}WARN {{reset}}]` +
`{{if .System}}({{cyan}}{{.System}}{{reset}}){{end}} ` +
`{{.Message}}{{"\n"}}` +
`{{if .Fields}}{{green}}{{printf "%v\n" .Fields}}{{reset}}{{end}}`
DEFAULT_INFO = `{{Time}} [{{blue}}INFO {{reset}}]` +
`{{if .System}}({{cyan}}{{.System}}{{reset}}){{end}} ` +
`{{.Message}}{{"\n"}}` +
`{{if .Fields}}{{green}}{{printf "%v\n" .Fields}}{{reset}}{{end}}`
)
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
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
}
func Default() *logger {
l := &logger{
err: os.Stderr,
out: os.Stdout,
color: true,
formatter: NewLogFormater(),
}
l.guessColor()
l.ParseTmpl()
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,
}
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.color = c
}
func (l *logger) SetErrOutput(err io.Writer) {
l.err = err
}
func (l *logger) SetOutput(out io.Writer) {
l.out = out
}
func (l *logger) Reload() error {
l.guessColor()
return l.ParseTmpl()
}
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") },
}
setColorMap(funcMap, l.color)
return funcMap
}
func (l *logger) ParseTmpl() error {
if l.funcMap == nil {
l.funcMap = l.DefaultFuncMap()
}
err_tmpl, err := template.New("err_tmpl").Funcs(l.funcMap).Parse(l.formatter.ErrTmplStr)
if err != nil {
return err
}
l.err_tmpl = err_tmpl
warn_tmpl, err := template.New("warn_tmpl").Funcs(l.funcMap).Parse(l.formatter.WarnTmplStr)
if err != nil {
return err
}
l.warn_tmpl = warn_tmpl
info_tmpl, err := template.New("info_tmpl").Funcs(l.funcMap).Parse(l.formatter.InfoTmplStr)
if err != nil {
return err
}
l.info_tmpl = info_tmpl
debug_tmpl, err := template.New("debug_tmpl").Funcs(l.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,
}
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...)
}