| |
| |
| |
|
|
| package logger |
|
|
| import ( |
| "fmt" |
| "os" |
| "path/filepath" |
| "strconv" |
|
|
| "github.com/GoAdminGroup/go-admin/context" |
| "github.com/GoAdminGroup/go-admin/modules/trace" |
| "github.com/GoAdminGroup/go-admin/modules/utils" |
| "go.uber.org/zap" |
| "go.uber.org/zap/zapcore" |
| "gopkg.in/natefinch/lumberjack.v2" |
| ) |
|
|
| var ( |
| defaultEncoderCfg = EncoderCfg{ |
| TimeKey: "ts", |
| LevelKey: "level", |
| NameKey: "logger", |
| CallerKey: "caller", |
| MessageKey: "msg", |
| StacktraceKey: "stacktrace", |
| Level: "capitalColor", |
| Time: "ISO8601", |
| Duration: "seconds", |
| Caller: "short", |
| Encoding: "console", |
| } |
| defaultRotateCfg = RotateCfg{ |
| MaxSize: 10, |
| MaxBackups: 5, |
| MaxAge: 30, |
| Compress: false, |
| } |
|
|
| logger = &Logger{ |
| rotate: defaultRotateCfg, |
| encoder: defaultEncoderCfg, |
| Level: zapcore.InfoLevel, |
| } |
|
|
| infoLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { |
| return lvl == zapcore.InfoLevel |
| }) |
|
|
| errorLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { |
| return lvl >= zapcore.ErrorLevel |
| }) |
|
|
| accessLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { |
| return lvl == zapcore.WarnLevel |
| }) |
| ) |
|
|
| func init() { |
| logger.Init() |
| } |
|
|
| type Logger struct { |
| logger *zap.Logger |
| sugaredLogger *zap.SugaredLogger |
|
|
| infoLogOff bool |
| errorLogOff bool |
| accessLogOff bool |
|
|
| accessAssetsLogOff bool |
|
|
| debug bool |
|
|
| sqlLogOpen bool |
|
|
| infoLogPath string |
| errorLogPath string |
| accessLogPath string |
|
|
| rotate RotateCfg |
| encoder EncoderCfg |
|
|
| Level zapcore.Level |
| } |
|
|
| type EncoderCfg struct { |
| TimeKey string |
| LevelKey string |
| NameKey string |
| CallerKey string |
| MessageKey string |
| StacktraceKey string |
| Level string |
| Time string |
| Duration string |
| Caller string |
| Encoding string |
| } |
|
|
| type RotateCfg struct { |
| MaxSize int |
| MaxBackups int |
| MaxAge int |
| Compress bool |
| } |
|
|
| func (l *Logger) Init() { |
| zapLogger := zap.New(zapcore.NewTee( |
| zapcore.NewCore(l.getEncoder(l.encoder.LevelKey), l.getLogWriter(l.infoLogPath), infoLevelEnabler), |
| zapcore.NewCore(l.getEncoder(l.encoder.LevelKey), l.getLogWriter(l.errorLogPath), errorLevelEnabler), |
| zapcore.NewCore(l.getEncoder(""), l.getLogWriter(l.accessLogPath), accessLevelEnabler), |
| ), zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(errorLevelEnabler)) |
| l.sugaredLogger = zapLogger.Sugar() |
| l.logger = zapLogger |
| } |
|
|
| func (l *Logger) getEncoder(levelKey string) zapcore.Encoder { |
|
|
| var ( |
| timeEncoder = new(zapcore.TimeEncoder) |
| durationEncoder = new(zapcore.DurationEncoder) |
| callerEncoder = new(zapcore.CallerEncoder) |
| nameEncoder = new(zapcore.NameEncoder) |
| levelEncoder = new(zapcore.LevelEncoder) |
| ) |
|
|
| _ = timeEncoder.UnmarshalText([]byte(l.encoder.Time)) |
| _ = durationEncoder.UnmarshalText([]byte(l.encoder.Duration)) |
| _ = callerEncoder.UnmarshalText([]byte(l.encoder.Caller)) |
| _ = nameEncoder.UnmarshalText([]byte("full")) |
| _ = levelEncoder.UnmarshalText([]byte(l.encoder.Level)) |
|
|
| encoderConfig := zapcore.EncoderConfig{ |
| TimeKey: l.encoder.TimeKey, |
| LevelKey: levelKey, |
| NameKey: l.encoder.NameKey, |
| CallerKey: l.encoder.CallerKey, |
| MessageKey: l.encoder.MessageKey, |
| StacktraceKey: l.encoder.StacktraceKey, |
| LineEnding: zapcore.DefaultLineEnding, |
| EncodeLevel: *levelEncoder, |
| EncodeTime: *timeEncoder, |
| EncodeDuration: *durationEncoder, |
| EncodeCaller: *callerEncoder, |
| EncodeName: *nameEncoder, |
| } |
|
|
| return filterZapEncoder(l.encoder.Encoding, encoderConfig) |
| } |
|
|
| func (l *Logger) getLogWriter(path string) zapcore.WriteSyncer { |
| if path != "" { |
| lumberJackLogger := &lumberjack.Logger{ |
| Filename: path, |
| MaxSize: l.rotate.MaxSize, |
| MaxBackups: l.rotate.MaxBackups, |
| MaxAge: l.rotate.MaxAge, |
| Compress: l.rotate.Compress, |
| } |
| if l.debug { |
| return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger)) |
| } |
| return zapcore.AddSync(lumberJackLogger) |
| } |
| return zapcore.AddSync(os.Stdout) |
| } |
|
|
| func (l *Logger) SetRotate(cfg RotateCfg) { |
| if cfg.MaxSize != 0 && cfg.MaxAge != 0 && cfg.MaxBackups != 0 { |
| l.rotate = cfg |
| } |
| } |
|
|
| func (l *Logger) SetEncoder(cfg EncoderCfg) { |
| cfg.TimeKey = utils.SetDefault(cfg.TimeKey, "", defaultEncoderCfg.TimeKey) |
| cfg.LevelKey = utils.SetDefault(cfg.LevelKey, "", defaultEncoderCfg.LevelKey) |
| cfg.NameKey = utils.SetDefault(cfg.NameKey, "", defaultEncoderCfg.NameKey) |
| cfg.CallerKey = utils.SetDefault(cfg.CallerKey, "", defaultEncoderCfg.CallerKey) |
| cfg.MessageKey = utils.SetDefault(cfg.MessageKey, "", defaultEncoderCfg.MessageKey) |
| cfg.StacktraceKey = utils.SetDefault(cfg.StacktraceKey, "", defaultEncoderCfg.StacktraceKey) |
| cfg.Level = utils.SetDefault(cfg.Level, "", defaultEncoderCfg.Level) |
| cfg.Time = utils.SetDefault(cfg.Time, "", defaultEncoderCfg.Time) |
| cfg.Duration = utils.SetDefault(cfg.Duration, "", defaultEncoderCfg.Duration) |
| cfg.Caller = utils.SetDefault(cfg.Caller, "", defaultEncoderCfg.Caller) |
| cfg.Encoding = utils.SetDefault(cfg.Encoding, "", defaultEncoderCfg.Encoding) |
| l.encoder = cfg |
| } |
|
|
| type Config struct { |
| InfoLogOff bool |
| ErrorLogOff bool |
| AccessLogOff bool |
|
|
| SqlLogOpen bool |
|
|
| InfoLogPath string |
| ErrorLogPath string |
| AccessLogPath string |
|
|
| AccessAssetsLogOff bool |
|
|
| Rotate RotateCfg |
| Encode EncoderCfg |
|
|
| Level int8 |
|
|
| Debug bool |
| } |
|
|
| func InitWithConfig(cfg Config) { |
| logger.infoLogPath = cfg.InfoLogPath |
| logger.infoLogOff = cfg.InfoLogOff |
| logger.errorLogPath = cfg.ErrorLogPath |
| logger.errorLogOff = cfg.ErrorLogOff |
| logger.accessLogPath = cfg.AccessLogPath |
| logger.accessLogOff = cfg.AccessLogOff |
| logger.sqlLogOpen = cfg.SqlLogOpen |
| logger.accessAssetsLogOff = cfg.AccessAssetsLogOff |
| logger.debug = cfg.Debug |
| logger.SetRotate(cfg.Rotate) |
| logger.SetEncoder(cfg.Encode) |
| logger.Level = filterZapAtomicLevelByViper(cfg.Level) |
| logger.Init() |
| } |
|
|
| func SetRotate(cfg RotateCfg) { |
| logger.rotate = cfg |
| logger.Init() |
| } |
|
|
| |
| func OpenSQLLog() { |
| logger.sqlLogOpen = true |
| } |
|
|
| |
| func Debug(info ...interface{}) { |
| if !logger.infoLogOff { |
| if logger.Level <= zapcore.DebugLevel { |
| logger.sugaredLogger.Info(info...) |
| } |
| } |
| } |
|
|
| |
| func Debugf(template string, args ...interface{}) { |
| if !logger.infoLogOff && logger.Level <= zapcore.DebugLevel { |
| logger.sugaredLogger.Infof(template, args...) |
| } |
| } |
|
|
| |
| func Info(info ...interface{}) { |
| if !logger.infoLogOff && logger.Level <= zapcore.InfoLevel { |
| logger.sugaredLogger.Info(info...) |
| } |
| } |
|
|
| |
| func InfoCtx(ctx *context.Context, format string, args ...interface{}) { |
| if !logger.infoLogOff && logger.Level <= zapcore.InfoLevel { |
| logCtx(ctx, logger.logger.Info, format, args...) |
| } |
| } |
|
|
| type logFunc func(msg string, fields ...zapcore.Field) |
|
|
| func logCtx(ctx *context.Context, logFunc logFunc, format string, args ...interface{}) { |
| logFunc(fmt.Sprintf(format, args...), zap.String("traceID", trace.GetTraceID(ctx))) |
| } |
|
|
| |
| func Infof(template string, args ...interface{}) { |
| if !logger.infoLogOff && logger.Level <= zapcore.InfoLevel { |
| logger.sugaredLogger.Infof(template, args...) |
| } |
| } |
|
|
| |
| func Warn(info ...interface{}) { |
| if !logger.infoLogOff && logger.Level <= zapcore.WarnLevel { |
| logger.sugaredLogger.Warn(info...) |
| } |
| } |
|
|
| |
| func WarnCtx(ctx *context.Context, format string, args ...interface{}) { |
| if !logger.infoLogOff && logger.Level <= zapcore.WarnLevel { |
| logCtx(ctx, logger.logger.Warn, format, args...) |
| } |
| } |
|
|
| |
| func Warnf(template string, args ...interface{}) { |
| if !logger.infoLogOff && logger.Level <= zapcore.WarnLevel { |
| logger.sugaredLogger.Warnf(template, args...) |
| } |
| } |
|
|
| |
| func Error(err ...interface{}) { |
| if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { |
| logger.sugaredLogger.Error(err...) |
| } |
| } |
|
|
| |
| func ErrorCtx(ctx *context.Context, format string, args ...interface{}) { |
| if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { |
| logCtx(ctx, logger.logger.Error, format, args...) |
| } |
| } |
|
|
| |
| func Errorf(template string, args ...interface{}) { |
| if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { |
| logger.sugaredLogger.Errorf(template, args...) |
| } |
| } |
|
|
| |
| func Fatal(info ...interface{}) { |
| if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { |
| logger.sugaredLogger.Fatal(info...) |
| } |
| } |
|
|
| |
| func FatalCtx(ctx *context.Context, format string, args ...interface{}) { |
| if !logger.errorLogOff && logger.Level <= zapcore.FatalLevel { |
| logCtx(ctx, logger.logger.Fatal, format, args...) |
| } |
| } |
|
|
| |
| func Fatalf(template string, args ...interface{}) { |
| if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { |
| logger.sugaredLogger.Fatalf(template, args...) |
| } |
| } |
|
|
| |
| func Panic(info ...interface{}) { |
| logger.sugaredLogger.Panic(info...) |
| } |
|
|
| |
| func PanicCtx(ctx *context.Context, format string, args ...interface{}) { |
| logCtx(ctx, logger.logger.Panic, format, args...) |
| } |
|
|
| |
| func Panicf(template string, args ...interface{}) { |
| logger.sugaredLogger.Panicf(template, args...) |
| } |
|
|
| |
| func Access(ctx *context.Context) { |
| if !logger.accessLogOff && logger.Level <= zapcore.InfoLevel { |
| if logger.accessAssetsLogOff { |
| if filepath.Ext(ctx.Path()) == "" { |
| logger.logger.Info("[GoAdmin] access log", |
| zap.String("traceID", trace.GetTraceID(ctx)), |
| zap.String("statuscode", strconv.Itoa(ctx.Response.StatusCode)), |
| zap.String("method", string(ctx.Method())), |
| zap.String("path", ctx.Path())) |
| } |
| } else { |
| logger.logger.Info("[GoAdmin] access log", |
| zap.String("traceID", trace.GetTraceID(ctx)), |
| zap.String("statuscode", strconv.Itoa(ctx.Response.StatusCode)), |
| zap.String("method", string(ctx.Method())), |
| zap.String("path", ctx.Path())) |
| } |
| } |
| } |
|
|
| |
| func LogSQL(statement string, args []interface{}) { |
| if !logger.infoLogOff && logger.sqlLogOpen && statement != "" { |
| if logger.Level <= zapcore.InfoLevel { |
| logger.sugaredLogger.With("statement", statement, "args", args).Info("[GoAdmin]") |
| } |
| } |
| } |
|
|
| func filterZapEncoder(encoding string, encoderConfig zapcore.EncoderConfig) zapcore.Encoder { |
| var encoder zapcore.Encoder |
| switch encoding { |
| default: |
| encoder = zapcore.NewConsoleEncoder(encoderConfig) |
| case "json": |
| encoder = zapcore.NewJSONEncoder(encoderConfig) |
| case "console": |
| encoder = zapcore.NewConsoleEncoder(encoderConfig) |
| } |
| return encoder |
| } |
|
|
| func filterZapAtomicLevelByViper(level int8) zapcore.Level { |
| var atomViper zapcore.Level |
| switch level { |
| default: |
| atomViper = zap.InfoLevel |
| case -1: |
| atomViper = zap.DebugLevel |
| case 0: |
| atomViper = zap.InfoLevel |
| case 1: |
| atomViper = zap.WarnLevel |
| case 2: |
| atomViper = zap.ErrorLevel |
| } |
| return atomViper |
| } |
|
|