diff --git a/log/buffer.go b/log/buffer.go new file mode 100644 index 0000000..95eb2bb --- /dev/null +++ b/log/buffer.go @@ -0,0 +1,91 @@ +package log // import "go.uber.org/zap/buffer" + +import "strconv" + +const _size = 9216 + +type Buffer struct { + bs []byte +} + +func (buff *Buffer) Init(){ + buff.bs = make([]byte,_size) +} + +// AppendByte writes a single byte to the Buffer. +func (b *Buffer) AppendByte(v byte) { + b.bs = append(b.bs, v) +} + +func (b *Buffer) AppendBytes(v []byte) { + b.bs = append(b.bs, v...) +} + +// AppendString writes a string to the Buffer. +func (b *Buffer) AppendString(s string) { + b.bs = append(b.bs, s...) +} + +// AppendInt appends an integer to the underlying buffer (assuming base 10). +func (b *Buffer) AppendInt(i int64) { + b.bs = strconv.AppendInt(b.bs, i, 10) +} + +// AppendUint appends an unsigned integer to the underlying buffer (assuming +// base 10). +func (b *Buffer) AppendUint(i uint64) { + b.bs = strconv.AppendUint(b.bs, i, 10) +} + +// AppendBool appends a bool to the underlying buffer. +func (b *Buffer) AppendBool(v bool) { + b.bs = strconv.AppendBool(b.bs, v) +} + +// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN +// or +/- Inf. +func (b *Buffer) AppendFloat(f float64, bitSize int) { + b.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize) +} + +// Len returns the length of the underlying byte slice. +func (b *Buffer) Len() int { + return len(b.bs) +} + +// Cap returns the capacity of the underlying byte slice. +func (b *Buffer) Cap() int { + return cap(b.bs) +} + +// Bytes returns a mutable reference to the underlying byte slice. +func (b *Buffer) Bytes() []byte { + return b.bs +} + +// String returns a string copy of the underlying byte slice. +func (b *Buffer) String() string { + return string(b.bs) +} + +// Reset resets the underlying byte slice. Subsequent writes re-use the slice's +// backing array. +func (b *Buffer) Reset() { + b.bs = b.bs[:0] +} + +// Write implements io.Writer. +func (b *Buffer) Write(bs []byte) (int, error) { + b.bs = append(b.bs, bs...) + return len(bs), nil +} + +// TrimNewline trims any final "\n" byte from the end of the buffer. +func (b *Buffer) TrimNewline() { + if i := len(b.bs) - 1; i >= 0 { + if b.bs[i] == '\n' { + b.bs = b.bs[:i] + } + } +} + diff --git a/log/log.go b/log/log.go index 03d9db5..c969854 100644 --- a/log/log.go +++ b/log/log.go @@ -9,9 +9,15 @@ import ( "runtime/debug" "strings" "time" + "sync" + "io" + "runtime" + syslog "log" + jsoniter "github.com/json-iterator/go" ) -var OpenConsole bool = true +var json = jsoniter.ConfigCompatibleWithStandardLibrary +var OpenConsole bool // levels const ( @@ -35,15 +41,50 @@ const ( type Logger struct { filePath string filepre string - logTime time.Time + + //logTime time.Time + fileDay int level int - stdLogger *log.Logger - baseLogger *log.Logger - baseFile *os.File flag int + buf Buffer + + outFile io.Writer // destination for output + outConsole io.Writer //os.Stdout + + mu sync.Mutex // ensures atomic writes; protects the following fields } -func New(strLevel string, pathname string, filepre string, flag int) (*Logger, error) { +func (logger *Logger) GenDayFile(now *time.Time) error { + if logger.fileDay == now.Day() { + return nil + } + + filename := fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d.log", + now.Year(), + now.Month(), + now.Day(), + now.Hour(), + now.Minute(), + now.Second()) + + if logger.filePath != "" { + var err error + logger.outFile,err = os.Create(path.Join(logger.filePath, logger.filepre+filename)) + if err != nil { + return err + } + logger.fileDay = now.Day() + if OpenConsole == true { + logger.outConsole = os.Stdout + } + }else{ + logger.outConsole = os.Stdout + } + + return nil +} + +func New(strLevel string, pathName string, filePre string, flag int) (*Logger, error) { // level var level int switch strings.ToLower(strLevel) { @@ -63,88 +104,213 @@ func New(strLevel string, pathname string, filepre string, flag int) (*Logger, e return nil, errors.New("unknown level: " + strLevel) } - // logger - var baseLogger *log.Logger - var baseFile *os.File - now := time.Now() - if pathname != "" { - filename := fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d.log", - now.Year(), - now.Month(), - now.Day(), - now.Hour(), - now.Minute(), - now.Second()) - - file, err := os.Create(path.Join(pathname, filepre+filename)) - if err != nil { - return nil, err - } - - baseLogger = log.New(file, "", flag) - baseFile = file - } else { - baseLogger = log.New(os.Stdout, "", flag) - OpenConsole = false - } // new logger := new(Logger) logger.level = level - logger.stdLogger = log.New(os.Stdout, "", flag) - logger.baseLogger = baseLogger - logger.baseFile = baseFile - logger.logTime = now - logger.filePath = pathname + logger.filePath = pathName + logger.filepre = filePre logger.flag = flag - logger.filepre = filepre + logger.buf.Init() + now := time.Now() + err := logger.GenDayFile(&now) + if err != nil { + return nil,err + } return logger, nil } // It's dangerous to call the method on logging func (logger *Logger) Close() { - if logger.baseFile != nil { - logger.baseFile.Close() + if logger.outFile != nil { + logger.outFile.(io.Closer).Close() + logger.outFile = nil } - - logger.baseLogger = nil - logger.baseFile = nil } func (logger *Logger) doPrintf(level int, printLevel string, format string, a ...interface{}) { if level < logger.level { return } - if logger.baseLogger == nil { - panic("logger closed") + now := time.Now() + + logger.mu.Lock() + logger.GenDayFile(&now) + + logger.buf.Reset() + logger.formatHeader(3,&now) + logger.buf.AppendString(printLevel) + logger.buf.AppendString(fmt.Sprintf(format, a...)) + logger.buf.AppendByte('\n') + if logger.outFile!= nil { + logger.outFile.Write(logger.buf.Bytes()) } + if logger.outConsole!= nil { + logger.outConsole.Write(logger.buf.Bytes()) + } + logger.mu.Unlock() + if level == fatalLevel { + os.Exit(1) + } +} - if logger.baseFile != nil { - now := time.Now() - if now.Day() != logger.logTime.Day() { - filename := fmt.Sprintf("%d%02d%02d_%02d_%02d_%02d.log", - now.Year(), - now.Month(), - now.Day(), - now.Hour(), - now.Minute(), - now.Second()) - file, err := os.Create(path.Join(logger.filePath, logger.filepre+filename)) - if err == nil { - logger.baseFile.Close() - logger.baseLogger = log.New(file, "", logger.flag) - logger.baseFile = file - logger.logTime = now +func (logger *Logger) doSPrintf(level int, printLevel string, a []interface{}) { + if level < logger.level { + return + } + now := time.Now() + logger.mu.Lock() + logger.GenDayFile(&now) + logger.buf.Reset() + logger.formatHeader(3,&now) + logger.buf.AppendString(printLevel) + for _,s := range a { + switch s.(type) { + case int: + logger.buf.AppendInt(int64(s.(int))) + case int8: + logger.buf.AppendInt(int64(s.(int8))) + case int16: + logger.buf.AppendInt(int64(s.(int16))) + case int32: + logger.buf.AppendInt(int64(s.(int32))) + case int64: + logger.buf.AppendInt(s.(int64)) + case uint: + logger.buf.AppendUint(uint64(s.(uint))) + case uint8: + logger.buf.AppendUint(uint64(s.(uint8))) + case uint16: + logger.buf.AppendUint(uint64(s.(uint16))) + case uint32: + logger.buf.AppendUint(uint64(s.(uint32))) + case uint64: + logger.buf.AppendUint(s.(uint64)) + case float32: + logger.buf.AppendFloat(float64(s.(float32)),32) + case float64: + logger.buf.AppendFloat(s.(float64),64) + case bool: + logger.buf.AppendBool(s.(bool)) + case string: + logger.buf.AppendString(s.(string)) + case *int: + val := s.(*int) + if val != nil { + logger.buf.AppendInt(int64(*val)) + }else{ + logger.buf.AppendString("nil<*int>") } + case *int8: + val := s.(*int8) + if val != nil { + logger.buf.AppendInt(int64(*val)) + }else{ + logger.buf.AppendString("nil<*int8>") + } + case *int16: + val := s.(*int16) + if val != nil { + logger.buf.AppendInt(int64(*val)) + }else{ + logger.buf.AppendString("nil<*int16>") + } + case *int32: + val := s.(*int32) + if val != nil { + logger.buf.AppendInt(int64(*val)) + }else{ + logger.buf.AppendString("nil<*int32>") + } + case *int64: + val := s.(*int64) + if val != nil { + logger.buf.AppendInt(int64(*val)) + }else{ + logger.buf.AppendString("nil<*int64>") + } + case *uint: + val := s.(*uint) + if val != nil { + logger.buf.AppendUint(uint64(*val)) + }else{ + logger.buf.AppendString("nil<*uint>") + } + case *uint8: + val := s.(*uint8) + if val != nil { + logger.buf.AppendUint(uint64(*val)) + }else{ + logger.buf.AppendString("nil<*uint8>") + } + case *uint16: + val := s.(*uint16) + if val != nil { + logger.buf.AppendUint(uint64(*val)) + }else{ + logger.buf.AppendString("nil<*uint16>") + } + case *uint32: + val := s.(*uint32) + if val != nil { + logger.buf.AppendUint(uint64(*val)) + }else{ + logger.buf.AppendString("nil<*uint32>") + } + case *uint64: + val := s.(*uint64) + if val != nil { + logger.buf.AppendUint(uint64(*val)) + }else{ + logger.buf.AppendString("nil<*uint64>") + } + case *float32: + val := s.(*float32) + if val != nil { + logger.buf.AppendFloat(float64(*val),32) + }else{ + logger.buf.AppendString("nil<*float32>") + } + case *float64: + val := s.(*float32) + if val != nil { + logger.buf.AppendFloat(float64(*val),64) + }else{ + logger.buf.AppendString("nil<*float64>") + } + case *bool: + val := s.(*bool) + if val != nil { + logger.buf.AppendBool(*val) + }else{ + logger.buf.AppendString("nil<*bool>") + } + case *string: + val := s.(*string) + if val != nil { + logger.buf.AppendString(*val) + }else{ + logger.buf.AppendString("nil<*string>") + } + default: + b,err := json.Marshal(s) + if err != nil { + logger.buf.AppendString("") + }else{ + logger.buf.AppendBytes(b) + } + } } - - format = printLevel + format - logger.baseLogger.Output(3, fmt.Sprintf(format, a...)) - if OpenConsole == true { - logger.stdLogger.Output(3, fmt.Sprintf(format, a...)) + logger.buf.AppendByte('\n') + if logger.outFile!= nil { + logger.outFile.Write(logger.buf.Bytes()) } + if logger.outConsole!= nil { + logger.outConsole.Write(logger.buf.Bytes()) + } + logger.mu.Unlock() if level == fatalLevel { os.Exit(1) } @@ -211,3 +377,94 @@ func Fatal(format string, a ...interface{}) { func Close() { gLogger.Close() } + +func SDebug(a ...interface{}) { + gLogger.doSPrintf(debugLevel, printDebugLevel, a) +} + +func SRelease(a ...interface{}) { + gLogger.doSPrintf(releaseLevel, printReleaseLevel, a) +} + +func SWarning(a ...interface{}) { + gLogger.doSPrintf(warningLevel, printWarningLevel, a) +} + +func SError(a ...interface{}) { + gLogger.doSPrintf(errorLevel, printErrorLevel, a) +} + +func SStack(a ...interface{}) { + gLogger.doSPrintf(stackLevel, printStackLevel, a) + gLogger.doSPrintf(stackLevel, printStackLevel, []interface{}{string(debug.Stack())}) +} + +func SFatal(a ...interface{}) { + gLogger.doSPrintf(fatalLevel, printFatalLevel, a) +} + +const timeFlag = syslog.Ldate|syslog.Ltime|syslog.Lmicroseconds +func (l *Logger) formatHeader(calldepth int,t *time.Time) { + var file string + var line int + if l.flag&(syslog.Lshortfile|syslog.Llongfile) != 0 { + // Release lock while getting caller info - it's expensive. + var ok bool + _, file, line, ok = runtime.Caller(calldepth) + if !ok { + file = "???" + line = 0 + } + } + + if l.flag&syslog.Lmsgprefix != 0 { + l.buf.AppendString(l.filepre) + } + if l.flag&timeFlag != 0 { + if l.flag&syslog.Ldate != 0 { + year, month, day := t.Date() + l.buf.AppendInt(int64(year)) + l.buf.AppendByte('/') + l.buf.AppendInt(int64(month)) + l.buf.AppendByte('/') + l.buf.AppendInt(int64(day)) + l.buf.AppendByte(' ') + } + + if l.flag&(syslog.Ltime|syslog.Lmicroseconds) != 0 { + hour, min, sec := t.Clock() + l.buf.AppendInt(int64(hour)) + l.buf.AppendByte(':') + l.buf.AppendInt(int64(min)) + l.buf.AppendByte(':') + + l.buf.AppendInt(int64(sec)) + + if l.flag&syslog.Lmicroseconds != 0 { + l.buf.AppendByte('.') + l.buf.AppendInt(int64(t.Nanosecond()/1e3)) + } + l.buf.AppendByte(' ') + } + } + if l.flag&(syslog.Lshortfile|syslog.Llongfile) != 0 { + if l.flag&syslog.Lshortfile != 0 { + short := file + for i := len(file) - 1; i > 0; i-- { + if file[i] == '/' { + short = file[i+1:] + break + } + } + file = short + } + l.buf.AppendString(file) + l.buf.AppendByte(':') + l.buf.AppendInt(int64(line)) + l.buf.AppendString(": ") + } + + if l.flag&syslog.Lmsgprefix != 0 { + l.buf.AppendString(l.filepre) + } +}