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...) }