123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- package logger
- import (
- "io"
- "os"
- "path/filepath"
- "time"
- "go.uber.org/zap"
- "go.uber.org/zap/zapcore"
- "gopkg.in/natefinch/lumberjack.v2"
- )
- const (
- // DefaultLevel the default log level
- DefaultLevel = zapcore.InfoLevel
- // DefaultTimeLayout the default time layout;
- DefaultTimeLayout = time.DateTime
- )
- // Option custom setup config
- type Option func(*option)
- type option struct {
- level zapcore.Level
- fields map[string]string
- file io.Writer
- timeLayout string
- disableConsole bool
- disableCaller bool
- }
- // WithDebugLevel only greater than 'level' will output
- func WithDebugLevel() Option {
- return func(opt *option) {
- opt.level = zapcore.DebugLevel
- }
- }
- // WithInfoLevel only greater than 'level' will output
- func WithInfoLevel() Option {
- return func(opt *option) {
- opt.level = zapcore.InfoLevel
- }
- }
- // WithWarnLevel only greater than 'level' will output
- func WithWarnLevel() Option {
- return func(opt *option) {
- opt.level = zapcore.WarnLevel
- }
- }
- // WithErrorLevel only greater than 'level' will output
- func WithErrorLevel() Option {
- return func(opt *option) {
- opt.level = zapcore.ErrorLevel
- }
- }
- // WithField add some field(s) to log
- func WithField(key, value string) Option {
- return func(opt *option) {
- opt.fields[key] = value
- }
- }
- // WithFileP write log to some file
- func WithFileP(file string) Option {
- dir := filepath.Dir(file)
- if err := os.MkdirAll(dir, 0766); err != nil {
- panic(err)
- }
- f, err := os.OpenFile(file, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0766)
- if err != nil {
- panic(err)
- }
- return func(opt *option) {
- opt.file = zapcore.Lock(f)
- }
- }
- // WithFileRotationP write log to some file with rotation
- func WithFileRotationP(file string) Option {
- dir := filepath.Dir(file)
- if err := os.MkdirAll(dir, 0766); err != nil {
- panic(err)
- }
- return func(opt *option) {
- opt.file = &lumberjack.Logger{ // concurrent-safed
- Filename: file, // 文件路径
- MaxSize: 128, // 单个文件最大尺寸,默认单位 M
- MaxBackups: 300, // 最多保留 300 个备份
- MaxAge: 30, // 最大时间,默认单位 day
- LocalTime: true, // 使用本地时间
- Compress: true, // 是否压缩 disabled by default
- }
- }
- }
- // WithTimeLayout custom time format
- func WithTimeLayout(timeLayout string) Option {
- return func(opt *option) {
- opt.timeLayout = timeLayout
- }
- }
- // WithDisableConsole WithEnableConsole write log to os.Stdout or os.Stderr
- func WithDisableConsole() Option {
- return func(opt *option) {
- opt.disableConsole = true
- }
- }
- // WithDisableCaller disable caller field
- func WithDisableCaller() Option {
- return func(opt *option) {
- opt.disableCaller = true
- }
- }
- // NewJSONLogger return a json-encoder zap logger,
- func NewJSONLogger(opts ...Option) (*zap.Logger, error) {
- opt := &option{level: DefaultLevel, fields: make(map[string]string)}
- for _, f := range opts {
- f(opt)
- }
- timeLayout := DefaultTimeLayout
- if opt.timeLayout != "" {
- timeLayout = opt.timeLayout
- }
- // similar to zap.NewProductionEncoderConfig()
- encoderConfig := zapcore.EncoderConfig{
- TimeKey: "time",
- LevelKey: "level",
- NameKey: "logger", // used by logger.Named(key); optional; useless
- CallerKey: "caller",
- MessageKey: "msg",
- StacktraceKey: "stacktrace", // use by zap.AddStacktrace; optional; useless
- LineEnding: zapcore.DefaultLineEnding,
- EncodeLevel: zapcore.LowercaseLevelEncoder, // 小写编码器
- EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
- enc.AppendString(t.Format(timeLayout))
- },
- EncodeDuration: zapcore.MillisDurationEncoder,
- EncodeCaller: zapcore.ShortCallerEncoder, // 全路径编码器
- }
- jsonEncoder := zapcore.NewJSONEncoder(encoderConfig)
- // lowPriority usd by info\debug\warn
- lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
- return lvl >= opt.level && lvl < zapcore.ErrorLevel
- })
- // highPriority usd by error\panic\fatal
- highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
- return lvl >= opt.level && lvl >= zapcore.ErrorLevel
- })
- stdout := zapcore.Lock(os.Stdout) // lock for concurrent safe
- stderr := zapcore.Lock(os.Stderr) // lock for concurrent safe
- core := zapcore.NewTee()
- if !opt.disableConsole {
- core = zapcore.NewTee(
- zapcore.NewCore(jsonEncoder,
- zapcore.NewMultiWriteSyncer(stdout),
- lowPriority,
- ),
- zapcore.NewCore(jsonEncoder,
- zapcore.NewMultiWriteSyncer(stderr),
- highPriority,
- ),
- )
- }
- if opt.file != nil {
- core = zapcore.NewTee(core,
- zapcore.NewCore(jsonEncoder,
- zapcore.AddSync(opt.file),
- zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
- return lvl >= opt.level
- }),
- ),
- )
- }
- logger := zap.New(core,
- zap.WithCaller(!opt.disableCaller),
- zap.ErrorOutput(stderr),
- )
- for key, value := range opt.fields {
- logger = logger.WithOptions(zap.Fields(zapcore.Field{Key: key, Type: zapcore.StringType, String: value}))
- }
- return logger, nil
- }
- var _ Meta = (*meta)(nil)
- // Meta key-value
- type Meta interface {
- Key() string
- Value() any
- meta()
- }
- type meta struct {
- key string
- value any
- }
- func (m *meta) Key() string {
- return m.key
- }
- func (m *meta) Value() any {
- return m.value
- }
- func (m *meta) meta() {}
- // NewMeta create meat
- func NewMeta(key string, value any) Meta {
- return &meta{key: key, value: value}
- }
- // WrapMeta wrap meta to zap fields
- func WrapMeta(err error, metas ...Meta) (fields []zap.Field) {
- capacity := len(metas) + 1 // namespace meta
- if err != nil {
- capacity++
- }
- fields = make([]zap.Field, 0, capacity)
- if err != nil {
- fields = append(fields, zap.Error(err))
- }
- fields = append(fields, zap.Namespace("meta"))
- for _, meta := range metas {
- fields = append(fields, zap.Any(meta.Key(), meta.Value()))
- }
- return
- }
- // RestyClientLogger use by resty.Client
- func RestyClientLogger(logger *zap.Logger) *RestyClientLog {
- return &RestyClientLog{logger: logger}
- }
- type RestyClientLog struct {
- logger *zap.Logger
- }
- func (r *RestyClientLog) Errorf(format string, v ...any) {
- r.logger.Sugar().Errorf(format, v)
- }
- func (r *RestyClientLog) Warnf(format string, v ...any) {
- r.logger.Sugar().Warnf(format, v)
- }
- func (r *RestyClientLog) Debugf(format string, v ...any) {
- r.logger.Sugar().Debugf(format, v)
- }
|