forked from ebhomengo/niki
419 lines
7.2 KiB
Go
419 lines
7.2 KiB
Go
|
package log
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"os"
|
||
|
"path"
|
||
|
"runtime"
|
||
|
"strconv"
|
||
|
"sync"
|
||
|
"sync/atomic"
|
||
|
"time"
|
||
|
|
||
|
"github.com/mattn/go-isatty"
|
||
|
"github.com/valyala/fasttemplate"
|
||
|
|
||
|
"github.com/labstack/gommon/color"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
Logger struct {
|
||
|
prefix string
|
||
|
level uint32
|
||
|
skip int
|
||
|
output io.Writer
|
||
|
template *fasttemplate.Template
|
||
|
levels []string
|
||
|
color *color.Color
|
||
|
bufferPool sync.Pool
|
||
|
mutex sync.Mutex
|
||
|
}
|
||
|
|
||
|
Lvl uint8
|
||
|
|
||
|
JSON map[string]interface{}
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
DEBUG Lvl = iota + 1
|
||
|
INFO
|
||
|
WARN
|
||
|
ERROR
|
||
|
OFF
|
||
|
panicLevel
|
||
|
fatalLevel
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
global = New("-")
|
||
|
defaultHeader = `{"time":"${time_rfc3339_nano}","level":"${level}","prefix":"${prefix}",` +
|
||
|
`"file":"${short_file}","line":"${line}"}`
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
global.skip = 3
|
||
|
}
|
||
|
|
||
|
func New(prefix string) (l *Logger) {
|
||
|
l = &Logger{
|
||
|
level: uint32(INFO),
|
||
|
skip: 2,
|
||
|
prefix: prefix,
|
||
|
template: l.newTemplate(defaultHeader),
|
||
|
color: color.New(),
|
||
|
bufferPool: sync.Pool{
|
||
|
New: func() interface{} {
|
||
|
return bytes.NewBuffer(make([]byte, 256))
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
l.initLevels()
|
||
|
l.SetOutput(output())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (l *Logger) initLevels() {
|
||
|
l.levels = []string{
|
||
|
"-",
|
||
|
l.color.Blue("DEBUG"),
|
||
|
l.color.Green("INFO"),
|
||
|
l.color.Yellow("WARN"),
|
||
|
l.color.Red("ERROR"),
|
||
|
"",
|
||
|
l.color.Yellow("PANIC", color.U),
|
||
|
l.color.Red("FATAL", color.U),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (l *Logger) newTemplate(format string) *fasttemplate.Template {
|
||
|
return fasttemplate.New(format, "${", "}")
|
||
|
}
|
||
|
|
||
|
func (l *Logger) DisableColor() {
|
||
|
l.color.Disable()
|
||
|
l.initLevels()
|
||
|
}
|
||
|
|
||
|
func (l *Logger) EnableColor() {
|
||
|
l.color.Enable()
|
||
|
l.initLevels()
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Prefix() string {
|
||
|
return l.prefix
|
||
|
}
|
||
|
|
||
|
func (l *Logger) SetPrefix(p string) {
|
||
|
l.prefix = p
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Level() Lvl {
|
||
|
return Lvl(atomic.LoadUint32(&l.level))
|
||
|
}
|
||
|
|
||
|
func (l *Logger) SetLevel(level Lvl) {
|
||
|
atomic.StoreUint32(&l.level, uint32(level))
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Output() io.Writer {
|
||
|
return l.output
|
||
|
}
|
||
|
|
||
|
func (l *Logger) SetOutput(w io.Writer) {
|
||
|
l.output = w
|
||
|
if w, ok := w.(*os.File); !ok || !isatty.IsTerminal(w.Fd()) {
|
||
|
l.DisableColor()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Color() *color.Color {
|
||
|
return l.color
|
||
|
}
|
||
|
|
||
|
func (l *Logger) SetHeader(h string) {
|
||
|
l.template = l.newTemplate(h)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Print(i ...interface{}) {
|
||
|
l.log(0, "", i...)
|
||
|
// fmt.Fprintln(l.output, i...)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Printf(format string, args ...interface{}) {
|
||
|
l.log(0, format, args...)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Printj(j JSON) {
|
||
|
l.log(0, "json", j)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Debug(i ...interface{}) {
|
||
|
l.log(DEBUG, "", i...)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Debugf(format string, args ...interface{}) {
|
||
|
l.log(DEBUG, format, args...)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Debugj(j JSON) {
|
||
|
l.log(DEBUG, "json", j)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Info(i ...interface{}) {
|
||
|
l.log(INFO, "", i...)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Infof(format string, args ...interface{}) {
|
||
|
l.log(INFO, format, args...)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Infoj(j JSON) {
|
||
|
l.log(INFO, "json", j)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Warn(i ...interface{}) {
|
||
|
l.log(WARN, "", i...)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Warnf(format string, args ...interface{}) {
|
||
|
l.log(WARN, format, args...)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Warnj(j JSON) {
|
||
|
l.log(WARN, "json", j)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Error(i ...interface{}) {
|
||
|
l.log(ERROR, "", i...)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Errorf(format string, args ...interface{}) {
|
||
|
l.log(ERROR, format, args...)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Errorj(j JSON) {
|
||
|
l.log(ERROR, "json", j)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Fatal(i ...interface{}) {
|
||
|
l.log(fatalLevel, "", i...)
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Fatalf(format string, args ...interface{}) {
|
||
|
l.log(fatalLevel, format, args...)
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Fatalj(j JSON) {
|
||
|
l.log(fatalLevel, "json", j)
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Panic(i ...interface{}) {
|
||
|
l.log(panicLevel, "", i...)
|
||
|
panic(fmt.Sprint(i...))
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Panicf(format string, args ...interface{}) {
|
||
|
l.log(panicLevel, format, args...)
|
||
|
panic(fmt.Sprintf(format, args...))
|
||
|
}
|
||
|
|
||
|
func (l *Logger) Panicj(j JSON) {
|
||
|
l.log(panicLevel, "json", j)
|
||
|
panic(j)
|
||
|
}
|
||
|
|
||
|
func DisableColor() {
|
||
|
global.DisableColor()
|
||
|
}
|
||
|
|
||
|
func EnableColor() {
|
||
|
global.EnableColor()
|
||
|
}
|
||
|
|
||
|
func Prefix() string {
|
||
|
return global.Prefix()
|
||
|
}
|
||
|
|
||
|
func SetPrefix(p string) {
|
||
|
global.SetPrefix(p)
|
||
|
}
|
||
|
|
||
|
func Level() Lvl {
|
||
|
return global.Level()
|
||
|
}
|
||
|
|
||
|
func SetLevel(level Lvl) {
|
||
|
global.SetLevel(level)
|
||
|
}
|
||
|
|
||
|
func Output() io.Writer {
|
||
|
return global.Output()
|
||
|
}
|
||
|
|
||
|
func SetOutput(w io.Writer) {
|
||
|
global.SetOutput(w)
|
||
|
}
|
||
|
|
||
|
func SetHeader(h string) {
|
||
|
global.SetHeader(h)
|
||
|
}
|
||
|
|
||
|
func Print(i ...interface{}) {
|
||
|
global.Print(i...)
|
||
|
}
|
||
|
|
||
|
func Printf(format string, args ...interface{}) {
|
||
|
global.Printf(format, args...)
|
||
|
}
|
||
|
|
||
|
func Printj(j JSON) {
|
||
|
global.Printj(j)
|
||
|
}
|
||
|
|
||
|
func Debug(i ...interface{}) {
|
||
|
global.Debug(i...)
|
||
|
}
|
||
|
|
||
|
func Debugf(format string, args ...interface{}) {
|
||
|
global.Debugf(format, args...)
|
||
|
}
|
||
|
|
||
|
func Debugj(j JSON) {
|
||
|
global.Debugj(j)
|
||
|
}
|
||
|
|
||
|
func Info(i ...interface{}) {
|
||
|
global.Info(i...)
|
||
|
}
|
||
|
|
||
|
func Infof(format string, args ...interface{}) {
|
||
|
global.Infof(format, args...)
|
||
|
}
|
||
|
|
||
|
func Infoj(j JSON) {
|
||
|
global.Infoj(j)
|
||
|
}
|
||
|
|
||
|
func Warn(i ...interface{}) {
|
||
|
global.Warn(i...)
|
||
|
}
|
||
|
|
||
|
func Warnf(format string, args ...interface{}) {
|
||
|
global.Warnf(format, args...)
|
||
|
}
|
||
|
|
||
|
func Warnj(j JSON) {
|
||
|
global.Warnj(j)
|
||
|
}
|
||
|
|
||
|
func Error(i ...interface{}) {
|
||
|
global.Error(i...)
|
||
|
}
|
||
|
|
||
|
func Errorf(format string, args ...interface{}) {
|
||
|
global.Errorf(format, args...)
|
||
|
}
|
||
|
|
||
|
func Errorj(j JSON) {
|
||
|
global.Errorj(j)
|
||
|
}
|
||
|
|
||
|
func Fatal(i ...interface{}) {
|
||
|
global.Fatal(i...)
|
||
|
}
|
||
|
|
||
|
func Fatalf(format string, args ...interface{}) {
|
||
|
global.Fatalf(format, args...)
|
||
|
}
|
||
|
|
||
|
func Fatalj(j JSON) {
|
||
|
global.Fatalj(j)
|
||
|
}
|
||
|
|
||
|
func Panic(i ...interface{}) {
|
||
|
global.Panic(i...)
|
||
|
}
|
||
|
|
||
|
func Panicf(format string, args ...interface{}) {
|
||
|
global.Panicf(format, args...)
|
||
|
}
|
||
|
|
||
|
func Panicj(j JSON) {
|
||
|
global.Panicj(j)
|
||
|
}
|
||
|
|
||
|
func (l *Logger) log(level Lvl, format string, args ...interface{}) {
|
||
|
if level >= l.Level() || level == 0 {
|
||
|
buf := l.bufferPool.Get().(*bytes.Buffer)
|
||
|
buf.Reset()
|
||
|
defer l.bufferPool.Put(buf)
|
||
|
_, file, line, _ := runtime.Caller(l.skip)
|
||
|
message := ""
|
||
|
|
||
|
if format == "" {
|
||
|
message = fmt.Sprint(args...)
|
||
|
} else if format == "json" {
|
||
|
b, err := json.Marshal(args[0])
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
message = string(b)
|
||
|
} else {
|
||
|
message = fmt.Sprintf(format, args...)
|
||
|
}
|
||
|
|
||
|
_, err := l.template.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) {
|
||
|
switch tag {
|
||
|
case "time_rfc3339":
|
||
|
return w.Write([]byte(time.Now().Format(time.RFC3339)))
|
||
|
case "time_rfc3339_nano":
|
||
|
return w.Write([]byte(time.Now().Format(time.RFC3339Nano)))
|
||
|
case "level":
|
||
|
return w.Write([]byte(l.levels[level]))
|
||
|
case "prefix":
|
||
|
return w.Write([]byte(l.prefix))
|
||
|
case "long_file":
|
||
|
return w.Write([]byte(file))
|
||
|
case "short_file":
|
||
|
return w.Write([]byte(path.Base(file)))
|
||
|
case "line":
|
||
|
return w.Write([]byte(strconv.Itoa(line)))
|
||
|
}
|
||
|
return 0, nil
|
||
|
})
|
||
|
|
||
|
if err == nil {
|
||
|
s := buf.String()
|
||
|
i := buf.Len() - 1
|
||
|
if i >= 0 && s[i] == '}' {
|
||
|
// JSON header
|
||
|
buf.Truncate(i)
|
||
|
buf.WriteByte(',')
|
||
|
if format == "json" {
|
||
|
buf.WriteString(message[1:])
|
||
|
} else {
|
||
|
buf.WriteString(`"message":`)
|
||
|
buf.WriteString(strconv.Quote(message))
|
||
|
buf.WriteString(`}`)
|
||
|
}
|
||
|
} else {
|
||
|
// Text header
|
||
|
if len(s) > 0 {
|
||
|
buf.WriteByte(' ')
|
||
|
}
|
||
|
buf.WriteString(message)
|
||
|
}
|
||
|
buf.WriteByte('\n')
|
||
|
l.mutex.Lock()
|
||
|
defer l.mutex.Unlock()
|
||
|
l.output.Write(buf.Bytes())
|
||
|
}
|
||
|
}
|
||
|
}
|