github.com/safedep/dry@v0.0.0-20241016050132-a15651f0548b/log/zap.go (about)

     1  package log
     2  
     3  import (
     4  	"os"
     5  
     6  	"go.uber.org/zap"
     7  	"go.uber.org/zap/zapcore"
     8  	"gopkg.in/natefinch/lumberjack.v2"
     9  )
    10  
    11  // InitZapLogger initializes a zap based logger
    12  // and sets it as the default logger using SetGlobal
    13  func InitZapLogger(name, env string) {
    14  	logger, err := newZapLogger(name, env)
    15  	if err != nil {
    16  		panic(err)
    17  	}
    18  
    19  	SetGlobal(logger)
    20  }
    21  
    22  type zapLoggerWrapper struct {
    23  	logger        *zap.Logger
    24  	sugaredLogger *zap.SugaredLogger
    25  }
    26  
    27  func newZapLogger(name, env string) (Logger, error) {
    28  	// Start with the default log level
    29  	level := zap.NewAtomicLevelAt(zapcore.InfoLevel)
    30  
    31  	// Override based on env configuration
    32  	logLevelFromEnv := os.Getenv(loggerKeyEnvLogLevel)
    33  	switch logLevelFromEnv {
    34  	case logLevelNameDebug:
    35  		level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
    36  	case logLevelNameWarn:
    37  		level = zap.NewAtomicLevelAt(zapcore.WarnLevel)
    38  	case logLevelNameError:
    39  		level = zap.NewAtomicLevelAt(zapcore.ErrorLevel)
    40  	}
    41  
    42  	// Our default console logger using development config
    43  	developmentConfig := zap.NewDevelopmentEncoderConfig()
    44  	developmentConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
    45  
    46  	consoleEncoder := zapcore.NewConsoleEncoder(developmentConfig)
    47  
    48  	// Create zap core using our default logger. This is required only in development
    49  	// mode. We should make this configurable i.e. skip the "costly" console log writer
    50  	// in production to avoid performance bottlenecks related to container console I/O
    51  	cores := []zapcore.Core{zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), level)}
    52  
    53  	// Add the file core with production config only when enabled
    54  	logFile := os.Getenv(loggerKeyEnvLogFileName)
    55  	if logFile != "" {
    56  		productionConfig := zap.NewProductionEncoderConfig()
    57  		productionConfig.TimeKey = "timestamp"
    58  		productionConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    59  
    60  		file := zapcore.AddSync(&lumberjack.Logger{
    61  			Filename:   logFile,
    62  			MaxSize:    100,
    63  			MaxBackups: 3,
    64  			MaxAge:     7,
    65  		})
    66  
    67  		fileEncoder := zapcore.NewJSONEncoder(productionConfig)
    68  		cores = append(cores, zapcore.NewCore(fileEncoder, zapcore.AddSync(file), level))
    69  	}
    70  
    71  	core := zapcore.NewTee(cores...)
    72  
    73  	// We add a caller stack skip of 2 because the host app will be accessing the
    74  	// zap logger through methods in utils, which in turn will invoke the global
    75  	// logger implementation
    76  	logger := zap.New(core, zap.AddCallerSkip(2))
    77  
    78  	logger = logger.With(zap.String(loggerKeyServiceName, name))
    79  	logger = logger.With(zap.String(loggerKeyServiceEnv, env))
    80  	logger = logger.With(zap.String(loggerKeyLoggerType, "zap"))
    81  
    82  	return &zapLoggerWrapper{
    83  		logger:        logger,
    84  		sugaredLogger: logger.Sugar(),
    85  	}, nil
    86  }
    87  
    88  func (z *zapLoggerWrapper) Infof(msg string, args ...any) {
    89  	z.sugaredLogger.Infof(msg, args...)
    90  }
    91  
    92  func (z *zapLoggerWrapper) Warnf(msg string, args ...any) {
    93  	z.sugaredLogger.Warnf(msg, args...)
    94  }
    95  
    96  func (z *zapLoggerWrapper) Errorf(msg string, args ...any) {
    97  	z.sugaredLogger.Errorf(msg, args...)
    98  }
    99  
   100  func (z *zapLoggerWrapper) Debugf(msg string, args ...any) {
   101  	z.sugaredLogger.Debugf(msg, args...)
   102  }
   103  
   104  func (z *zapLoggerWrapper) Fatalf(msg string, args ...any) {
   105  	z.sugaredLogger.Fatalf(msg, args...)
   106  }
   107  
   108  func (z *zapLoggerWrapper) With(args map[string]any) Logger {
   109  	var fields []zap.Field
   110  	for key, value := range args {
   111  		fields = append(fields, zap.Any(key, value))
   112  	}
   113  
   114  	logger := z.logger.With(fields...).WithOptions(zap.AddCallerSkip(1))
   115  	return &zapLoggerWrapper{
   116  		logger:        logger,
   117  		sugaredLogger: logger.Sugar(),
   118  	}
   119  }