github.com/abolfazlbeh/zhycan@v0.0.0-20230819144214-24cf38237387/internal/logger/zap_wrapper.go (about) 1 package logger 2 3 // Imports needed list 4 import ( 5 "errors" 6 "fmt" 7 "github.com/abolfazlbeh/zhycan/internal/config" 8 "github.com/abolfazlbeh/zhycan/internal/utils" 9 "go.uber.org/zap" 10 "go.uber.org/zap/zapcore" 11 "os" 12 "path/filepath" 13 "strings" 14 "sync" 15 ) 16 17 // Mark: ZapWrapper 18 19 // ZapWrapper structure - implements Logger interface 20 type ZapWrapper struct { 21 name string 22 serviceName string 23 logger *zap.Logger 24 ch chan LogObject 25 initialized bool 26 wg sync.WaitGroup 27 operationType string 28 supportedOutput []string 29 } 30 31 // Constructor - It initializes the logger configuration params 32 func (l *ZapWrapper) Constructor(name string) error { 33 l.wg.Add(1) 34 defer l.wg.Done() 35 36 l.name = name 37 l.serviceName = config.GetManager().GetName() 38 l.operationType = config.GetManager().GetOperationType() 39 l.supportedOutput = []string{"console", "file"} 40 l.initialized = false 41 42 channelSize, err := config.GetManager().Get(l.name, "channel_size") 43 if err != nil { 44 return err 45 } 46 47 options, err := config.GetManager().Get(l.name, "options") 48 if err != nil { 49 return err 50 } 51 52 var optionArray []string 53 for _, v := range options.([]interface{}) { 54 optionArray = append(optionArray, v.(string)) 55 } 56 57 outputs, err := config.GetManager().Get(l.name, "outputs") 58 if err != nil { 59 return err 60 } 61 62 var outputArray []string 63 for _, v := range outputs.([]interface{}) { 64 outputArray = append(outputArray, v.(string)) 65 } 66 67 l.ch = make(chan LogObject, int(channelSize.(float64))) 68 69 if l.operationType == "prod" { 70 productionEncoderConfig := zap.NewProductionEncoderConfig() 71 productionEncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder 72 productionEncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder 73 74 var cores []zapcore.Core 75 for _, outputItem := range outputArray { 76 if utils.ArrayContains(&l.supportedOutput, outputItem) { 77 if outputItem == "console" { 78 level := zapcore.DebugLevel 79 80 key := fmt.Sprintf("%s.level", outputItem) 81 levelStr, err := config.GetManager().Get(l.name, key) 82 if err == nil { 83 level, err = zapcore.ParseLevel(levelStr.(string)) 84 if err != nil { 85 continue 86 } 87 } 88 89 consoleEncoder := zapcore.NewConsoleEncoder(productionEncoderConfig) 90 c := zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), level) 91 cores = append(cores, c) 92 } else if outputItem == "file" { 93 level := zapcore.DebugLevel 94 95 key := fmt.Sprintf("%s.level", outputItem) 96 levelStr, err := config.GetManager().Get(l.name, key) 97 if err == nil { 98 level, err = zapcore.ParseLevel(levelStr.(string)) 99 if err != nil { 100 continue 101 } 102 } 103 104 // Read the root path of logs 105 path := "logs" 106 key = fmt.Sprintf("%s.path", outputItem) 107 pathStr, err := config.GetManager().Get(l.name, key) 108 if err == nil { 109 if strings.TrimSpace(pathStr.(string)) != "" { 110 path = strings.TrimSpace(pathStr.(string)) 111 } 112 } 113 114 // Check the directory existed, If not create all the nested directories 115 if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { 116 err1 := os.MkdirAll(path, os.ModePerm) 117 if err1 != nil { 118 continue 119 } 120 } 121 122 expectLogPath := filepath.Join(path, fmt.Sprintf("%s.log", config.GetManager().GetName())) 123 logFile, err := os.OpenFile(expectLogPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) 124 if err != nil { 125 continue 126 } 127 writer := zapcore.AddSync(logFile) 128 fileEncoder := zapcore.NewJSONEncoder(productionEncoderConfig) 129 130 c := zapcore.NewCore(fileEncoder, writer, level) 131 cores = append(cores, c) 132 } 133 } 134 } 135 136 core := zapcore.NewTee( 137 cores..., 138 ) 139 140 if utils.ArrayContains(&optionArray, "stackTrace") && utils.ArrayContains(&optionArray, "caller") { 141 l.logger = zap.New(core, zap.AddStacktrace(zapcore.ErrorLevel), zap.AddCaller()) 142 } else if utils.ArrayContains(&optionArray, "stackTrace") { 143 l.logger = zap.New(core, zap.AddStacktrace(zapcore.ErrorLevel)) 144 } else if utils.ArrayContains(&optionArray, "caller") { 145 l.logger = zap.New(core, zap.AddCaller()) 146 } else { 147 l.logger = zap.New(core) 148 } 149 } else { // otherwise we create a development version (`dev`, `test`, ...) 150 developmentEncoderConfig := zap.NewDevelopmentEncoderConfig() 151 developmentEncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder 152 developmentEncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder 153 154 var cores []zapcore.Core 155 for _, outputItem := range outputArray { 156 if utils.ArrayContains(&l.supportedOutput, outputItem) { 157 if outputItem == "console" { 158 level := zapcore.DebugLevel 159 160 key := fmt.Sprintf("%s.level", outputItem) 161 levelStr, err := config.GetManager().Get(l.name, key) 162 if err == nil { 163 level, err = zapcore.ParseLevel(levelStr.(string)) 164 if err != nil { 165 continue 166 } 167 } 168 169 consoleEncoder := zapcore.NewConsoleEncoder(developmentEncoderConfig) 170 c := zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), level) 171 172 cores = append(cores, c) 173 } else if outputItem == "file" { 174 level := zapcore.DebugLevel 175 176 key := fmt.Sprintf("%s.level", outputItem) 177 levelStr, err := config.GetManager().Get(l.name, key) 178 if err == nil { 179 level, err = zapcore.ParseLevel(levelStr.(string)) 180 if err != nil { 181 continue 182 } 183 } 184 185 // Read the root path of logs 186 path := "logs" 187 key = fmt.Sprintf("%s.path", outputItem) 188 pathStr, err := config.GetManager().Get(l.name, key) 189 if err == nil { 190 if strings.TrimSpace(pathStr.(string)) != "" { 191 path = strings.TrimSpace(pathStr.(string)) 192 } 193 } 194 195 // Check the directory existed, If not create all the nested directories 196 if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { 197 err1 := os.MkdirAll(path, os.ModePerm) 198 if err1 != nil { 199 continue 200 } 201 } 202 203 expectLogPath := filepath.Join(path, fmt.Sprintf("%s.log", config.GetManager().GetName())) 204 logFile, err := os.OpenFile(expectLogPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) 205 if err != nil { 206 continue 207 } 208 writer := zapcore.AddSync(logFile) 209 fileEncoder := zapcore.NewJSONEncoder(developmentEncoderConfig) 210 211 c := zapcore.NewCore(fileEncoder, writer, level) 212 cores = append(cores, c) 213 } 214 } 215 } 216 217 core := zapcore.NewTee( 218 cores..., 219 ) 220 221 if utils.ArrayContains(&optionArray, "stackTrace") && utils.ArrayContains(&optionArray, "caller") { 222 l.logger = zap.New(core, zap.AddStacktrace(zapcore.ErrorLevel), zap.AddCaller()) 223 } else if utils.ArrayContains(&optionArray, "stackTrace") { 224 l.logger = zap.New(core, zap.AddStacktrace(zapcore.ErrorLevel)) 225 } else if utils.ArrayContains(&optionArray, "caller") { 226 l.logger = zap.New(core, zap.AddCaller()) 227 } else { 228 l.logger = zap.New(core) 229 } 230 } 231 232 go l.runner() 233 l.initialized = true 234 235 return nil 236 } 237 238 // Close - it closes logger channel 239 func (l *ZapWrapper) Close() { 240 l.wg.Wait() 241 242 _ = l.logger.Sync() 243 defer close(l.ch) 244 } 245 246 // Log - write log object to the channel 247 func (l *ZapWrapper) Log(obj *LogObject) { 248 l.wg.Wait() 249 250 go func(obj *LogObject) { 251 l.ch <- *obj 252 }(obj) 253 } 254 255 // IsInitialized - that returns boolean value whether it's initialized 256 func (l *ZapWrapper) IsInitialized() bool { 257 return l.initialized 258 } 259 260 // Instance - returns exact logger instance 261 func (l *ZapWrapper) Instance() *zap.Logger { 262 l.wg.Wait() 263 return l.logger 264 } 265 266 // Sync - call the sync method of the project 267 func (l *ZapWrapper) Sync() { 268 l.wg.Wait() 269 l.logger.Sync() 270 } 271 272 // runner - the goroutine that reads from channel and process it 273 func (l *ZapWrapper) runner() { 274 l.wg.Wait() 275 for c := range l.ch { 276 if c.Level.IsLogLevel() { 277 f := []zapcore.Field{ 278 zap.Any("service", l.serviceName), 279 zap.Any("module", c.Module), 280 zap.Any("log_type", c.LogType), 281 zap.Any("time", c.Time), 282 zap.Any("additional", c.Additional), 283 } 284 switch c.Level { 285 case DEBUG: 286 l.logger.Debug(fmt.Sprintf("%v", c.Message), f...) 287 break 288 case INFO: 289 l.logger.Info(fmt.Sprintf("%v", c.Message), f...) 290 break 291 case WARNING: 292 l.logger.Warn(fmt.Sprintf("%v", c.Message), f...) 293 break 294 case ERROR: 295 l.logger.Error(fmt.Sprintf("%v", c.Message), f...) 296 break 297 } 298 } 299 } 300 }