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.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, } 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...) }