commit 5ed139e0a5f2b98dc61f3015563bd78830cc4bf3 Author: ehlxr Date: Tue Nov 5 15:57:09 2019 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bcf81ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +go.sum \ No newline at end of file diff --git a/bufferpool/bufferpool.go b/bufferpool/bufferpool.go new file mode 100644 index 0000000..35eabbc --- /dev/null +++ b/bufferpool/bufferpool.go @@ -0,0 +1,9 @@ +package bufferpool + +import "go.uber.org/zap/buffer" + +var ( + _pool = buffer.NewPool() + // Get retrieves a buffer from the pool, creating one if necessary. + Get = _pool.Get +) diff --git a/crash/crash_darwin.go b/crash/crash_darwin.go new file mode 100644 index 0000000..ae7ce3d --- /dev/null +++ b/crash/crash_darwin.go @@ -0,0 +1,21 @@ +// +build darwin + +package crash + +import ( + "log" + "os" + "syscall" + + "github.com/pkg/errors" +) + +// CrashLog set crash log +func CrashLog(file string) { + f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + log.Fatalf("open crash log file error. %v", errors.WithStack(err)) + } else { + syscall.Dup2(int(f.Fd()), 2) + } +} diff --git a/crash/crash_unix.go b/crash/crash_unix.go new file mode 100644 index 0000000..34ba929 --- /dev/null +++ b/crash/crash_unix.go @@ -0,0 +1,21 @@ +// +build freebsd openbsd netbsd dragonfly linux + +package crash + +import ( + "log" + "os" + "syscall" + + "github.com/pkg/errors" +) + +// CrashLog set crash log +func CrashLog(file string) { + f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + log.Fatalf("open crash log file error. %v", errors.WithStack(err)) + } else { + syscall.Dup3(int(f.Fd()), 2, 0) + } +} diff --git a/crash/crash_win.go b/crash/crash_win.go new file mode 100644 index 0000000..6c23602 --- /dev/null +++ b/crash/crash_win.go @@ -0,0 +1,40 @@ +// +build windows + +package crash + +import ( + "log" + "os" + "syscall" + + "github.com/pkg/errors" +) + +var ( + kernel32 = syscall.MustLoadDLL("kernel32.dll") + procSetStdHandle = kernel32.MustFindProc("SetStdHandle") +) + +func setStdHandle(stdhandle int32, handle syscall.Handle) error { + r0, _, e1 := syscall.Syscall(procSetStdHandle.Addr(), 2, uintptr(stdhandle), uintptr(handle), 0) + if r0 == 0 { + if e1 != 0 { + return error(e1) + } + return syscall.EINVAL + } + return nil +} + +// CrashLog set crash log +func CrashLog(file string) { + f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + log.Println(err.Error()) + } else { + err = setStdHandle(syscall.STD_ERROR_HANDLE, syscall.Handle(f.Fd())) + if err != nil { + log.Fatalf("open crash log file error. %v", errors.WithStack(err)) + } + } +} diff --git a/encoder/text_encoder.go b/encoder/text_encoder.go new file mode 100644 index 0000000..e3afdf7 --- /dev/null +++ b/encoder/text_encoder.go @@ -0,0 +1,450 @@ +package encoder + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "math" + "sync" + "time" + "unicode/utf8" + + "github.com/ehlxr/logger/bufferpool" + + "go.uber.org/zap/buffer" + "go.uber.org/zap/zapcore" +) + +const _hex = "0123456789abcdef" + +var ( + _arrayEncoderPool = sync.Pool{ + New: func() interface{} { + return &arrayEncoder{elems: make([]interface{}, 0, 2)} + }} + + _textPool = sync.Pool{New: func() interface{} { + return &textEncoder{} + }} +) + +func getTextEncoder() *textEncoder { + return _textPool.Get().(*textEncoder) +} +func getSliceEncoder() *arrayEncoder { + return _arrayEncoderPool.Get().(*arrayEncoder) +} + +func putSliceEncoder(e *arrayEncoder) { + e.elems = e.elems[:0] + _arrayEncoderPool.Put(e) +} + +type textEncoder struct { + *zapcore.EncoderConfig + buf *buffer.Buffer + spaced bool // include spaces after colons and commas + openNamespaces int + + // for encoding generic values by reflection + reflectBuf *buffer.Buffer + reflectEnc *json.Encoder +} + +func NewTextEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder { + return &textEncoder{ + EncoderConfig: &cfg, + buf: bufferpool.Get(), + spaced: false, + } +} + +func (enc textEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) { + line := bufferpool.Get() + + // If this ever becomes a performance bottleneck, we can implement + // ArrayEncoder for our plain-text format. + arr := getSliceEncoder() + if enc.TimeKey != "" && enc.EncodeTime != nil { + enc.EncodeTime(ent.Time, arr) + } + if enc.LevelKey != "" && enc.EncodeLevel != nil { + enc.EncodeLevel(ent.Level, arr) + } + if ent.LoggerName != "" && enc.NameKey != "" { + nameEncoder := enc.EncodeName + + if nameEncoder == nil { + // Fall back to FullNameEncoder for backward compatibility. + nameEncoder = zapcore.FullNameEncoder + } + + nameEncoder(ent.LoggerName, arr) + } + if ent.Caller.Defined && enc.CallerKey != "" && enc.EncodeCaller != nil { + enc.EncodeCaller(ent.Caller, arr) + } + for i := range arr.elems { + // if i > 0 { + // line.AppendByte('\t') + // } + fmt.Fprint(line, arr.elems[i]) + } + putSliceEncoder(arr) + + // Add the message itself. + if enc.MessageKey != "" { + // c.addTabIfNecessary(line) + line.AppendString(" - ") + line.AppendString(ent.Message) + } + + // Add any structured context. + enc.writeContext(line, fields) + + // If there's no stacktrace key, honor that; this allows users to force + // single-line output. + if ent.Stack != "" && enc.StacktraceKey != "" { + line.AppendByte('\n') + line.AppendString(ent.Stack) + } + + if enc.LineEnding != "" { + line.AppendString(enc.LineEnding) + } else { + line.AppendString(zapcore.DefaultLineEnding) + } + return line, nil +} + +func (enc textEncoder) writeContext(line *buffer.Buffer, extra []zapcore.Field) { + context := enc.Clone().(*textEncoder) + defer context.buf.Free() + + addFields(context, extra) + // context.closeOpenNamespaces() + if context.buf.Len() == 0 { + return + } + + // c.addTabIfNecessary(line) + line.AppendByte('{') + line.Write(context.buf.Bytes()) + line.AppendByte('}') +} + +func (enc textEncoder) addTabIfNecessary(line *buffer.Buffer) { + if line.Len() > 0 { + line.AppendByte('\t') + } +} + +func (enc *textEncoder) resetReflectBuf() { + if enc.reflectBuf == nil { + enc.reflectBuf = bufferpool.Get() + enc.reflectEnc = json.NewEncoder(enc.reflectBuf) + + // For consistency with our custom JSON encoder. + enc.reflectEnc.SetEscapeHTML(false) + } else { + enc.reflectBuf.Reset() + } +} + +func (enc *textEncoder) AddArray(key string, arr zapcore.ArrayMarshaler) error { + enc.addKey(key) + + enc.addElementSeparator() + enc.buf.AppendByte('[') + // TODO: + // err := arr.MarshalLogArray(enc) + enc.buf.AppendByte(']') + // return err + return nil +} +func (enc *textEncoder) AddObject(key string, obj zapcore.ObjectMarshaler) error { + enc.addKey(key) + enc.addElementSeparator() + enc.buf.AppendByte('{') + err := obj.MarshalLogObject(enc) + enc.buf.AppendByte('}') + return err +} +func (enc *textEncoder) AddBinary(key string, val []byte) { + enc.AddString(key, base64.StdEncoding.EncodeToString(val)) +} +func (enc *textEncoder) AddByteString(key string, val []byte) { + enc.addKey(key) + enc.addElementSeparator() + enc.buf.AppendByte('"') + enc.safeAddByteString(val) + enc.buf.AppendByte('"') +} +func (enc *textEncoder) AddBool(key string, val bool) { + enc.addKey(key) + enc.addElementSeparator() + enc.buf.AppendBool(val) +} +func (enc *textEncoder) AddComplex128(key string, val complex128) { + enc.addKey(key) + enc.addElementSeparator() + // Cast to a platform-independent, fixed-size type. + r, i := float64(real(val)), float64(imag(val)) + enc.buf.AppendByte('"') + // Because we're always in a quoted string, we can use strconv without + // special-casing NaN and +/-Inf. + enc.buf.AppendFloat(r, 64) + enc.buf.AppendByte('+') + enc.buf.AppendFloat(i, 64) + enc.buf.AppendByte('i') + enc.buf.AppendByte('"') +} +func (enc *textEncoder) AddDuration(key string, val time.Duration) { + enc.addKey(key) + cur := enc.buf.Len() + // TODO: + // enc.EncodeDuration(val, enc) + if cur == enc.buf.Len() { + // User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep + // JSON valid. + enc.addElementSeparator() + enc.buf.AppendInt(int64(val)) + } +} +func (enc *textEncoder) AddFloat64(key string, val float64) { + enc.addKey(key) + enc.appendFloat(val, 64) +} +func (enc *textEncoder) AddInt64(key string, val int64) { + enc.addKey(key) + enc.addElementSeparator() + enc.buf.AppendInt(val) +} +func (enc *textEncoder) AddReflected(key string, obj interface{}) error { + enc.resetReflectBuf() + err := enc.reflectEnc.Encode(obj) + if err != nil { + return err + } + enc.reflectBuf.TrimNewline() + enc.addKey(key) + _, err = enc.buf.Write(enc.reflectBuf.Bytes()) + return err +} +func (enc *textEncoder) OpenNamespace(key string) { + enc.addKey(key) + enc.buf.AppendByte('{') + enc.openNamespaces++ +} +func (enc *textEncoder) AddString(key, val string) { + enc.addKey(key) + enc.addElementSeparator() + enc.buf.AppendByte('"') + enc.safeAddString(val) + enc.buf.AppendByte('"') +} +func (enc *textEncoder) AddTime(key string, val time.Time) { + enc.addKey(key) + cur := enc.buf.Len() + // TODO: + // enc.EncodeTime(val, enc) + if cur == enc.buf.Len() { + // User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep + // output JSON valid. + enc.addElementSeparator() + enc.buf.AppendInt(val.UnixNano()) + } +} +func (enc *textEncoder) AddUint64(key string, val uint64) { + enc.addKey(key) + enc.addElementSeparator() + enc.buf.AppendUint(val) +} +func (enc *textEncoder) AddComplex64(k string, v complex64) { enc.AddComplex128(k, complex128(v)) } +func (enc *textEncoder) AddFloat32(k string, v float32) { enc.AddFloat64(k, float64(v)) } +func (enc *textEncoder) AddInt(k string, v int) { enc.AddInt64(k, int64(v)) } +func (enc *textEncoder) AddInt32(k string, v int32) { enc.AddInt64(k, int64(v)) } +func (enc *textEncoder) AddInt16(k string, v int16) { enc.AddInt64(k, int64(v)) } +func (enc *textEncoder) AddInt8(k string, v int8) { enc.AddInt64(k, int64(v)) } +func (enc *textEncoder) AddUint(k string, v uint) { enc.AddUint64(k, uint64(v)) } +func (enc *textEncoder) AddUint32(k string, v uint32) { enc.AddUint64(k, uint64(v)) } +func (enc *textEncoder) AddUint16(k string, v uint16) { enc.AddUint64(k, uint64(v)) } +func (enc *textEncoder) AddUint8(k string, v uint8) { enc.AddUint64(k, uint64(v)) } +func (enc *textEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) } + +func (enc *textEncoder) Clone() zapcore.Encoder { + clone := enc.clone() + clone.buf.Write(enc.buf.Bytes()) + return clone +} + +func (enc *textEncoder) clone() *textEncoder { + clone := getTextEncoder() + clone.EncoderConfig = enc.EncoderConfig + clone.spaced = enc.spaced + clone.openNamespaces = enc.openNamespaces + clone.buf = bufferpool.Get() + return clone +} + +func (enc *textEncoder) addKey(key string) { + enc.addElementSeparator() + enc.buf.AppendByte('"') + enc.safeAddString(key) + enc.buf.AppendByte('"') + enc.buf.AppendByte(':') + if enc.spaced { + enc.buf.AppendByte(' ') + } +} + +func (enc *textEncoder) addElementSeparator() { + last := enc.buf.Len() - 1 + if last < 0 { + return + } + switch enc.buf.Bytes()[last] { + case '{', '[', ':', ',', ' ': + return + default: + enc.buf.AppendByte(',') + if enc.spaced { + enc.buf.AppendByte(' ') + } + } +} + +func (enc *textEncoder) appendFloat(val float64, bitSize int) { + enc.addElementSeparator() + switch { + case math.IsNaN(val): + enc.buf.AppendString(`"NaN"`) + case math.IsInf(val, 1): + enc.buf.AppendString(`"+Inf"`) + case math.IsInf(val, -1): + enc.buf.AppendString(`"-Inf"`) + default: + enc.buf.AppendFloat(val, bitSize) + } +} + +func (enc *textEncoder) safeAddString(s string) { + for i := 0; i < len(s); { + if enc.tryAddRuneSelf(s[i]) { + i++ + continue + } + r, size := utf8.DecodeRuneInString(s[i:]) + if enc.tryAddRuneError(r, size) { + i++ + continue + } + enc.buf.AppendString(s[i : i+size]) + i += size + } +} + +func (enc *textEncoder) safeAddByteString(s []byte) { + for i := 0; i < len(s); { + if enc.tryAddRuneSelf(s[i]) { + i++ + continue + } + r, size := utf8.DecodeRune(s[i:]) + if enc.tryAddRuneError(r, size) { + i++ + continue + } + enc.buf.Write(s[i : i+size]) + i += size + } +} + +func (enc *textEncoder) tryAddRuneSelf(b byte) bool { + if b >= utf8.RuneSelf { + return false + } + if 0x20 <= b && b != '\\' && b != '"' { + enc.buf.AppendByte(b) + return true + } + switch b { + case '\\', '"': + enc.buf.AppendByte('\\') + enc.buf.AppendByte(b) + case '\n': + enc.buf.AppendByte('\\') + enc.buf.AppendByte('n') + case '\r': + enc.buf.AppendByte('\\') + enc.buf.AppendByte('r') + case '\t': + enc.buf.AppendByte('\\') + enc.buf.AppendByte('t') + default: + // Encode bytes < 0x20, except for the escape sequences above. + enc.buf.AppendString(`\u00`) + enc.buf.AppendByte(_hex[b>>4]) + enc.buf.AppendByte(_hex[b&0xF]) + } + return true +} + +func (enc *textEncoder) tryAddRuneError(r rune, size int) bool { + if r == utf8.RuneError && size == 1 { + enc.buf.AppendString(`\ufffd`) + return true + } + return false +} + +func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) { + for i := range fields { + fields[i].AddTo(enc) + } +} + +type arrayEncoder struct { + elems []interface{} +} + +func (s *arrayEncoder) AppendArray(v zapcore.ArrayMarshaler) error { + enc := &arrayEncoder{} + err := v.MarshalLogArray(enc) + s.elems = append(s.elems, enc.elems) + + return err +} + +func (s *arrayEncoder) AppendObject(v zapcore.ObjectMarshaler) error { + m := zapcore.NewMapObjectEncoder() + err := v.MarshalLogObject(m) + s.elems = append(s.elems, m.Fields) + return err +} + +func (s *arrayEncoder) AppendReflected(v interface{}) error { + s.elems = append(s.elems, v) + return nil +} + +func (s *arrayEncoder) AppendBool(v bool) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendByteString(v []byte) { s.elems = append(s.elems, string(v)) } +func (s *arrayEncoder) AppendComplex128(v complex128) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendComplex64(v complex64) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendDuration(v time.Duration) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendFloat64(v float64) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendFloat32(v float32) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendInt(v int) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendInt64(v int64) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendInt32(v int32) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendInt16(v int16) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendInt8(v int8) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendString(v string) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendTime(v time.Time) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendUint(v uint) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendUint64(v uint64) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendUint32(v uint32) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendUint16(v uint16) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendUint8(v uint8) { s.elems = append(s.elems, v) } +func (s *arrayEncoder) AppendUintptr(v uintptr) { s.elems = append(s.elems, v) } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7b4fc87 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/ehlxr/logger + +go 1.13 + +require ( + github.com/pkg/errors v0.8.1 + go.uber.org/zap v1.12.0 + gopkg.in/natefinch/lumberjack.v2 v2.0.0 +) diff --git a/level_color.go b/level_color.go new file mode 100644 index 0000000..1cdd936 --- /dev/null +++ b/level_color.go @@ -0,0 +1,55 @@ +package log + +import ( + "fmt" + + "go.uber.org/zap/zapcore" +) + +var ( + _levelToColor = map[zapcore.Level]Color{ + zapcore.DebugLevel: Magenta, + zapcore.InfoLevel: Blue, + zapcore.WarnLevel: Yellow, + zapcore.ErrorLevel: Red, + zapcore.DPanicLevel: Red, + zapcore.PanicLevel: Red, + zapcore.FatalLevel: Red, + } + _unknownLevelColor = Red + + _levelToColorStrings = make(map[zapcore.Level]string, len(_levelToColor)) +) + +type Color uint8 + +//noinspection GoUnusedConst +const ( + Black Color = iota + 30 + Red + Green + Yellow + Blue + Magenta + Cyan + White +) + +func (lc *logConfig) initColor() { + for level, color := range _levelToColor { + lcs := level.String() + + if lc.EnableCapitalLevel { + lcs = level.CapitalString() + } + + if lc.EnableLevelTruncation { + lcs = lcs[:4] + } + _levelToColorStrings[level] = color.Add(lcs) + } +} + +func (c Color) Add(s string) string { + return fmt.Sprintf("\x1b[%dm%s\x1b[0m", uint8(c), s) +} diff --git a/log.go b/log.go new file mode 100644 index 0000000..f5b6f18 --- /dev/null +++ b/log.go @@ -0,0 +1,248 @@ +package log + +import ( + "fmt" + "log" + "os" + "path" + "strings" + "time" + + "github.com/ehlxr/logger/encoder" + + "github.com/ehlxr/logger/bufferpool" + "github.com/ehlxr/logger/crash" + "github.com/pkg/errors" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "gopkg.in/natefinch/lumberjack.v2" +) + +var logger *zap.SugaredLogger + +const ( + DebugLevel = zapcore.DebugLevel + InfoLevel = zapcore.InfoLevel + WarnLevel = zapcore.WarnLevel + ErrorLevel = zapcore.ErrorLevel + DPanicLevel = zapcore.DPanicLevel + PanicLevel = zapcore.PanicLevel + FatalLevel = zapcore.FatalLevel +) + +type logConfig struct { + Level zapcore.Level + FilePath string + EnableColors bool + CrashLogPath string + ErrorLogPath string + EnableLineNumber bool + + // enable the truncation of the level text to 4 characters. + EnableLevelTruncation bool + EnableErrorStacktrace bool + TimestampFormat string + EnableCapitalLevel bool + atomicLevel zap.AtomicLevel + Name string +} + +func init() { + lc := &logConfig{ + Level: InfoLevel, + FilePath: "", + EnableColors: false, + CrashLogPath: "", + ErrorLogPath: "", + EnableLineNumber: false, + EnableLevelTruncation: false, + EnableErrorStacktrace: false, + TimestampFormat: "", + EnableCapitalLevel: false, + Name: "", + } + + logger = lc.newLogger().Sugar() +} + +func (lc *logConfig) Init() { + logger = lc.newLogger().Sugar() +} + +func NewLogConfig() *logConfig { + return &logConfig{ + Level: DebugLevel, + FilePath: "./log/", + EnableColors: true, + CrashLogPath: "./log/crash.log", + ErrorLogPath: "./log/error_", + EnableLineNumber: true, + EnableLevelTruncation: true, + EnableErrorStacktrace: true, + TimestampFormat: "2006-01-02 15:04:05.000", + EnableCapitalLevel: true, + } +} + +func (lc *logConfig) newLogger() *zap.Logger { + if lc.CrashLogPath != "" { + writeCrashLog(lc.CrashLogPath) + } + + lc.atomicLevel = zap.NewAtomicLevelAt(lc.Level) + lc.initColor() + + cores := []zapcore.Core{ + zapcore.NewCore( + encoder.NewTextEncoder(lc.encoderConfig()), + zapcore.Lock(os.Stdout), + lc.atomicLevel, + )} + + if lc.FilePath != "" { + cores = append(cores, lc.fileCore()) + } + + if lc.ErrorLogPath != "" { + cores = append(cores, lc.errorFileCore()) + } + + core := zapcore.NewTee(cores...) + + var options []zap.Option + + if lc.EnableLineNumber { + options = append(options, zap.AddCaller(), zap.AddCallerSkip(1)) + } + + if lc.EnableErrorStacktrace { + options = append(options, zap.AddStacktrace(zapcore.ErrorLevel)) + } + + zapLog := zap.New(core, options...) + + if lc.Name != "" { + zapLog = zapLog.Named(fmt.Sprintf("[%s]", lc.Name)) + } + + return zapLog +} + +func (lc *logConfig) encoderConfig() zapcore.EncoderConfig { + el := lc.encodeLevel + if lc.EnableColors { + el = lc.encodeColorLevel + } + + return zapcore.EncoderConfig{ + TimeKey: "T", + LevelKey: "L", + NameKey: "N", + CallerKey: "C", + MessageKey: "M", + StacktraceKey: "S", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: el, + EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + tf := time.RFC3339 + if lc.TimestampFormat != "" { + tf = lc.TimestampFormat + } + enc.AppendString(t.Format(fmt.Sprintf("[%s]", tf))) + }, + EncodeDuration: zapcore.StringDurationEncoder, + EncodeCaller: func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(trimCallerFilePath(caller)) + }, + } +} + +func (lc *logConfig) fileCore() zapcore.Core { + return zapcore.NewCore( + encoder.NewTextEncoder(lc.encoderConfig()), + // zapcore.NewMultiWriteSyncer( + // zapcore.Lock(os.Stdout), + // lc.fileWriteSyncer(), + // ), + fileWriteSyncer(lc.FilePath), + lc.atomicLevel, + ) +} + +func (lc *logConfig) errorFileCore() zapcore.Core { + return zapcore.NewCore( + encoder.NewTextEncoder(lc.encoderConfig()), + + fileWriteSyncer(lc.ErrorLogPath), + zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl >= zapcore.ErrorLevel + }), + ) +} + +func (lc *logConfig) encodeLevel(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { + levelString := l.CapitalString() + + if lc.EnableLevelTruncation { + levelString = levelString[:4] + } + + enc.AppendString(fmt.Sprintf("[%s]", levelString)) +} + +func (lc *logConfig) encodeColorLevel(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { + s, ok := _levelToColorStrings[l] + if !ok { + s = _unknownLevelColor.Add(l.CapitalString()) + } + + enc.AppendString(fmt.Sprintf("[%s]", s)) +} + +func trimCallerFilePath(ec zapcore.EntryCaller) string { + if !ec.Defined { + return "undefined" + } + // Find the last separator. + idx := strings.LastIndexByte(ec.File, '/') + if idx == -1 { + return ec.FullPath() + } + + buf := bufferpool.Get() + buf.AppendString(ec.File[idx+1:]) + buf.AppendByte(':') + buf.AppendInt(int64(ec.Line)) + caller := buf.String() + buf.Free() + + return fmt.Sprintf(" %s", caller) +} + +func fileWriteSyncer(name string) zapcore.WriteSyncer { + fileName := fmt.Sprintf("%s%s.log", + name, + time.Now().Format("2006-01-02")) + + return zapcore.AddSync(&lumberjack.Logger{ + Filename: fileName, + MaxSize: 500, // 单个日志文件大小(MB) + MaxBackups: 10, + MaxAge: 30, // 保留多少天的日志 + LocalTime: true, + Compress: true, + }) +} + +func writeCrashLog(file string) { + err := os.MkdirAll(path.Dir(file), os.ModePerm) + if err != nil { + log.Fatalf("make crash log dir error. %v", errors.WithStack(err)) + } + + crash.CrashLog(file) +} + +func Fields(args ...interface{}) { + logger = logger.With(args...) +} diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..c49c80c --- /dev/null +++ b/logger.go @@ -0,0 +1,57 @@ +package log + +func Debug(args ...interface{}) { + logger.Debug(args...) +} + +func Debugf(template string, args ...interface{}) { + logger.Debugf(template, args...) +} + +func Info(args ...interface{}) { + logger.Info(args...) +} + +func Infof(template string, args ...interface{}) { + logger.Infof(template, args...) +} + +func Warn(args ...interface{}) { + logger.Warn(args...) +} + +func Warnf(template string, args ...interface{}) { + logger.Warnf(template, args...) +} + +func Error(args ...interface{}) { + logger.Error(args...) +} + +func Errorf(template string, args ...interface{}) { + logger.Errorf(template, args...) +} + +func DPanic(args ...interface{}) { + logger.DPanic(args...) +} + +func DPanicf(template string, args ...interface{}) { + logger.DPanicf(template, args...) +} + +func Panic(args ...interface{}) { + logger.Panic(args...) +} + +func Panicf(template string, args ...interface{}) { + logger.Panicf(template, args...) +} + +func Fatal(args ...interface{}) { + logger.Fatal(args...) +} + +func Fatalf(template string, args ...interface{}) { + logger.Fatalf(template, args...) +}