trpc.group/trpc-go/trpc-go@v1.0.2/log/zaplogger.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package log
    15  
    16  import (
    17  	"fmt"
    18  	"os"
    19  	"strconv"
    20  	"time"
    21  
    22  	"trpc.group/trpc-go/trpc-go/internal/report"
    23  	"trpc.group/trpc-go/trpc-go/log/rollwriter"
    24  
    25  	"go.uber.org/zap"
    26  	"go.uber.org/zap/zapcore"
    27  )
    28  
    29  var defaultConfig = []OutputConfig{
    30  	{
    31  		Writer:    "console",
    32  		Level:     "debug",
    33  		Formatter: "console",
    34  	},
    35  }
    36  
    37  // Some ZapCore constants.
    38  const (
    39  	ConsoleZapCore = "console"
    40  	FileZapCore    = "file"
    41  )
    42  
    43  // Levels is the map from string to zapcore.Level.
    44  var Levels = map[string]zapcore.Level{
    45  	"":      zapcore.DebugLevel,
    46  	"trace": zapcore.DebugLevel,
    47  	"debug": zapcore.DebugLevel,
    48  	"info":  zapcore.InfoLevel,
    49  	"warn":  zapcore.WarnLevel,
    50  	"error": zapcore.ErrorLevel,
    51  	"fatal": zapcore.FatalLevel,
    52  }
    53  
    54  var levelToZapLevel = map[Level]zapcore.Level{
    55  	LevelTrace: zapcore.DebugLevel,
    56  	LevelDebug: zapcore.DebugLevel,
    57  	LevelInfo:  zapcore.InfoLevel,
    58  	LevelWarn:  zapcore.WarnLevel,
    59  	LevelError: zapcore.ErrorLevel,
    60  	LevelFatal: zapcore.FatalLevel,
    61  }
    62  
    63  var zapLevelToLevel = map[zapcore.Level]Level{
    64  	zapcore.DebugLevel: LevelDebug,
    65  	zapcore.InfoLevel:  LevelInfo,
    66  	zapcore.WarnLevel:  LevelWarn,
    67  	zapcore.ErrorLevel: LevelError,
    68  	zapcore.FatalLevel: LevelFatal,
    69  }
    70  
    71  // NewZapLog creates a trpc default Logger from zap whose caller skip is set to 2.
    72  func NewZapLog(c Config) Logger {
    73  	return NewZapLogWithCallerSkip(c, 2)
    74  }
    75  
    76  // NewZapLogWithCallerSkip creates a trpc default Logger from zap.
    77  func NewZapLogWithCallerSkip(cfg Config, callerSkip int) Logger {
    78  	var (
    79  		cores  []zapcore.Core
    80  		levels []zap.AtomicLevel
    81  	)
    82  	for _, c := range cfg {
    83  		writer := GetWriter(c.Writer)
    84  		if writer == nil {
    85  			panic("log: writer core: " + c.Writer + " no registered")
    86  		}
    87  		decoder := &Decoder{OutputConfig: &c}
    88  		if err := writer.Setup(c.Writer, decoder); err != nil {
    89  			panic("log: writer core: " + c.Writer + " setup fail: " + err.Error())
    90  		}
    91  		cores = append(cores, decoder.Core)
    92  		levels = append(levels, decoder.ZapLevel)
    93  	}
    94  	return &zapLog{
    95  		levels: levels,
    96  		logger: zap.New(
    97  			zapcore.NewTee(cores...),
    98  			zap.AddCallerSkip(callerSkip),
    99  			zap.AddCaller(),
   100  		),
   101  	}
   102  }
   103  
   104  func newEncoder(c *OutputConfig) zapcore.Encoder {
   105  	encoderCfg := zapcore.EncoderConfig{
   106  		TimeKey:        GetLogEncoderKey("T", c.FormatConfig.TimeKey),
   107  		LevelKey:       GetLogEncoderKey("L", c.FormatConfig.LevelKey),
   108  		NameKey:        GetLogEncoderKey("N", c.FormatConfig.NameKey),
   109  		CallerKey:      GetLogEncoderKey("C", c.FormatConfig.CallerKey),
   110  		FunctionKey:    GetLogEncoderKey(zapcore.OmitKey, c.FormatConfig.FunctionKey),
   111  		MessageKey:     GetLogEncoderKey("M", c.FormatConfig.MessageKey),
   112  		StacktraceKey:  GetLogEncoderKey("S", c.FormatConfig.StacktraceKey),
   113  		LineEnding:     zapcore.DefaultLineEnding,
   114  		EncodeLevel:    zapcore.CapitalLevelEncoder,
   115  		EncodeTime:     NewTimeEncoder(c.FormatConfig.TimeFmt),
   116  		EncodeDuration: zapcore.StringDurationEncoder,
   117  		EncodeCaller:   zapcore.ShortCallerEncoder,
   118  	}
   119  	if c.EnableColor {
   120  		encoderCfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
   121  	}
   122  	switch c.Formatter {
   123  	case "console":
   124  		return zapcore.NewConsoleEncoder(encoderCfg)
   125  	case "json":
   126  		return zapcore.NewJSONEncoder(encoderCfg)
   127  	default:
   128  		return zapcore.NewConsoleEncoder(encoderCfg)
   129  	}
   130  }
   131  
   132  // GetLogEncoderKey gets user defined log output name, uses defKey if empty.
   133  func GetLogEncoderKey(defKey, key string) string {
   134  	if key == "" {
   135  		return defKey
   136  	}
   137  	return key
   138  }
   139  
   140  func newConsoleCore(c *OutputConfig) (zapcore.Core, zap.AtomicLevel) {
   141  	lvl := zap.NewAtomicLevelAt(Levels[c.Level])
   142  	return zapcore.NewCore(
   143  		newEncoder(c),
   144  		zapcore.Lock(os.Stdout),
   145  		lvl), lvl
   146  }
   147  
   148  func newFileCore(c *OutputConfig) (zapcore.Core, zap.AtomicLevel, error) {
   149  	opts := []rollwriter.Option{
   150  		rollwriter.WithMaxAge(c.WriteConfig.MaxAge),
   151  		rollwriter.WithMaxBackups(c.WriteConfig.MaxBackups),
   152  		rollwriter.WithCompress(c.WriteConfig.Compress),
   153  		rollwriter.WithMaxSize(c.WriteConfig.MaxSize),
   154  	}
   155  	// roll by time.
   156  	if c.WriteConfig.RollType != RollBySize {
   157  		opts = append(opts, rollwriter.WithRotationTime(c.WriteConfig.TimeUnit.Format()))
   158  	}
   159  	writer, err := rollwriter.NewRollWriter(c.WriteConfig.Filename, opts...)
   160  	if err != nil {
   161  		return nil, zap.AtomicLevel{}, err
   162  	}
   163  
   164  	// write mode.
   165  	var ws zapcore.WriteSyncer
   166  	switch m := c.WriteConfig.WriteMode; m {
   167  	case 0, WriteFast:
   168  		// Use WriteFast as default mode.
   169  		// It has better performance, discards logs on full and avoid blocking service.
   170  		ws = rollwriter.NewAsyncRollWriter(writer, rollwriter.WithDropLog(true))
   171  	case WriteSync:
   172  		ws = zapcore.AddSync(writer)
   173  	case WriteAsync:
   174  		ws = rollwriter.NewAsyncRollWriter(writer, rollwriter.WithDropLog(false))
   175  	default:
   176  		return nil, zap.AtomicLevel{}, fmt.Errorf("validating WriteMode parameter: got %d, "+
   177  			"but expect one of WriteFast(%d), WriteAsync(%d), or WriteSync(%d)", m, WriteFast, WriteAsync, WriteSync)
   178  	}
   179  
   180  	// log level.
   181  	lvl := zap.NewAtomicLevelAt(Levels[c.Level])
   182  	return zapcore.NewCore(
   183  		newEncoder(c),
   184  		ws, lvl,
   185  	), lvl, nil
   186  }
   187  
   188  // NewTimeEncoder creates a time format encoder.
   189  func NewTimeEncoder(format string) zapcore.TimeEncoder {
   190  	switch format {
   191  	case "":
   192  		return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
   193  			enc.AppendByteString(defaultTimeFormat(t))
   194  		}
   195  	case "seconds":
   196  		return zapcore.EpochTimeEncoder
   197  	case "milliseconds":
   198  		return zapcore.EpochMillisTimeEncoder
   199  	case "nanoseconds":
   200  		return zapcore.EpochNanosTimeEncoder
   201  	default:
   202  		return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
   203  			enc.AppendString(t.Format(format))
   204  		}
   205  	}
   206  }
   207  
   208  // defaultTimeFormat returns the default time format "2006-01-02 15:04:05.000",
   209  // which performs better than https://pkg.go.dev/time#Time.AppendFormat.
   210  func defaultTimeFormat(t time.Time) []byte {
   211  	t = t.Local()
   212  	year, month, day := t.Date()
   213  	hour, minute, second := t.Clock()
   214  	micros := t.Nanosecond() / 1000
   215  
   216  	buf := make([]byte, 23)
   217  	buf[0] = byte((year/1000)%10) + '0'
   218  	buf[1] = byte((year/100)%10) + '0'
   219  	buf[2] = byte((year/10)%10) + '0'
   220  	buf[3] = byte(year%10) + '0'
   221  	buf[4] = '-'
   222  	buf[5] = byte((month)/10) + '0'
   223  	buf[6] = byte((month)%10) + '0'
   224  	buf[7] = '-'
   225  	buf[8] = byte((day)/10) + '0'
   226  	buf[9] = byte((day)%10) + '0'
   227  	buf[10] = ' '
   228  	buf[11] = byte((hour)/10) + '0'
   229  	buf[12] = byte((hour)%10) + '0'
   230  	buf[13] = ':'
   231  	buf[14] = byte((minute)/10) + '0'
   232  	buf[15] = byte((minute)%10) + '0'
   233  	buf[16] = ':'
   234  	buf[17] = byte((second)/10) + '0'
   235  	buf[18] = byte((second)%10) + '0'
   236  	buf[19] = '.'
   237  	buf[20] = byte((micros/100000)%10) + '0'
   238  	buf[21] = byte((micros/10000)%10) + '0'
   239  	buf[22] = byte((micros/1000)%10) + '0'
   240  	return buf
   241  }
   242  
   243  // zapLog is a Logger implementation based on zaplogger.
   244  type zapLog struct {
   245  	levels []zap.AtomicLevel
   246  	logger *zap.Logger
   247  }
   248  
   249  func (l *zapLog) WithOptions(opts ...Option) Logger {
   250  	o := &options{}
   251  	for _, opt := range opts {
   252  		opt(o)
   253  	}
   254  	return &zapLog{
   255  		levels: l.levels,
   256  		logger: l.logger.WithOptions(zap.AddCallerSkip(o.skip)),
   257  	}
   258  }
   259  
   260  // With add user defined fields to Logger. Fields support multiple values.
   261  func (l *zapLog) With(fields ...Field) Logger {
   262  	zapFields := make([]zap.Field, len(fields))
   263  	for i := range fields {
   264  		zapFields[i] = zap.Any(fields[i].Key, fields[i].Value)
   265  	}
   266  
   267  	return &zapLog{
   268  		levels: l.levels,
   269  		logger: l.logger.With(zapFields...)}
   270  }
   271  
   272  func getLogMsg(args ...interface{}) string {
   273  	msg := fmt.Sprint(args...)
   274  	report.LogWriteSize.IncrBy(float64(len(msg)))
   275  	return msg
   276  }
   277  
   278  func getLogMsgf(format string, args ...interface{}) string {
   279  	msg := fmt.Sprintf(format, args...)
   280  	report.LogWriteSize.IncrBy(float64(len(msg)))
   281  	return msg
   282  }
   283  
   284  // Trace logs to TRACE log. Arguments are handled in the manner of fmt.Print.
   285  func (l *zapLog) Trace(args ...interface{}) {
   286  	if l.logger.Core().Enabled(zapcore.DebugLevel) {
   287  		l.logger.Debug(getLogMsg(args...))
   288  	}
   289  }
   290  
   291  // Tracef logs to TRACE log. Arguments are handled in the manner of fmt.Printf.
   292  func (l *zapLog) Tracef(format string, args ...interface{}) {
   293  	if l.logger.Core().Enabled(zapcore.DebugLevel) {
   294  		l.logger.Debug(getLogMsgf(format, args...))
   295  	}
   296  }
   297  
   298  // Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Print.
   299  func (l *zapLog) Debug(args ...interface{}) {
   300  	if l.logger.Core().Enabled(zapcore.DebugLevel) {
   301  		l.logger.Debug(getLogMsg(args...))
   302  	}
   303  }
   304  
   305  // Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf.
   306  func (l *zapLog) Debugf(format string, args ...interface{}) {
   307  	if l.logger.Core().Enabled(zapcore.DebugLevel) {
   308  		l.logger.Debug(getLogMsgf(format, args...))
   309  	}
   310  }
   311  
   312  // Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
   313  func (l *zapLog) Info(args ...interface{}) {
   314  	if l.logger.Core().Enabled(zapcore.InfoLevel) {
   315  		l.logger.Info(getLogMsg(args...))
   316  	}
   317  }
   318  
   319  // Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
   320  func (l *zapLog) Infof(format string, args ...interface{}) {
   321  	if l.logger.Core().Enabled(zapcore.InfoLevel) {
   322  		l.logger.Info(getLogMsgf(format, args...))
   323  	}
   324  }
   325  
   326  // Warn logs to WARNING log. Arguments are handled in the manner of fmt.Print.
   327  func (l *zapLog) Warn(args ...interface{}) {
   328  	if l.logger.Core().Enabled(zapcore.WarnLevel) {
   329  		l.logger.Warn(getLogMsg(args...))
   330  	}
   331  }
   332  
   333  // Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
   334  func (l *zapLog) Warnf(format string, args ...interface{}) {
   335  	if l.logger.Core().Enabled(zapcore.WarnLevel) {
   336  		l.logger.Warn(getLogMsgf(format, args...))
   337  	}
   338  }
   339  
   340  // Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
   341  func (l *zapLog) Error(args ...interface{}) {
   342  	if l.logger.Core().Enabled(zapcore.ErrorLevel) {
   343  		l.logger.Error(getLogMsg(args...))
   344  	}
   345  }
   346  
   347  // Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
   348  func (l *zapLog) Errorf(format string, args ...interface{}) {
   349  	if l.logger.Core().Enabled(zapcore.ErrorLevel) {
   350  		l.logger.Error(getLogMsgf(format, args...))
   351  	}
   352  }
   353  
   354  // Fatal logs to FATAL log. Arguments are handled in the manner of fmt.Print.
   355  func (l *zapLog) Fatal(args ...interface{}) {
   356  	if l.logger.Core().Enabled(zapcore.FatalLevel) {
   357  		l.logger.Fatal(getLogMsg(args...))
   358  	}
   359  }
   360  
   361  // Fatalf logs to FATAL log. Arguments are handled in the manner of fmt.Printf.
   362  func (l *zapLog) Fatalf(format string, args ...interface{}) {
   363  	if l.logger.Core().Enabled(zapcore.FatalLevel) {
   364  		l.logger.Fatal(getLogMsgf(format, args...))
   365  	}
   366  }
   367  
   368  // Sync calls the zap logger's Sync method, and flushes any buffered log entries.
   369  // Applications should take care to call Sync before exiting.
   370  func (l *zapLog) Sync() error {
   371  	return l.logger.Sync()
   372  }
   373  
   374  // SetLevel sets output log level.
   375  func (l *zapLog) SetLevel(output string, level Level) {
   376  	i, e := strconv.Atoi(output)
   377  	if e != nil {
   378  		return
   379  	}
   380  	if i < 0 || i >= len(l.levels) {
   381  		return
   382  	}
   383  	l.levels[i].SetLevel(levelToZapLevel[level])
   384  }
   385  
   386  // GetLevel gets output log level.
   387  func (l *zapLog) GetLevel(output string) Level {
   388  	i, e := strconv.Atoi(output)
   389  	if e != nil {
   390  		return LevelDebug
   391  	}
   392  	if i < 0 || i >= len(l.levels) {
   393  		return LevelDebug
   394  	}
   395  	return zapLevelToLevel[l.levels[i].Level()]
   396  }
   397  
   398  // CustomTimeFormat customize time format.
   399  // Deprecated: Use https://pkg.go.dev/time#Time.Format instead.
   400  func CustomTimeFormat(t time.Time, format string) string {
   401  	return t.Format(format)
   402  }
   403  
   404  // DefaultTimeFormat returns the default time format "2006-01-02 15:04:05.000".
   405  // Deprecated: Use https://pkg.go.dev/time#Time.AppendFormat instead.
   406  func DefaultTimeFormat(t time.Time) []byte {
   407  	return defaultTimeFormat(t)
   408  }