github.com/rudderlabs/rudder-go-kit@v0.30.0/logger/factory.go (about)

     1  package logger
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  	"strings"
     7  
     8  	"go.uber.org/zap"
     9  	"go.uber.org/zap/zapcore"
    10  	"gopkg.in/natefinch/lumberjack.v2"
    11  
    12  	"github.com/rudderlabs/rudder-go-kit/config"
    13  )
    14  
    15  // Default factory instance
    16  var Default *Factory
    17  
    18  func init() {
    19  	Default = NewFactory(config.Default)
    20  }
    21  
    22  // Reset resets the default logger factory.
    23  // Shall only be used by tests, until we move to a proper DI framework
    24  func Reset() {
    25  	Default = NewFactory(config.Default)
    26  }
    27  
    28  // NewFactory creates a new logger factory
    29  func NewFactory(config *config.Config, options ...Option) *Factory {
    30  	f := &Factory{}
    31  	f.config = newConfig(config)
    32  	for _, option := range options {
    33  		option.apply(f)
    34  	}
    35  	f.zap = newZapLogger(config, f.config)
    36  	f.sugaredZap = f.zap.Sugar()
    37  	return f
    38  }
    39  
    40  // Factory is a factory for creating new loggers
    41  type Factory struct {
    42  	config     *factoryConfig
    43  	zap        *zap.Logger
    44  	sugaredZap *zap.SugaredLogger
    45  }
    46  
    47  // NewLogger creates a new logger using the default logger factory
    48  func NewLogger() Logger {
    49  	return Default.NewLogger()
    50  }
    51  
    52  // NewLogger creates a new logger
    53  func (f *Factory) NewLogger() Logger {
    54  	return &logger{
    55  		logConfig:  f.config,
    56  		zap:        f.zap,
    57  		sugaredZap: f.sugaredZap,
    58  	}
    59  }
    60  
    61  // GetLoggingConfig returns the log levels for default logger factory
    62  func GetLoggingConfig() map[string]int {
    63  	return Default.GetLoggingConfig()
    64  }
    65  
    66  // GetLoggingConfig returns the log levels
    67  func (f *Factory) GetLoggingConfig() map[string]int {
    68  	return f.config.levelConfigCache.m
    69  }
    70  
    71  // SetLogLevel sets the log level for a module for the default logger factory
    72  func SetLogLevel(name, levelStr string) error {
    73  	return Default.SetLogLevel(name, levelStr)
    74  }
    75  
    76  // SetLogLevel sets the log level for a module
    77  func (f *Factory) SetLogLevel(name, levelStr string) error {
    78  	err := f.config.SetLogLevel(name, levelStr)
    79  	if err != nil {
    80  		f.sugaredZap.Info(f.config.levelConfig)
    81  	}
    82  	return err
    83  }
    84  
    85  // Sync flushes the loggers' output buffers for the default logger factory
    86  func Sync() {
    87  	Default.Sync()
    88  }
    89  
    90  // Sync flushes the loggers' output buffers
    91  func (f *Factory) Sync() {
    92  	_ = f.zap.Sync()
    93  	_ = f.sugaredZap.Sync()
    94  }
    95  
    96  func newConfig(config *config.Config) *factoryConfig {
    97  	fc := &factoryConfig{
    98  		levelConfig:      &syncMap[string, int]{m: make(map[string]int)},
    99  		levelConfigCache: &syncMap[string, int]{m: make(map[string]int)},
   100  	}
   101  	fc.rootLevel = levelMap[config.GetString("LOG_LEVEL", "INFO")]
   102  	fc.enableNameInLog = config.GetBool("Logger.enableLoggerNameInLog", true)
   103  	fc.enableStackTrace = config.GetReloadableBoolVar(false, "Logger.enableStackTrace")
   104  	config.GetBool("Logger.enableLoggerNameInLog", true)
   105  
   106  	// colon separated key value pairs
   107  	// Example: "router.GA=DEBUG:warehouse.REDSHIFT=DEBUG"
   108  	levelConfigStr := strings.TrimSpace(config.GetString("Logger.moduleLevels", ""))
   109  	if levelConfigStr != "" {
   110  		moduleLevelKVs := strings.Split(levelConfigStr, ":")
   111  		for _, moduleLevelKV := range moduleLevelKVs {
   112  			pair := strings.SplitN(moduleLevelKV, "=", 2)
   113  			if len(pair) < 2 {
   114  				continue
   115  			}
   116  			module := strings.TrimSpace(pair[0])
   117  			if module == "" {
   118  				continue
   119  			}
   120  			levelStr := strings.TrimSpace(pair[1])
   121  			level, ok := levelMap[levelStr]
   122  			if !ok {
   123  				continue
   124  			}
   125  			fc.levelConfig.set(module, level)
   126  		}
   127  	}
   128  	return fc
   129  }
   130  
   131  // newZapLogger configures the zap logger based on the config provide in config.toml
   132  func newZapLogger(config *config.Config, fc *factoryConfig) *zap.Logger {
   133  	var cores []zapcore.Core
   134  	if config.GetBool("Logger.enableConsole", true) {
   135  		var writeSyncer zapcore.WriteSyncer = os.Stdout
   136  		if config.GetBool("Logger.discardConsole", false) {
   137  			writeSyncer = &discarder{}
   138  		}
   139  		writer := zapcore.Lock(writeSyncer)
   140  		core := zapcore.NewCore(zapEncoder(config, config.GetBool("Logger.consoleJsonFormat", false)), writer, zapcore.DebugLevel)
   141  		cores = append(cores, core)
   142  	}
   143  	if config.GetBool("Logger.enableFile", false) {
   144  		writer := zapcore.AddSync(&lumberjack.Logger{
   145  			Filename:  config.GetString("Logger.logFileLocation", "/tmp/rudder_log.log"),
   146  			MaxSize:   config.GetInt("Logger.logFileSize", 100),
   147  			Compress:  true,
   148  			LocalTime: true,
   149  		})
   150  		core := zapcore.NewCore(zapEncoder(config, config.GetBool("Logger.fileJsonFormat", false)), writer, zapcore.DebugLevel)
   151  		cores = append(cores, core)
   152  	}
   153  	combinedCore := zapcore.NewTee(cores...)
   154  	var options []zap.Option
   155  	if config.GetBool("Logger.enableFileNameInLog", true) {
   156  		options = append(options, zap.AddCaller(), zap.AddCallerSkip(1))
   157  	}
   158  	if config.GetBool("Logger.enableStackTrace", false) {
   159  		// enables stack track for log level error
   160  		options = append(options, zap.AddStacktrace(zap.ErrorLevel))
   161  	}
   162  
   163  	if fc.clock != nil {
   164  		options = append(options, zap.WithClock(fc.clock))
   165  	}
   166  
   167  	return zap.New(combinedCore, options...)
   168  }
   169  
   170  // zapEncoder configures the output of the log
   171  func zapEncoder(config *config.Config, json bool) zapcore.Encoder {
   172  	encoderConfig := zap.NewProductionEncoderConfig()
   173  	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
   174  	if config.GetBool("Logger.enableTimestamp", true) {
   175  		encoderConfig.TimeKey = "ts"
   176  		encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
   177  	} else {
   178  		encoderConfig.TimeKey = ""
   179  	}
   180  	if json {
   181  		return zapcore.NewJSONEncoder(encoderConfig)
   182  	}
   183  	return zapcore.NewConsoleEncoder(encoderConfig)
   184  }
   185  
   186  type discarder struct{}
   187  
   188  func (d *discarder) Sync() error                 { return nil }
   189  func (d *discarder) Write(b []byte) (int, error) { return io.Discard.Write(b) }