332 lines
8.0 KiB
Go
Executable File
332 lines
8.0 KiB
Go
Executable File
package logger
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
DebugLevel uint8 = 1
|
|
InfoLevel uint8 = 1 << 1
|
|
WarnLevel uint8 = 1 << 2
|
|
ErrorLevel uint8 = 1 << 3
|
|
FatalLevel uint8 = 1 << 4
|
|
AllLevels uint8 = byte(DebugLevel | InfoLevel | WarnLevel | ErrorLevel | FatalLevel)
|
|
)
|
|
|
|
var (
|
|
flush = &sync.WaitGroup{}
|
|
exitchn = make(chan bool, 1)
|
|
)
|
|
|
|
type Fields map[string]string
|
|
type Writer interface {
|
|
Write(e Entry)
|
|
Init() error
|
|
}
|
|
|
|
type Logger struct {
|
|
writersMap *map[int][]*WrappedWriter
|
|
mu sync.Mutex
|
|
modeSafe bool
|
|
wait sync.WaitGroup
|
|
fields Fields
|
|
fieldsString string
|
|
}
|
|
|
|
type WrappedWriter struct {
|
|
lock *sync.Mutex
|
|
chn chan Entry
|
|
writer Writer
|
|
wait sync.WaitGroup
|
|
}
|
|
type Entry struct {
|
|
Content string
|
|
Timestamp int64
|
|
Milliseconds int64
|
|
TimestampMilliseconds int64
|
|
Level uint8
|
|
LevelString string
|
|
Fields map[string]string
|
|
FieldsString string
|
|
}
|
|
type MiniLogger interface {
|
|
Debug(v ...interface{}) MiniLogger
|
|
Info(v ...interface{}) MiniLogger
|
|
Warn(v ...interface{}) MiniLogger
|
|
Error(v ...interface{}) MiniLogger
|
|
Fatal(v ...interface{})
|
|
Debugf(format string, v ...interface{}) MiniLogger
|
|
Infof(format string, v ...interface{}) MiniLogger
|
|
Warnf(format string, v ...interface{}) MiniLogger
|
|
Errorf(format string, v ...interface{}) MiniLogger
|
|
Fatalf(format string, v ...interface{})
|
|
Debugln(v ...interface{}) MiniLogger
|
|
Infoln(v ...interface{}) MiniLogger
|
|
Warnln(v ...interface{}) MiniLogger
|
|
Errorln(v ...interface{}) MiniLogger
|
|
Fatalln(v ...interface{})
|
|
AddWriter(w Writer, levels uint8) MiniLogger
|
|
Safe() MiniLogger
|
|
Unsafe() MiniLogger
|
|
With(fields Fields) MiniLogger
|
|
}
|
|
|
|
func getLevelString(level uint8) string {
|
|
switch level {
|
|
case DebugLevel:
|
|
return "DEBUG"
|
|
case InfoLevel:
|
|
return "INFO"
|
|
case WarnLevel:
|
|
return "WARN"
|
|
case ErrorLevel:
|
|
return "ERROR"
|
|
case FatalLevel:
|
|
return "FATAL"
|
|
}
|
|
return "UNKOWN"
|
|
}
|
|
|
|
// New returns a new logger
|
|
//modeSafe when false : must call logger.Flush() in main defer
|
|
//if not do that, may be lost message,but this mode has a highest performance
|
|
//when true : each message can be processed , but this mode may be has a little lower performance
|
|
//beacuse of logger must wait for all writers process done with each messsage.
|
|
//note: if you do logging before call os.Exit(), you had better to set safeMode to true before call os.Exit().
|
|
//fields same as With()'s args,more about fields to see With().
|
|
func New(modeSafe bool, fields Fields) MiniLogger {
|
|
if fields == nil {
|
|
fields = Fields{}
|
|
}
|
|
return &Logger{
|
|
fields: fields,
|
|
fieldsString: "",
|
|
mu: sync.Mutex{},
|
|
modeSafe: modeSafe,
|
|
wait: sync.WaitGroup{},
|
|
writersMap: &map[int][]*WrappedWriter{
|
|
int(DebugLevel): []*WrappedWriter{},
|
|
int(InfoLevel): []*WrappedWriter{},
|
|
int(WarnLevel): []*WrappedWriter{},
|
|
int(ErrorLevel): []*WrappedWriter{},
|
|
int(FatalLevel): []*WrappedWriter{},
|
|
},
|
|
}
|
|
}
|
|
|
|
//Flush wait for process all left entry
|
|
func Flush() {
|
|
exitchn <- true
|
|
flush.Wait()
|
|
}
|
|
|
|
//With return a new MiniLogger,which it's modeSafe inited same as caller MiniLogger,
|
|
//and revecived levels same as caller MiniLogger,
|
|
//and set it's fields.
|
|
//you can use it as a standard MiniLogger.
|
|
func (l *Logger) With(fields Fields) MiniLogger {
|
|
for k, v := range l.fields {
|
|
fields[k] = v
|
|
}
|
|
s, _ := json.Marshal(fields)
|
|
return &Logger{
|
|
mu: sync.Mutex{},
|
|
modeSafe: l.modeSafe,
|
|
wait: sync.WaitGroup{},
|
|
writersMap: l.writersMap,
|
|
fields: fields,
|
|
fieldsString: string(s),
|
|
}
|
|
}
|
|
|
|
//Unsafe : if you call Unsafe() , then you must call logger.Flush() in main defer
|
|
//if not do that, may be lost message,but this mode has a highest performance
|
|
//note: if you do logging before call os.Exit(), you had better to call Safe() before call os.Exit().
|
|
//you call call func Safe() or Unsafe() in any where any time to switch safe mode.
|
|
|
|
func (l *Logger) Unsafe() MiniLogger {
|
|
l.modeSafe = false
|
|
return l
|
|
}
|
|
|
|
//Safe : if you call Safe(), each message can be processed immediately,
|
|
//but this mode may be has lower performance,beacuse of logger must wait for
|
|
//all writers process done with each messsage.
|
|
//note: if you do logging before call os.Exit(), you had better to call Safe() before call os.Exit().
|
|
//you call func Safe() or Unsafe() in any where any time to switch safe mode.
|
|
func (l *Logger) Safe() MiniLogger {
|
|
l.modeSafe = true
|
|
return l
|
|
}
|
|
func (l *Logger) AddWriter(writer Writer, levels byte) MiniLogger {
|
|
w := &WrappedWriter{
|
|
lock: &sync.Mutex{},
|
|
writer: writer,
|
|
chn: make(chan Entry, 1024),
|
|
}
|
|
m := *l.writersMap
|
|
if DebugLevel&levels == DebugLevel {
|
|
m[int(DebugLevel)] = append(m[int(DebugLevel)], w)
|
|
}
|
|
if InfoLevel&levels == InfoLevel {
|
|
m[int(InfoLevel)] = append(m[int(InfoLevel)], w)
|
|
}
|
|
if WarnLevel&levels == WarnLevel {
|
|
m[int(WarnLevel)] = append(m[int(WarnLevel)], w)
|
|
}
|
|
if ErrorLevel&levels == ErrorLevel {
|
|
m[int(ErrorLevel)] = append(m[int(ErrorLevel)], w)
|
|
}
|
|
if FatalLevel&levels == FatalLevel {
|
|
m[int(FatalLevel)] = append(m[int(FatalLevel)], w)
|
|
}
|
|
flush.Add(1)
|
|
go func() {
|
|
defer func() {
|
|
_ = recover()
|
|
flush.Done()
|
|
}()
|
|
if err := w.writer.Init(); err != nil {
|
|
fmt.Printf("init writer fail,%s", err)
|
|
return
|
|
}
|
|
for {
|
|
select {
|
|
case entry, ok := <-w.chn:
|
|
if ok {
|
|
w.writer.Write(entry)
|
|
if l.modeSafe {
|
|
l.wait.Done()
|
|
}
|
|
if entry.Level == FatalLevel {
|
|
os.Exit(0)
|
|
}
|
|
} else {
|
|
return
|
|
}
|
|
case exit := <-exitchn:
|
|
if exit {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
return l
|
|
}
|
|
func (l *Logger) callWriter(level byte, t, foramt string, v ...interface{}) {
|
|
if level == FatalLevel {
|
|
l.Safe()
|
|
}
|
|
c := ""
|
|
if t == "f" && len(v) == 0 {
|
|
v = append(v, foramt)
|
|
foramt = "%s"
|
|
}
|
|
switch t {
|
|
case "ln":
|
|
c = fmt.Sprintln(v...)
|
|
case "f":
|
|
c = fmt.Sprintf(foramt, v...)
|
|
default:
|
|
c = fmt.Sprint(v...)
|
|
}
|
|
m := *l.writersMap
|
|
for _, w := range m[int(level)] {
|
|
now := time.Now().UnixNano()
|
|
nowUnix := time.Now().Unix()
|
|
mili := (now / 1000000) - nowUnix*1000
|
|
w.chn <- Entry{
|
|
Timestamp: nowUnix,
|
|
TimestampMilliseconds: now / 10000000,
|
|
Milliseconds: mili,
|
|
Content: c,
|
|
Level: level,
|
|
LevelString: getLevelString(level),
|
|
Fields: l.fields,
|
|
FieldsString: l.fieldsString,
|
|
}
|
|
if l.modeSafe {
|
|
l.wait.Add(1)
|
|
}
|
|
}
|
|
if l.modeSafe {
|
|
l.wait.Wait()
|
|
}
|
|
}
|
|
|
|
func (l *Logger) Fatal(v ...interface{}) {
|
|
l.callWriter(FatalLevel, "", "", v...)
|
|
}
|
|
|
|
func (l *Logger) Error(v ...interface{}) MiniLogger {
|
|
l.callWriter(ErrorLevel, "", "", v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Warn(v ...interface{}) MiniLogger {
|
|
l.callWriter(WarnLevel, "", "", v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Info(v ...interface{}) MiniLogger {
|
|
l.callWriter(InfoLevel, "", "", v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Debug(v ...interface{}) MiniLogger {
|
|
l.callWriter(DebugLevel, "", "", v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Fatalf(format string, v ...interface{}) {
|
|
l.callWriter(FatalLevel, "f", format, v...)
|
|
}
|
|
|
|
func (l *Logger) Errorf(format string, v ...interface{}) MiniLogger {
|
|
l.callWriter(ErrorLevel, "f", format, v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Warnf(format string, v ...interface{}) MiniLogger {
|
|
l.callWriter(WarnLevel, "f", format, v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Infof(format string, v ...interface{}) MiniLogger {
|
|
l.callWriter(InfoLevel, "f", format, v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Debugf(format string, v ...interface{}) MiniLogger {
|
|
l.callWriter(DebugLevel, "f", format, v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Fatalln(v ...interface{}) {
|
|
l.callWriter(FatalLevel, "ln", "", v...)
|
|
}
|
|
|
|
func (l *Logger) Errorln(v ...interface{}) MiniLogger {
|
|
l.callWriter(ErrorLevel, "ln", "", v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Warnln(v ...interface{}) MiniLogger {
|
|
l.callWriter(WarnLevel, "ln", "", v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Infoln(v ...interface{}) MiniLogger {
|
|
l.callWriter(InfoLevel, "ln", "", v...)
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) Debugln(v ...interface{}) MiniLogger {
|
|
l.callWriter(DebugLevel, "ln", "", v...)
|
|
return l
|
|
}
|