Compare commits

...

1 Commits

Author SHA1 Message Date
Evan Chen bd9a99414c update 2021-11-02 00:09:02 +08:00
6 changed files with 309 additions and 70 deletions

View File

@ -1,5 +1,7 @@
package log package log
import "text/template"
type Attribute int type Attribute int
// Base attributes // Base attributes
@ -63,3 +65,29 @@ const (
BgHiCyan BgHiCyan
BgHiWhite BgHiWhite
) )
func setColorMap(funcMap template.FuncMap, color bool) {
if color {
funcMap["red"] = func() string { return "\033[91m" }
funcMap["redl"] = func() string { return "\033[31m" }
funcMap["green"] = func() string { return "\033[92m" }
funcMap["yellow"] = func() string { return "\033[93m" }
funcMap["blue"] = func() string { return "\033[94m" }
funcMap["magenta"] = func() string { return "\033[95m" }
funcMap["cyan"] = func() string { return "\033[96m" }
funcMap["white"] = func() string { return "\033[97m" }
funcMap["reset"] = func() string { return "\033[0m" }
return
}
funcMap["red"] = func() string { return "" }
funcMap["redl"] = func() string { return "" }
funcMap["green"] = func() string { return "" }
funcMap["yellow"] = func() string { return "" }
funcMap["blue"] = func() string { return "" }
funcMap["magenta"] = func() string { return "" }
funcMap["cyan"] = func() string { return "" }
funcMap["white"] = func() string { return "" }
funcMap["reset"] = func() string { return "" }
}
// func

7
go.mod
View File

@ -2,7 +2,6 @@ module kumoly.io/core/log
go 1.17 go 1.17
require ( require github.com/mattn/go-isatty v0.0.14
github.com/mattn/go-isatty v0.0.14 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect require golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
)

