github.com/kotovmak/go-admin@v1.1.1/modules/logger/logger.go (about)

     1  // Copyright 2019 GoAdmin Core Team. All rights reserved.
     2  // Use of this source code is governed by a Apache-2.0 style
     3  // license that can be found in the LICENSE file.
     4  
     5  package logger
     6  
     7  import (
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  
    12  	"github.com/kotovmak/go-admin/context"
    13  	"github.com/kotovmak/go-admin/modules/utils"
    14  	"github.com/mgutz/ansi"
    15  	"github.com/natefinch/lumberjack"
    16  	"go.uber.org/zap"
    17  	"go.uber.org/zap/zapcore"
    18  )
    19  
    20  var (
    21  	defaultEncoderCfg = EncoderCfg{
    22  		TimeKey:       "ts",
    23  		LevelKey:      "level",
    24  		NameKey:       "logger",
    25  		CallerKey:     "caller",
    26  		MessageKey:    "msg",
    27  		StacktraceKey: "stacktrace",
    28  		Level:         "capitalColor",
    29  		Time:          "ISO8601",
    30  		Duration:      "seconds",
    31  		Caller:        "short",
    32  		Encoding:      "console",
    33  	}
    34  	defaultRotateCfg = RotateCfg{
    35  		MaxSize:    10,
    36  		MaxBackups: 5,
    37  		MaxAge:     30,
    38  		Compress:   false,
    39  	}
    40  
    41  	logger = &Logger{
    42  		rotate:  defaultRotateCfg,
    43  		encoder: defaultEncoderCfg,
    44  		Level:   zapcore.InfoLevel,
    45  	}
    46  
    47  	infoLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    48  		return lvl == zapcore.InfoLevel
    49  	})
    50  
    51  	errorLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    52  		return lvl >= zapcore.ErrorLevel
    53  	})
    54  
    55  	accessLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    56  		return lvl == zapcore.WarnLevel
    57  	})
    58  )
    59  
    60  func init() {
    61  	logger.Init()
    62  }
    63  
    64  type Logger struct {
    65  	logger        *zap.Logger
    66  	sugaredLogger *zap.SugaredLogger
    67  
    68  	infoLogOff   bool
    69  	errorLogOff  bool
    70  	accessLogOff bool
    71  
    72  	accessAssetsLogOff bool
    73  
    74  	debug bool
    75  
    76  	sqlLogOpen bool
    77  
    78  	infoLogPath   string
    79  	errorLogPath  string
    80  	accessLogPath string
    81  
    82  	rotate  RotateCfg
    83  	encoder EncoderCfg
    84  
    85  	Level zapcore.Level
    86  }
    87  
    88  type EncoderCfg struct {
    89  	TimeKey       string
    90  	LevelKey      string
    91  	NameKey       string
    92  	CallerKey     string
    93  	MessageKey    string
    94  	StacktraceKey string
    95  	Level         string
    96  	Time          string
    97  	Duration      string
    98  	Caller        string
    99  	Encoding      string
   100  }
   101  
   102  type RotateCfg struct {
   103  	MaxSize    int
   104  	MaxBackups int
   105  	MaxAge     int
   106  	Compress   bool
   107  }
   108  
   109  func (l *Logger) Init() {
   110  	zapLogger := zap.New(zapcore.NewTee(
   111  		zapcore.NewCore(l.getEncoder(l.encoder.LevelKey), l.getLogWriter(l.infoLogPath), infoLevelEnabler),
   112  		zapcore.NewCore(l.getEncoder(l.encoder.LevelKey), l.getLogWriter(l.errorLogPath), errorLevelEnabler),
   113  		zapcore.NewCore(l.getEncoder(""), l.getLogWriter(l.accessLogPath), accessLevelEnabler),
   114  	), zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(errorLevelEnabler))
   115  	l.sugaredLogger = zapLogger.Sugar()
   116  	l.logger = zapLogger
   117  }
   118  
   119  func (l *Logger) getEncoder(levelKey string) zapcore.Encoder {
   120  
   121  	var (
   122  		timeEncoder     = new(zapcore.TimeEncoder)
   123  		durationEncoder = new(zapcore.DurationEncoder)
   124  		callerEncoder   = new(zapcore.CallerEncoder)
   125  		nameEncoder     = new(zapcore.NameEncoder)
   126  		levelEncoder    = new(zapcore.LevelEncoder)
   127  	)
   128  
   129  	_ = timeEncoder.UnmarshalText([]byte(l.encoder.Time))
   130  	_ = durationEncoder.UnmarshalText([]byte(l.encoder.Duration))
   131  	_ = callerEncoder.UnmarshalText([]byte(l.encoder.Caller))
   132  	_ = nameEncoder.UnmarshalText([]byte("full"))
   133  	_ = levelEncoder.UnmarshalText([]byte(l.encoder.Level))
   134  
   135  	encoderConfig := zapcore.EncoderConfig{
   136  		TimeKey:        l.encoder.TimeKey,
   137  		LevelKey:       levelKey,
   138  		NameKey:        l.encoder.NameKey,
   139  		CallerKey:      l.encoder.CallerKey,
   140  		MessageKey:     l.encoder.MessageKey,
   141  		StacktraceKey:  l.encoder.StacktraceKey,
   142  		LineEnding:     zapcore.DefaultLineEnding,
   143  		EncodeLevel:    *levelEncoder,
   144  		EncodeTime:     *timeEncoder,
   145  		EncodeDuration: *durationEncoder,
   146  		EncodeCaller:   *callerEncoder,
   147  		EncodeName:     *nameEncoder,
   148  	}
   149  
   150  	return filterZapEncoder(l.encoder.Encoding, encoderConfig)
   151  }
   152  
   153  func (l *Logger) getLogWriter(path string) zapcore.WriteSyncer {
   154  	if path != "" {
   155  		lumberJackLogger := &lumberjack.Logger{
   156  			Filename:   path,
   157  			MaxSize:    l.rotate.MaxSize,
   158  			MaxBackups: l.rotate.MaxBackups,
   159  			MaxAge:     l.rotate.MaxAge,
   160  			Compress:   l.rotate.Compress,
   161  		}
   162  		if l.debug {
   163  			return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger))
   164  		}
   165  		return zapcore.AddSync(lumberJackLogger)
   166  	}
   167  	return zapcore.AddSync(os.Stdout)
   168  }
   169  
   170  func (l *Logger) SetRotate(cfg RotateCfg) {
   171  	if cfg.MaxSize != 0 && cfg.MaxAge != 0 && cfg.MaxBackups != 0 {
   172  		l.rotate = cfg
   173  	}
   174  }
   175  
   176  func (l *Logger) SetEncoder(cfg EncoderCfg) {
   177  	cfg.TimeKey = utils.SetDefault(cfg.TimeKey, "", defaultEncoderCfg.TimeKey)
   178  	cfg.LevelKey = utils.SetDefault(cfg.LevelKey, "", defaultEncoderCfg.LevelKey)
   179  	cfg.NameKey = utils.SetDefault(cfg.NameKey, "", defaultEncoderCfg.NameKey)
   180  	cfg.CallerKey = utils.SetDefault(cfg.CallerKey, "", defaultEncoderCfg.CallerKey)
   181  	cfg.MessageKey = utils.SetDefault(cfg.MessageKey, "", defaultEncoderCfg.MessageKey)
   182  	cfg.StacktraceKey = utils.SetDefault(cfg.StacktraceKey, "", defaultEncoderCfg.StacktraceKey)
   183  	cfg.Level = utils.SetDefault(cfg.Level, "", defaultEncoderCfg.Level)
   184  	cfg.Time = utils.SetDefault(cfg.Time, "", defaultEncoderCfg.Time)
   185  	cfg.Duration = utils.SetDefault(cfg.Duration, "", defaultEncoderCfg.Duration)
   186  	cfg.Caller = utils.SetDefault(cfg.Caller, "", defaultEncoderCfg.Caller)
   187  	cfg.Encoding = utils.SetDefault(cfg.Encoding, "", defaultEncoderCfg.Encoding)
   188  	l.encoder = cfg
   189  }
   190  
   191  type Config struct {
   192  	InfoLogOff   bool
   193  	ErrorLogOff  bool
   194  	AccessLogOff bool
   195  
   196  	SqlLogOpen bool
   197  
   198  	InfoLogPath   string
   199  	ErrorLogPath  string
   200  	AccessLogPath string
   201  
   202  	AccessAssetsLogOff bool
   203  
   204  	Rotate RotateCfg
   205  	Encode EncoderCfg
   206  
   207  	Level int8
   208  
   209  	Debug bool
   210  }
   211  
   212  func InitWithConfig(cfg Config) {
   213  	logger.infoLogPath = cfg.InfoLogPath
   214  	logger.infoLogOff = cfg.InfoLogOff
   215  	logger.errorLogPath = cfg.ErrorLogPath
   216  	logger.errorLogOff = cfg.ErrorLogOff
   217  	logger.accessLogPath = cfg.AccessLogPath
   218  	logger.accessLogOff = cfg.AccessLogOff
   219  	logger.sqlLogOpen = cfg.SqlLogOpen
   220  	logger.accessAssetsLogOff = cfg.AccessAssetsLogOff
   221  	logger.debug = cfg.Debug
   222  	logger.SetRotate(cfg.Rotate)
   223  	logger.SetEncoder(cfg.Encode)
   224  	logger.Level = filterZapAtomicLevelByViper(cfg.Level)
   225  	logger.Init()
   226  }
   227  
   228  func SetRotate(cfg RotateCfg) {
   229  	logger.rotate = cfg
   230  	logger.Init()
   231  }
   232  
   233  // OpenSQLLog set the sqlLogOpen true.
   234  func OpenSQLLog() {
   235  	logger.sqlLogOpen = true
   236  }
   237  
   238  // Debug print the debug message.
   239  func Debug(info ...interface{}) {
   240  	if !logger.infoLogOff {
   241  		if logger.Level <= zapcore.DebugLevel {
   242  			logger.sugaredLogger.Info(info...)
   243  		}
   244  	}
   245  }
   246  
   247  // Debugf print the debug message.
   248  func Debugf(template string, args ...interface{}) {
   249  	if !logger.infoLogOff && logger.Level <= zapcore.DebugLevel {
   250  		logger.sugaredLogger.Infof(template, args...)
   251  	}
   252  }
   253  
   254  // Info print the info message.
   255  func Info(info ...interface{}) {
   256  	if !logger.infoLogOff && logger.Level <= zapcore.InfoLevel {
   257  		logger.sugaredLogger.Info(info...)
   258  	}
   259  }
   260  
   261  // Info print the info message.
   262  func Infof(template string, args ...interface{}) {
   263  	if !logger.infoLogOff && logger.Level <= zapcore.InfoLevel {
   264  		logger.sugaredLogger.Infof(template, args...)
   265  	}
   266  }
   267  
   268  // Warn print the warning message.
   269  func Warn(info ...interface{}) {
   270  	if !logger.infoLogOff && logger.Level <= zapcore.WarnLevel {
   271  		logger.sugaredLogger.Warn(info...)
   272  	}
   273  }
   274  
   275  // Warnf print the warning message.
   276  func Warnf(template string, args ...interface{}) {
   277  	if !logger.infoLogOff && logger.Level <= zapcore.WarnLevel {
   278  		logger.sugaredLogger.Warnf(template, args...)
   279  	}
   280  }
   281  
   282  // Error print the error message.
   283  func Error(err ...interface{}) {
   284  	if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel {
   285  		logger.sugaredLogger.Error(err...)
   286  	}
   287  }
   288  
   289  // Errorf print the error message.
   290  func Errorf(template string, args ...interface{}) {
   291  	if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel {
   292  		logger.sugaredLogger.Errorf(template, args...)
   293  	}
   294  }
   295  
   296  // Fatal print the fatal message.
   297  func Fatal(info ...interface{}) {
   298  	if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel {
   299  		logger.sugaredLogger.Fatal(info...)
   300  	}
   301  }
   302  
   303  // Fatalf print the fatal message.
   304  func Fatalf(template string, args ...interface{}) {
   305  	if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel {
   306  		logger.sugaredLogger.Fatalf(template, args...)
   307  	}
   308  }
   309  
   310  // Fatal print the panic message.
   311  func Panic(info ...interface{}) {
   312  	logger.sugaredLogger.Panic(info...)
   313  }
   314  
   315  // Panicf print the panic message.
   316  func Panicf(template string, args ...interface{}) {
   317  	logger.sugaredLogger.Panicf(template, args...)
   318  }
   319  
   320  // Access print the access message.
   321  func Access(ctx *context.Context) {
   322  	if !logger.accessLogOff && logger.Level <= zapcore.InfoLevel {
   323  		temp := "[GoAdmin] %s %s %s"
   324  		if logger.accessAssetsLogOff {
   325  			if filepath.Ext(ctx.Path()) == "" {
   326  				logger.sugaredLogger.Warnf(temp,
   327  					ansi.Color(" "+strconv.Itoa(ctx.Response.StatusCode)+" ", "white:blue"),
   328  					ansi.Color(" "+string(ctx.Method())+"   ", "white:blue+h"),
   329  					ctx.Path())
   330  			}
   331  		} else {
   332  			logger.sugaredLogger.Warnf(temp,
   333  				ansi.Color(" "+strconv.Itoa(ctx.Response.StatusCode)+" ", "white:blue"),
   334  				ansi.Color(" "+string(ctx.Method())+"   ", "white:blue+h"),
   335  				ctx.Path())
   336  		}
   337  	}
   338  }
   339  
   340  // LogSQL print the sql info message.
   341  func LogSQL(statement string, args []interface{}) {
   342  	if !logger.infoLogOff && logger.sqlLogOpen && statement != "" {
   343  		if logger.Level <= zapcore.InfoLevel {
   344  			logger.sugaredLogger.With("statement", statement, "args", args).Info("[GoAdmin]")
   345  		}
   346  	}
   347  }
   348  
   349  func filterZapEncoder(encoding string, encoderConfig zapcore.EncoderConfig) zapcore.Encoder {
   350  	var encoder zapcore.Encoder
   351  	switch encoding {
   352  	default:
   353  		encoder = zapcore.NewConsoleEncoder(encoderConfig)
   354  	case "json":
   355  		encoder = zapcore.NewJSONEncoder(encoderConfig)
   356  	case "console":
   357  		encoder = zapcore.NewConsoleEncoder(encoderConfig)
   358  	}
   359  	return encoder
   360  }
   361  
   362  func filterZapAtomicLevelByViper(level int8) zapcore.Level {
   363  	var atomViper zapcore.Level
   364  	switch level {
   365  	default:
   366  		atomViper = zap.InfoLevel
   367  	case -1:
   368  		atomViper = zap.DebugLevel
   369  	case 0:
   370  		atomViper = zap.InfoLevel
   371  	case 1:
   372  		atomViper = zap.WarnLevel
   373  	case 2:
   374  		atomViper = zap.ErrorLevel
   375  	}
   376  	return atomViper
   377  }