416 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			416 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package log
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"path"
 | |
| 	"runtime"
 | |
| 	"strconv"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/mattn/go-isatty"
 | |
| 	"github.com/valyala/fasttemplate"
 | |
| 
 | |
| 	"github.com/labstack/gommon/color"
 | |
| )
 | |
| 
 | |
| type (
 | |
| 	Logger struct {
 | |
| 		prefix     string
 | |
| 		level      Lvl
 | |
| 		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:    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 l.level
 | |
| }
 | |
| 
 | |
| func (l *Logger) SetLevel(v Lvl) {
 | |
| 	l.level = v
 | |
| }
 | |
| 
 | |
| 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(v Lvl) {
 | |
| 	global.SetLevel(v)
 | |
| }
 | |
| 
 | |
| 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(v Lvl, format string, args ...interface{}) {
 | |
| 	l.mutex.Lock()
 | |
| 	defer l.mutex.Unlock()
 | |
| 	buf := l.bufferPool.Get().(*bytes.Buffer)
 | |
| 	buf.Reset()
 | |
| 	defer l.bufferPool.Put(buf)
 | |
| 	_, file, line, _ := runtime.Caller(l.skip)
 | |
| 
 | |
| 	if v >= l.level || v == 0 {
 | |
| 		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[v]))
 | |
| 			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 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
 | |
| 				buf.WriteByte(' ')
 | |
| 				buf.WriteString(message)
 | |
| 			}
 | |
| 			buf.WriteByte('\n')
 | |
| 			l.output.Write(buf.Bytes())
 | |
| 		}
 | |
| 	}
 | |
| }
 |