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 }