github.com/zhiqiangxu/util@v0.0.0-20230112053021-0a7aee056cd5/logger/zap.go (about)

     1  package logger
     2  
     3  import (
     4  	"errors"
     5  	"sort"
     6  	"time"
     7  
     8  	"go.uber.org/zap"
     9  	"go.uber.org/zap/zapcore"
    10  )
    11  
    12  var errEncodingNotSupported = errors.New("encoding not supported")
    13  
    14  // New is similar to Config.Build except that info and error logs are separated
    15  // only json/console encoder is supported (zap doesn't provide a way to refer to other encoders)
    16  func New(cfg zap.Config) (logger *zap.Logger, err error) {
    17  
    18  	sink, errSink, err := openSinks(cfg)
    19  	if err != nil {
    20  		return
    21  	}
    22  
    23  	var encoder zapcore.Encoder
    24  	switch cfg.Encoding {
    25  	case "json":
    26  		encoder = zapcore.NewJSONEncoder(cfg.EncoderConfig)
    27  	case "console":
    28  		encoder = zapcore.NewConsoleEncoder(cfg.EncoderConfig)
    29  	default:
    30  		err = errEncodingNotSupported
    31  		return
    32  	}
    33  
    34  	logLevel := cfg.Level.Level()
    35  	stdoutPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    36  		return lvl >= logLevel && lvl < zapcore.ErrorLevel
    37  	})
    38  	stderrPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    39  		return lvl >= zapcore.ErrorLevel
    40  	})
    41  
    42  	core := zapcore.NewTee(
    43  		zapcore.NewCore(encoder, sink, stdoutPriority),
    44  		zapcore.NewCore(encoder, errSink, stderrPriority),
    45  	)
    46  
    47  	return zap.New(core, buildOptions(cfg, errSink)...), nil
    48  }
    49  
    50  func openSinks(cfg zap.Config) (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
    51  	sink, closeOut, err := zap.Open(cfg.OutputPaths...)
    52  	if err != nil {
    53  		return nil, nil, err
    54  	}
    55  	errSink, _, err := zap.Open(cfg.ErrorOutputPaths...)
    56  	if err != nil {
    57  		closeOut()
    58  		return nil, nil, err
    59  	}
    60  	return sink, errSink, nil
    61  }
    62  
    63  func buildOptions(cfg zap.Config, errSink zapcore.WriteSyncer) []zap.Option {
    64  	opts := []zap.Option{zap.ErrorOutput(errSink)}
    65  
    66  	if cfg.Development {
    67  		opts = append(opts, zap.Development())
    68  	}
    69  
    70  	if !cfg.DisableCaller {
    71  		opts = append(opts, zap.AddCaller())
    72  	}
    73  
    74  	stackLevel := zap.ErrorLevel
    75  	if cfg.Development {
    76  		stackLevel = zap.WarnLevel
    77  	}
    78  	if !cfg.DisableStacktrace {
    79  		opts = append(opts, zap.AddStacktrace(stackLevel))
    80  	}
    81  
    82  	if cfg.Sampling != nil {
    83  		opts = append(opts, zap.WrapCore(func(core zapcore.Core) zapcore.Core {
    84  			return zapcore.NewSampler(core, time.Second, int(cfg.Sampling.Initial), int(cfg.Sampling.Thereafter))
    85  		}))
    86  	}
    87  
    88  	if len(cfg.InitialFields) > 0 {
    89  		fs := make([]zap.Field, 0, len(cfg.InitialFields))
    90  		keys := make([]string, 0, len(cfg.InitialFields))
    91  		for k := range cfg.InitialFields {
    92  			keys = append(keys, k)
    93  		}
    94  		sort.Strings(keys)
    95  		for _, k := range keys {
    96  			fs = append(fs, zap.Any(k, cfg.InitialFields[k]))
    97  		}
    98  		opts = append(opts, zap.Fields(fs...))
    99  	}
   100  
   101  	return opts
   102  }