184
log.go
View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"runtime"
"text/template" "text/template"
"time" "time"
@ -13,19 +12,42 @@ import (
const ( const (
Lerror = 1 << iota Lerror = 1 << iota
Ldebug
Lwarn Lwarn
Linfo Linfo
Ldebug )
type tout int
const (
terror tout = iota
tdebug
tinfo
twarn
) )
var PROD = true var PROD = true
var LEVEL = Lerror | Linfo var LEVEL = Lerror | Linfo
const ( const (
DEFAULT_ERR = `{{Time}} [{{red}}ERROR{{reset}}]({{cyan}}{{.System}}{{reset}}) {{.Caller}} {{.Message}}{{if .Fields}}{{printf "%v\n" .Fields}}{{end}}{{if .Stack}}{{redl}}{{.Stack}}{{reset}}{{end}}` DEFAULT_ERR = `{{Time}} [{{red}}ERROR{{reset}}]` +
DEFAULT_DEBUG = `{{Time}} [{{magenta}}DEBUG{{reset}}]({{cyan}}{{.System}}{{reset}}) {{.Caller}} {{.Message}}{{if .Fields}}{{printf "%v\n" .Fields}}{{end}}{{if .Stack}}{{.Stack}}{{end}}` `{{if .System}}({{cyan}}{{.System}}{{reset}}){{end}} ` +
DEFAULT_WARN = `{{Time}} [{{yellow}}WARN {{reset}}]({{cyan}}{{.System}}{{reset}}) {{.Message}}{{if .Fields}}{{printf "%v\n" .Fields}}{{end}}` `{{.Caller}} {{.Message}}{{"\n"}}` +
DEFAULT_INFO = `{{Time}} [{{blue}}INFO {{reset}}]({{cyan}}{{.System}}{{reset}}) {{.Message}}{{if .Fields}}{{printf "%v\n" .Fields}}{{end}}` `{{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 { type LogFormater struct {
@ -58,30 +80,48 @@ type logger struct {
err io.Writer err io.Writer
out io.Writer out io.Writer
formatter *LogFormater
err_tmpl *template.Template err_tmpl *template.Template
debug_tmpl *template.Template
warn_tmpl *template.Template warn_tmpl *template.Template
info_tmpl *template.Template info_tmpl *template.Template
debug_tmpl *template.Template
} }
func New() *logger { func Default() *logger {
l := &logger{ l := &logger{
err: os.Stderr, err: os.Stderr,
out: os.Stdout, out: os.Stdout,
color: true, color: true,
formatter: NewLogFormater(),
} }
l.guessColor() l.guessColor()
l.SetTmpl(NewLogFormater()) l.ParseTmpl()
return l 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() { func (l *logger) guessColor() {
if w, ok := l.out.(*os.File); !ok || os.Getenv("TERM") == "dumb" || if w, ok := l.out.(*os.File); !ok || os.Getenv("TERM") == "dumb" ||
(!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) { (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {
l.color = false l.color = false
} }
} }
func (l *logger) SetColor(c bool) { func (l *logger) Color(c bool) {
l.color = c l.color = c
} }
@ -94,29 +134,46 @@ func NewLogFormater() *LogFormater {
} }
} }
func (l *logger) SetTmpl(tmpl *LogFormater) error { func (l *logger) FuncMap() template.FuncMap {
funcMap := template.FuncMap{ funcMap := template.FuncMap{
"Time": func() string { return time.Now().Format("2006/01/02 15:04:05") }, "Time": func() string { return time.Now().Format("2006/01/02 15:04:05") },
"red": func() string { return "\033[91m" },
"redl": func() string { return "\033[31m" },
"green": func() string { return "\033[92m" },
"yellow": func() string { return "\033[93m" },
"blue": func() string { return "\033[94m" },
"magenta": func() string { return "\033[95m" },
"cyan": func() string { return "\033[96m" },
"white": func() string { return "\033[97m" },
"reset": func() string { return "\033[0m" },
} }
err_tmpl, err := template.New("err_tmpl").Funcs(funcMap).Parse(tmpl.ErrTmplStr) 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 { if err != nil {
return err return err
} }
l.err_tmpl = err_tmpl 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 return nil
} }
func (l *logger) error(depth int, stack string, fields H, v ...interface{}) { func (l *logger) SetTmpl(formatter *LogFormater) error {
msg := fmt.Sprintln(v...) 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{ data := Ldata{
Fields: fields, Fields: fields,
Message: msg, Message: msg,
@ -125,48 +182,67 @@ func (l *logger) error(depth int, stack string, fields H, v ...interface{}) {
Color: l.color, Color: l.color,
Stack: stack, Stack: stack,
} }
err := l.err_tmpl.Execute(os.Stdout, data) 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 { if err != nil {
fmt.Fprintln(l.err, "[FATAL] logger error:", err)
panic(err) panic(err)
} }
} }
func (l *logger) ErrorF(fields H, v ...interface{}) { func (l *logger) ErrorF(fields H, v ...interface{}) {
stak := "" if PROD {
if !PROD { l.output(terror, 3, "", fields, v...)
stak = stack() } else {
l.output(terror, 3, stack(), fields, v...)
} }
l.error(3, stak, fields, v...)
} }
func (l *logger) Error(v ...interface{}) { func (l *logger) Error(v ...interface{}) {
stak := "" if PROD {
if !PROD { l.output(terror, 3, "", H{}, v...)
stak = stack() } else {
l.output(terror, 3, stack(), H{}, v...)
} }
l.error(3, stak, H{}, v...)
} }
func caller(depth int) string { func (l *logger) DebugF(fields H, v ...interface{}) {
_, file, line, _ := runtime.Caller(depth) if PROD {
short := file l.output(tdebug, 3, "", fields, v...)
for i := len(file) - 1; i > 0; i-- { } else {
if file[i] == '/' { l.output(tdebug, 3, stack(), fields, v...)
short = file[i+1:]
break
}
} }
return fmt.Sprintf("%s:%d", short, line)
} }
func stack() string { func (l *logger) Debug(v ...interface{}) {
buf := make([]byte, 1024) if PROD {
for { l.output(tdebug, 3, "", H{}, v...)
n := runtime.Stack(buf, false) } else {
if n < len(buf) { l.output(tdebug, 3, stack(), H{}, v...)
return string(buf[:n])
}
buf = make([]byte, 2*len(buf))
} }
} }
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...)
}

View File

@ -2,20 +2,72 @@ package log
import "testing" import "testing"
func TestError(t *testing.T) { func TestDev(t *testing.T) {
PROD = false PROD = false
log := New() // log.system = "dev"
log.system = "test" Error("d", "sdf", "sdfsdf")
log.Error("d", "sdf", "sdfsdf") ErrorF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
Debug("d", "sdf", "sdfsdf")
log.ErrorF(H{"Test": "set"}, "d", "sdf", "sdfsdf") DebugF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
Warn("d", "sdf", "sdfsdf")
WarnF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
Info("d", "sdf", "sdfsdf")
InfoF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
} }
func TestErrorProd(t *testing.T) { func TestProd(t *testing.T) {
PROD = true PROD = true
log := New() // log.system = "prod"
log.system = "test" Error("d", "sdf", "sdfsdf")
log.Error("d", "sdf", "sdfsdf") ErrorF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
Debug("d", "sdf", "sdfsdf")
log.ErrorF(H{"Test": "set"}, "d", "sdf", "sdfsdf") DebugF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
Warn("d", "sdf", "sdfsdf")
WarnF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
Info("d", "sdf", "sdfsdf")
InfoF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
}
func TestSubDev(t *testing.T) {
PROD = false
l := Sub("TestSubDev")
l.Error("d", "sdf", "sdfsdf")
l.ErrorF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
l.Debug("d", "sdf", "sdfsdf")
l.DebugF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
l.Warn("d", "sdf", "sdfsdf")
l.WarnF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
l.Info("d", "sdf", "sdfsdf")
l.InfoF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
}
func TestSubProd(t *testing.T) {
PROD = true
l := Sub("TestSubProd")
l.Error("d", "sdf", "sdfsdf")
l.ErrorF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
l.Debug("d", "sdf", "sdfsdf")
l.DebugF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
l.Warn("d", "sdf", "sdfsdf")
l.WarnF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
l.Info("d", "sdf", "sdfsdf")
l.InfoF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
}
func TestCustTmpl(t *testing.T) {
PROD = true
l := Sub("TestCustTmpl")
CustFormater := NewLogFormater()
CustFormater.InfoTmplStr = `{{Time}} [{{blue}}INFO {{reset}}]` +
`{{if .System}}({{cyan}}{{.System}}{{reset}}){{end}} ` +
`key: test, value:{{.Fields.Test}}{{"\n"}}`
l.SetTmpl(CustFormater)
l.Error("d", "sdf", "sdfsdf")
l.ErrorF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
l.Debug("d", "sdf", "sdfsdf")
l.DebugF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
l.Warn("d", "sdf", "sdfsdf")
l.WarnF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
l.Info("d", "sdf", "sdfsdf")
l.InfoF(H{"Test": "set"}, "d", "sdf", "sdfsdf")
} }

55
std.go Normal file
View File

@ -0,0 +1,55 @@
package log
var std = Default()
func Sub(sys string) *logger {
return std.Sub(sys)
}
func ErrorF(fields H, v ...interface{}) {
if PROD {
std.output(terror, 3, "", fields, v...)
} else {
std.output(terror, 3, stack(), fields, v...)
}
}
func Error(v ...interface{}) {
if PROD {
std.output(terror, 3, "", H{}, v...)
} else {
std.output(terror, 3, stack(), H{}, v...)
}
}
func DebugF(fields H, v ...interface{}) {
if PROD {
std.output(tdebug, 3, "", fields, v...)
} else {
std.output(tdebug, 3, stack(), fields, v...)
}
}
func Debug(v ...interface{}) {
if PROD {
std.output(tdebug, 3, "", H{}, v...)
} else {
std.output(tdebug, 3, stack(), H{}, v...)
}
}
func WarnF(fields H, v ...interface{}) {
std.output(twarn, 3, "", fields, v...)
}
func Warn(v ...interface{}) {
std.output(twarn, 3, "", H{}, v...)
}
func InfoF(fields H, v ...interface{}) {
std.output(tinfo, 3, "", fields, v...)
}
func Info(v ...interface{}) {
std.output(tinfo, 3, "", H{}, v...)
}

29
util.go Normal file
View File

@ -0,0 +1,29 @@
package log
import (
"fmt"
"runtime"
)
func caller(depth int) string {
_, file, line, _ := runtime.Caller(depth)
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
return fmt.Sprintf("%s:%d", short, line)
}
func stack() string {
buf := make([]byte, 1024)
for {
n := runtime.Stack(buf, false)
if n < len(buf) {
return string(buf[:n])
}
buf = make([]byte, 2*len(buf))
}
}