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 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() { if w, ok := l.out.(*os.File); !ok || os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) { l.color = false } } func (l *logger) Color(c bool) { l.color = c } func NewLogFormater() *LogFormater { return &LogFormater{ ErrTmplStr: DEFAULT_ERR, WarnTmplStr: DEFAULT_WARN, InfoTmplStr: DEFAULT_INFO, DebugTmplStr: DEFAULT_DEBUG, } } func (l *logger) FuncMap() 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 { funcMap := l.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) error { l.formatter = formatter return l.ParseTmpl() } 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: err = l.err_tmpl.Execute(l.err, data) case tdebug: err = l.debug_tmpl.Execute(l.err, data) case twarn: err = l.warn_tmpl.Execute(l.out, data) case tinfo: 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...) }