gitee.com/h79/goutils@v1.22.10/common/logger/log.go (about)

     1  package logger
     2  
     3  import (
     4  	"context"
     5  	"flag"
     6  	"fmt"
     7  	"gitee.com/h79/goutils/common/system"
     8  	"github.com/natefinch/lumberjack"
     9  	"go.uber.org/zap"
    10  	"go.uber.org/zap/zapcore"
    11  	"io"
    12  	"os"
    13  	"path"
    14  	"runtime/debug"
    15  )
    16  
    17  var (
    18  	ndebugEnabled = false
    19  	logger        *zap.Logger
    20  )
    21  
    22  type Config struct {
    23  	Level          int8           `json:"level" yaml:"level" xml:"level"`
    24  	NDebug         bool           `json:"nDebug" yaml:"nDebug" xml:"nDebug"`
    25  	WriterEnabled  bool           `json:"writerEnabled" yaml:"writerEnabled" xml:"writerEnabled"` // 自己实现writer
    26  	ConsoleEnabled bool           `json:"console" yaml:"console" xml:"console"`
    27  	FileEnabled    bool           `json:"fileEnabled" yaml:"fileEnabled" xml:"fileEnabled"`
    28  	EncoderFormat  string         `json:"encoderFormat" yaml:"encoderFormat" xml:"encoderFormat"`
    29  	FilePath       string         `json:"filePath" yaml:"filePath" xml:"filePath"`
    30  	FileConfigs    []FileConfig   `json:"fileConfigs" yaml:"fileConfigs" xml:"fileConfigs"`
    31  	WriterConfigs  []WriterConfig `json:"writerConfigs" yaml:"writerConfigs" xml:"writerConfigs"`
    32  	hooks          []func(entry zapcore.Entry) error
    33  }
    34  
    35  type FileConfig struct {
    36  	MinLevel int8 `json:"minLevel" yaml:"minLevel" xml:"minLevel"`
    37  	MaxLevel int8 `json:"maxLevel" yaml:"maxLevel" xml:"maxLevel"`
    38  	// Name is the name of the logfile which will be placed inside the directory
    39  	Name string `json:"name" yaml:"name" xml:"name"`
    40  	// MaxSize the max size in MB of the logfile before it's rolled
    41  	MaxSize int `json:"maxSize" yaml:"maxSize" xml:"maxSize"`
    42  	// MaxBackups the max number of rolled files to keep
    43  	MaxBackups int `json:"backupNum" yaml:"backupNum" xml:"backupNum"`
    44  	// MaxAge the max age in days to keep a logfile
    45  	MaxAge int `json:"days" json:"days" xml:"days"`
    46  }
    47  
    48  type WriterConfig struct {
    49  	MinLevel  int8   `json:"minLevel" yaml:"minLevel" xml:"minLevel"`
    50  	MaxLevel  int8   `json:"maxLevel" yaml:"maxLevel" xml:"maxLevel"`
    51  	WriteType string `json:"type" yaml:"type" xml:"type"`
    52  	w         io.Writer
    53  }
    54  
    55  // 对外进行统一的封装
    56  const (
    57  	// RecoverLevel ()
    58  	RecoverLevel = int8(-3)
    59  	// NDebugLevel ()
    60  	NDebugLevel = int8(-2)
    61  
    62  	DebugLevel = int8(zap.DebugLevel)
    63  	InfoLevel  = int8(zap.InfoLevel)
    64  	// WarnLevel defines warn log level.
    65  	WarnLevel = int8(zap.WarnLevel)
    66  	// ErrorLevel defines error log level.
    67  	ErrorLevel = int8(zap.ErrorLevel)
    68  	// FatalLevel defines fatal log level.
    69  	FatalLevel = int8(zap.FatalLevel)
    70  	// PanicLevel defines panic log level.
    71  	PanicLevel = int8(zap.PanicLevel)
    72  )
    73  
    74  func init() {
    75  	flag.BoolVar(&ndebugEnabled, "ndebug", true, "not output debug log in release.")
    76  
    77  	system.DefRecoverFunc = func(r any) {
    78  		Recover(6, r)
    79  	}
    80  	if err := Configure(Config{ConsoleEnabled: true, Level: int8(zap.DebugLevel)}); err != nil {
    81  		panic("init log failure")
    82  	}
    83  }
    84  
    85  func Byte2(level int8, b []byte) string {
    86  	if level == NDebugLevel {
    87  		if ndebugEnabled {
    88  			return string(b)
    89  		}
    90  		return ""
    91  	}
    92  	if zap.L().Core().Enabled(zapcore.Level(level)) {
    93  		return string(b)
    94  	}
    95  	return ""
    96  }
    97  
    98  const msg = "system"
    99  
   100  // Info defines info log level.
   101  func Info(format string, args ...interface{}) {
   102  	if ce := logger.Check(zapcore.InfoLevel, msg); ce != nil {
   103  		ce.Write(ZLogf(format, args...))
   104  	}
   105  }
   106  
   107  // I defines info log level.
   108  func I(msg, format string, args ...interface{}) {
   109  	if ce := logger.Check(zapcore.InfoLevel, msg); ce != nil {
   110  		ce.Write(ZLogf(format, args...))
   111  	}
   112  }
   113  
   114  // Warn defines warn log level.
   115  func Warn(format string, args ...interface{}) {
   116  	if ce := logger.Check(zapcore.WarnLevel, msg); ce != nil {
   117  		ce.Write(ZLogf(format, args...))
   118  	}
   119  }
   120  
   121  // W defines warn log level.
   122  func W(msg, format string, args ...interface{}) {
   123  	if ce := logger.Check(zapcore.WarnLevel, msg); ce != nil {
   124  		ce.Write(ZLogf(format, args...))
   125  	}
   126  }
   127  
   128  // Error defines error log level.
   129  func Error(format string, args ...interface{}) {
   130  	if ce := logger.Check(zapcore.ErrorLevel, msg); ce != nil {
   131  		ce.Write(ZLogf(format, args...))
   132  	}
   133  }
   134  
   135  // E defines error log level.
   136  func E(msg, format string, args ...interface{}) {
   137  	if ce := logger.Check(zapcore.ErrorLevel, msg); ce != nil {
   138  		ce.Write(ZLogf(format, args...))
   139  	}
   140  }
   141  
   142  // Recover defines error log level.
   143  func Recover(callerSkip int, err interface{}) {
   144  	if callerSkip <= 0 {
   145  		callerSkip = 2
   146  	}
   147  	lg := zap.L()
   148  	lg = lg.WithOptions(zap.AddCallerSkip(callerSkip))
   149  	if ce := lg.Check(zapcore.Level(RecoverLevel), "recover"); ce != nil {
   150  		ce.Write(zap.Any("err", err), zap.String("stack", string(debug.Stack())))
   151  	}
   152  }
   153  
   154  // Fatal defines fatal log level.
   155  func Fatal(format string, args ...interface{}) {
   156  	if ce := logger.Check(zapcore.FatalLevel, msg); ce != nil {
   157  		ce.Write(ZLogf(format, args...))
   158  	}
   159  }
   160  
   161  // F defines fatal log level.
   162  func F(msg, format string, args ...interface{}) {
   163  	if ce := logger.Check(zapcore.FatalLevel, msg); ce != nil {
   164  		ce.Write(ZLogf(format, args...))
   165  	}
   166  }
   167  
   168  // Panic (format string, args ...interface{}) { defines panic log level.
   169  func Panic(format string, args ...interface{}) {
   170  	if ce := logger.Check(zapcore.PanicLevel, msg); ce != nil {
   171  		ce.Write(ZLogf(format, args...))
   172  	}
   173  }
   174  
   175  // P (format string, args ...interface{}) { defines panic log level.
   176  func P(msg, format string, args ...interface{}) {
   177  	if ce := logger.Check(zapcore.PanicLevel, msg); ce != nil {
   178  		ce.Write(ZLogf(format, args...))
   179  	}
   180  }
   181  
   182  func Debug(format string, args ...interface{}) {
   183  	if ce := logger.Check(zapcore.DebugLevel, msg); ce != nil {
   184  		ce.Write(ZLogf(format, args...))
   185  	}
   186  }
   187  
   188  func D(msg, format string, args ...interface{}) {
   189  	if ce := logger.Check(zapcore.DebugLevel, msg); ce != nil {
   190  		ce.Write(ZLogf(format, args...))
   191  	}
   192  }
   193  
   194  // NDebug
   195  /**
   196    正式环境,不输出日志
   197  */
   198  func NDebug(format string, args ...interface{}) {
   199  	if !ndebugEnabled {
   200  		return
   201  	}
   202  	if ce := logger.Check(zapcore.DebugLevel, msg); ce != nil {
   203  		ce.Write(ZLogf(format, args...))
   204  	}
   205  }
   206  
   207  func N(msg, format string, args ...interface{}) {
   208  	if !ndebugEnabled {
   209  		return
   210  	}
   211  	if ce := logger.Check(zapcore.DebugLevel, msg); ce != nil {
   212  		ce.Write(ZLogf(format, args...))
   213  	}
   214  }
   215  
   216  func SpecW(level int8, msg string, fields ...zapcore.Field) {
   217  	if ce := logger.Check(zapcore.Level(level), msg); ce != nil {
   218  		ce.Write(fields...)
   219  	}
   220  }
   221  
   222  func ZLog(info string) zap.Field {
   223  	return zap.String("log", info)
   224  }
   225  
   226  func ZLogf(format string, args ...any) zap.Field {
   227  	return zap.String("log", fmt.Sprintf(format, args...))
   228  }
   229  
   230  func TraceId(ctx context.Context) string {
   231  	ret, ok := ctx.Value("traceId").(string)
   232  	if ok {
   233  		return ret
   234  	}
   235  	return ""
   236  }
   237  
   238  func NTraceId(ctx context.Context) string {
   239  	if !ndebugEnabled {
   240  		return ""
   241  	}
   242  	return DTraceId(ctx)
   243  }
   244  
   245  func DTraceId(ctx context.Context) string {
   246  	if L().Core().Enabled(zapcore.DebugLevel) {
   247  		return TraceId(ctx)
   248  	}
   249  	return ""
   250  }
   251  
   252  func WTraceId(ctx context.Context) string {
   253  	if L().Core().Enabled(zapcore.WarnLevel) {
   254  		return TraceId(ctx)
   255  	}
   256  	return ""
   257  }
   258  
   259  func ITraceId(ctx context.Context) string {
   260  	if L().Core().Enabled(zapcore.InfoLevel) {
   261  		return TraceId(ctx)
   262  	}
   263  	return ""
   264  }
   265  
   266  func ETraceId(ctx context.Context) string {
   267  	if L().Core().Enabled(zapcore.ErrorLevel) {
   268  		return TraceId(ctx)
   269  	}
   270  	return ""
   271  }
   272  
   273  type Option func(config *Config)
   274  
   275  func WithWriterLevel(level int8, w io.Writer) Option {
   276  	return func(config *Config) {
   277  		if !config.WriterEnabled {
   278  			return
   279  		}
   280  		for i := range config.WriterConfigs {
   281  			if config.WriterConfigs[i].MinLevel == level {
   282  				config.WriterConfigs[i].w = w
   283  			}
   284  		}
   285  	}
   286  }
   287  
   288  func WithWriterType(writeType string, w io.Writer) Option {
   289  	return func(config *Config) {
   290  		if !config.WriterEnabled {
   291  			return
   292  		}
   293  		for i := range config.WriterConfigs {
   294  			if config.WriterConfigs[i].WriteType == writeType {
   295  				config.WriterConfigs[i].w = w
   296  			}
   297  		}
   298  	}
   299  }
   300  
   301  func WithHook(hook func(entry zapcore.Entry) error) Option {
   302  	return func(config *Config) {
   303  		config.hooks = append(config.hooks, hook)
   304  	}
   305  }
   306  
   307  func WithNDebug(enabled bool) Option {
   308  	return func(config *Config) {
   309  		config.NDebug = enabled
   310  	}
   311  }
   312  
   313  func SetNDebug(enable bool) {
   314  	ndebugEnabled = enable
   315  }
   316  
   317  func IsNDebug() bool {
   318  	return ndebugEnabled
   319  }
   320  
   321  func Configure(config Config, opts ...Option) error {
   322  	var cores []zapcore.Core
   323  	var encoder zapcore.Encoder
   324  
   325  	for ii := range opts {
   326  		opts[ii](&config)
   327  	}
   328  	SetNDebug(config.NDebug)
   329  
   330  	if config.ConsoleEnabled {
   331  		writer := zapcore.Lock(os.Stdout)
   332  		encoder = zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
   333  		cores = append(cores, zapcore.NewCore(encoder, writer, zapcore.Level(config.Level)))
   334  	}
   335  	if config.EncoderFormat == "console" {
   336  		encoder = zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
   337  	} else {
   338  		encoder = jsonEncoder()
   339  	}
   340  
   341  	//可以按级别写入不同的文件 zap.LevelEnablerFunc()
   342  	if config.FileEnabled {
   343  		if err := os.MkdirAll(config.FilePath, 0744); err != nil {
   344  			return fmt.Errorf("can't create log directory, path= '%s'", config.FilePath)
   345  		}
   346  		for i := range config.FileConfigs {
   347  			var fileConfig = config.FileConfigs[i]
   348  			var levelEnable = zap.LevelEnablerFunc(func(level zapcore.Level) bool {
   349  				return level < zapcore.Level(fileConfig.MaxLevel) && level >= zapcore.Level(fileConfig.MinLevel)
   350  			})
   351  			fileConfig.Name = path.Join(config.FilePath, fileConfig.Name)
   352  			w, err := newRollingFile(fileConfig)
   353  			if err != nil {
   354  				return err
   355  			}
   356  			writer := zapcore.AddSync(w)
   357  			cores = append(cores, zapcore.NewCore(encoder, writer, levelEnable))
   358  		}
   359  	}
   360  	if config.WriterEnabled {
   361  		for i := range config.WriterConfigs {
   362  			var writerConfig = config.WriterConfigs[i]
   363  			var levelEnable = zap.LevelEnablerFunc(func(level zapcore.Level) bool {
   364  				return level < zapcore.Level(writerConfig.MaxLevel) && level >= zapcore.Level(writerConfig.MinLevel)
   365  			})
   366  			if writerConfig.w == nil {
   367  				continue
   368  			}
   369  			writer := zapcore.AddSync(writerConfig.w)
   370  			cores = append(cores, zapcore.NewCore(encoder, writer, levelEnable))
   371  		}
   372  	}
   373  	if len(cores) > 0 {
   374  		lg := zap.New(zapcore.NewTee(cores...), zap.AddCaller())
   375  		zap.ReplaceGlobals(lg)
   376  	}
   377  	logger = zap.L().WithOptions(zap.AddCallerSkip(1), zap.Hooks(config.hooks...))
   378  	return nil
   379  }
   380  
   381  func jsonEncoder() zapcore.Encoder {
   382  	encoderConfig := zap.NewProductionEncoderConfig()
   383  	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
   384  	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
   385  	encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
   386  	encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
   387  	encoderConfig.TimeKey = "time"
   388  	return zapcore.NewJSONEncoder(encoderConfig)
   389  }
   390  
   391  // Context
   392  // Deprecated: this function simply calls L.
   393  func Context() *zap.Logger {
   394  	return zap.L()
   395  }
   396  
   397  func L() *zap.Logger {
   398  	return zap.L()
   399  }
   400  
   401  func newRollingFile(file FileConfig) (io.Writer, error) {
   402  	return &lumberjack.Logger{
   403  		Filename:   file.Name,
   404  		MaxBackups: file.MaxBackups, // files
   405  		MaxSize:    file.MaxSize,    // megabytes
   406  		MaxAge:     file.MaxAge,     // days
   407  	}, nil
   408  }