gitee.com/lh-her-team/common@v1.5.1/log/log.go (about)

     1  package log
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"hash/crc32"
     7  	"io"
     8  	"log"
     9  	"os"
    10  	"strings"
    11  	"time"
    12  
    13  	rotatelogs "gitee.com/lh-her-team/common/log/file-rotatelogs"
    14  	"go.uber.org/zap"
    15  	"go.uber.org/zap/zapcore"
    16  )
    17  
    18  //LOG_LEVEL 日志级别,int类型,内部接口使用常量
    19  type LOG_LEVEL int
    20  
    21  const (
    22  	LEVEL_DEBUG LOG_LEVEL = iota
    23  	LEVEL_INFO
    24  	LEVEL_WARN
    25  	LEVEL_ERROR
    26  )
    27  
    28  // 日志级别,配置文件定义的常量
    29  const (
    30  	DEBUG = "DEBUG"
    31  	INFO  = "INFO"
    32  	WARN  = "WARN"
    33  	ERROR = "ERROR"
    34  )
    35  
    36  // GetLogLevel 根据字符串型的日志级别,返回枚举型日志级别
    37  // @param lvl
    38  // @return LOG_LEVEL
    39  func GetLogLevel(lvl string) LOG_LEVEL {
    40  	switch strings.ToUpper(lvl) {
    41  	case ERROR:
    42  		return LEVEL_ERROR
    43  	case WARN:
    44  		return LEVEL_WARN
    45  	case INFO:
    46  		return LEVEL_INFO
    47  	case DEBUG:
    48  		return LEVEL_DEBUG
    49  	}
    50  	return LEVEL_INFO
    51  }
    52  
    53  func getZapLevel(lvl string) (*zapcore.Level, error) {
    54  	var zapLevel zapcore.Level
    55  	switch strings.ToUpper(lvl) {
    56  	case ERROR:
    57  		zapLevel = zap.ErrorLevel
    58  	case WARN:
    59  		zapLevel = zap.WarnLevel
    60  	case INFO:
    61  		zapLevel = zap.InfoLevel
    62  	case DEBUG:
    63  		zapLevel = zap.DebugLevel
    64  	default:
    65  		return nil, errors.New("invalid log level")
    66  	}
    67  	return &zapLevel, nil
    68  }
    69  
    70  // 日志切割默认配置
    71  const (
    72  	DEFAULT_MAX_AGE       = 365 // 日志最长保存时间,单位:天
    73  	DEFAULT_ROTATION_TIME = 6   // 日志滚动间隔,单位:小时
    74  	DEFAULT_ROTATION_SIZE = 100 // 默认的日志滚动大小,单位:MB
    75  )
    76  
    77  //日志滚动单位
    78  const (
    79  	ROTATION_SIZE_MB = 1024 * 1024
    80  )
    81  
    82  type color int
    83  
    84  const (
    85  	ColorBlack color = iota + 30
    86  	ColorRed
    87  	ColorGreen
    88  	ColorYellow
    89  	ColorBlue
    90  	ColorMagenta
    91  	ColorCyan
    92  	ColorWhite
    93  )
    94  
    95  var colorList = [...]color{ColorRed, ColorGreen, ColorYellow, ColorBlue, ColorMagenta}
    96  
    97  var hookMap = make(map[string]struct{})
    98  
    99  type LogConfig struct {
   100  	Module       string    // module: module name
   101  	ChainId      string    // chainId: chain id
   102  	LogPath      string    // logPath: log file save path
   103  	LogLevel     LOG_LEVEL // logLevel: log level
   104  	MaxAge       int       // maxAge: the maximum number of days to retain old log files
   105  	RotationTime int       // RotationTime: rotation time
   106  	RotationSize int64     // RotationSize: rotation size Mb
   107  	JsonFormat   bool      // jsonFormat: log file use json format
   108  	ShowLine     bool      // showLine: show filename and line number
   109  	LogInConsole bool      // logInConsole: show logs in console at the same time
   110  	ShowColor    bool      // if true, show color log
   111  	IsBrief      bool      // if true, only show log, won't print log level、caller func and line
   112  
   113  	// StackTraceLevel record a stack trace for all messages at or above a given level.
   114  	// Empty string or invalid level will not open stack trace.
   115  	StackTraceLevel string
   116  }
   117  
   118  func InitSugarLogger(logConfig *LogConfig, writer ...io.Writer) (*zap.SugaredLogger, zap.AtomicLevel) {
   119  	var level zapcore.Level
   120  	switch logConfig.LogLevel {
   121  	case LEVEL_DEBUG:
   122  		level = zap.DebugLevel
   123  	case LEVEL_INFO:
   124  		level = zap.InfoLevel
   125  	case LEVEL_WARN:
   126  		level = zap.WarnLevel
   127  	case LEVEL_ERROR:
   128  		level = zap.ErrorLevel
   129  	default:
   130  		level = zap.InfoLevel
   131  	}
   132  	aLevel := zap.NewAtomicLevel()
   133  	aLevel.SetLevel(level)
   134  	sugaredLogger := newLogger(logConfig, aLevel, writer...).Sugar()
   135  	return sugaredLogger, aLevel
   136  }
   137  
   138  func newLogger(logConfig *LogConfig, level zap.AtomicLevel, writer ...io.Writer) *zap.Logger {
   139  	var (
   140  		hook io.Writer
   141  		ok   bool
   142  		err  error
   143  	)
   144  	_, ok = hookMap[logConfig.LogPath]
   145  	if !ok {
   146  		hook, err = getHook(logConfig.LogPath, logConfig.MaxAge, logConfig.RotationTime, logConfig.RotationSize)
   147  		if err != nil {
   148  			log.Fatalf("new logger get hook failed, %s", err)
   149  		}
   150  		hookMap[logConfig.LogPath] = struct{}{}
   151  	} else {
   152  		hook, err = getHook(logConfig.LogPath, logConfig.MaxAge, 0, logConfig.RotationSize)
   153  		if err != nil {
   154  			log.Fatalf("new logger get hook failed, %s", err)
   155  		}
   156  	}
   157  	var syncer zapcore.WriteSyncer
   158  	syncers := []zapcore.WriteSyncer{zapcore.AddSync(hook)}
   159  	if logConfig.LogInConsole {
   160  		syncers = append(syncers, zapcore.AddSync(os.Stdout))
   161  	}
   162  	for _, outSyncer := range writer {
   163  		syncers = append(syncers, zapcore.AddSync(outSyncer))
   164  	}
   165  	syncer = zapcore.NewMultiWriteSyncer(syncers...)
   166  	var encoderConfig zapcore.EncoderConfig
   167  	if logConfig.IsBrief {
   168  		encoderConfig = zapcore.EncoderConfig{
   169  			TimeKey:    "time",
   170  			MessageKey: "msg",
   171  			EncodeTime: CustomTimeEncoder,
   172  			LineEnding: zapcore.DefaultLineEnding,
   173  		}
   174  	} else {
   175  		encoderConfig = zapcore.EncoderConfig{
   176  			TimeKey:        "time",
   177  			LevelKey:       "level",
   178  			NameKey:        "logger",
   179  			CallerKey:      "line",
   180  			MessageKey:     "msg",
   181  			StacktraceKey:  "stacktrace",
   182  			LineEnding:     zapcore.DefaultLineEnding,
   183  			EncodeLevel:    CustomLevelEncoder,
   184  			EncodeTime:     CustomTimeEncoder,
   185  			EncodeDuration: zapcore.SecondsDurationEncoder,
   186  			EncodeCaller:   zapcore.ShortCallerEncoder,
   187  			EncodeName:     zapcore.FullNameEncoder,
   188  		}
   189  	}
   190  	var encoder zapcore.Encoder
   191  	if logConfig.JsonFormat {
   192  		encoder = zapcore.NewJSONEncoder(encoderConfig)
   193  	} else {
   194  		encoder = zapcore.NewConsoleEncoder(encoderConfig)
   195  	}
   196  	core := zapcore.NewCore(
   197  		encoder,
   198  		syncer,
   199  		level,
   200  	)
   201  	chainId := fmt.Sprintf("@%s", logConfig.ChainId)
   202  	if logConfig.ShowColor {
   203  		chainId = getColorChainId(chainId)
   204  	}
   205  	var name string
   206  	if logConfig.ChainId != "" {
   207  		name = fmt.Sprintf("%s %s", logConfig.Module, chainId)
   208  	} else {
   209  		name = logConfig.Module
   210  	}
   211  	logger := zap.New(core).Named(name)
   212  	defer func(logger *zap.Logger) {
   213  		_ = logger.Sync()
   214  	}(logger)
   215  	if logConfig.ShowLine {
   216  		logger = logger.WithOptions(zap.AddCaller())
   217  	}
   218  	if lvl, err := getZapLevel(logConfig.StackTraceLevel); err == nil {
   219  		logger = logger.WithOptions(zap.AddStacktrace(lvl))
   220  	}
   221  	logger = logger.WithOptions(zap.AddCallerSkip(1))
   222  	return logger
   223  }
   224  
   225  func getHook(filename string, maxAge, rotationTime int, rotationSize int64) (io.Writer, error) {
   226  	hook, err := rotatelogs.New(
   227  		filename+".%Y%m%d%H",
   228  		rotatelogs.WithRotationTime(time.Hour*time.Duration(rotationTime)),
   229  		//filename+".%Y%m%d%H%M",
   230  		//rotatelogs.WithRotationSize(rotationSize*ROTATION_SIZE_MB),
   231  		rotatelogs.WithLinkName(filename),
   232  		rotatelogs.WithMaxAge(time.Hour*24*time.Duration(maxAge)),
   233  	)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	return hook, nil
   238  }
   239  
   240  // nolint: deadcode, unused
   241  func recognizeLogLevel(l string) LOG_LEVEL {
   242  	logLevel := strings.ToUpper(l)
   243  	var level LOG_LEVEL
   244  	switch logLevel {
   245  	case DEBUG:
   246  		level = LEVEL_DEBUG
   247  	case INFO:
   248  		level = LEVEL_INFO
   249  	case WARN:
   250  		level = LEVEL_WARN
   251  	case ERROR:
   252  		level = LEVEL_ERROR
   253  	default:
   254  		level = LEVEL_INFO
   255  	}
   256  	return level
   257  }
   258  
   259  func CustomLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
   260  	enc.AppendString("[" + level.CapitalString() + "]")
   261  }
   262  
   263  func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
   264  	enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
   265  }
   266  
   267  // nolint: deadcode, unused
   268  func showColor(color color, msg string) string {
   269  	return fmt.Sprintf("\033[%dm%s\033[0m", int(color), msg)
   270  }
   271  
   272  func showColorBold(color color, msg string) string {
   273  	return fmt.Sprintf("\033[%d;1m%s\033[0m", int(color), msg)
   274  }
   275  
   276  func getColorChainId(chainId string) string {
   277  	c := crc32.ChecksumIEEE([]byte(chainId))
   278  	color := colorList[int(c)%len(colorList)]
   279  	return showColorBold(color, chainId)
   280  }