github.com/ronaksoft/rony@v0.16.26-0.20230807065236-1743dbfe6959/log/logger.go (about) 1 package log 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "runtime/debug" 8 "strings" 9 "sync" 10 11 "github.com/ronaksoft/rony/di" 12 "go.uber.org/zap" 13 "go.uber.org/zap/zapcore" 14 ) 15 16 /* 17 Creation Time: 2019 - Mar - 02 18 Created by: (ehsan) 19 Maintainers: 20 1. Ehsan N. Moosa (E2) 21 Auditor: Ehsan N. Moosa (E2) 22 Copyright Ronak Software Group 2020 23 */ 24 25 // ronyLogger is a wrapper around zap.Logger and adds a good few features to it. 26 // It provides layered logs which could be used by separate packages, and could be turned off or on 27 // separately. Separate layers could also have independent log levels. 28 // Whenever you change log level it propagates through its children. 29 type ronyLogger struct { 30 prefix string 31 skipCaller int 32 encoder zapcore.Encoder 33 z *zap.Logger 34 sz *zap.SugaredLogger 35 lvl zap.AtomicLevel 36 } 37 38 func New(opts ...Option) *ronyLogger { 39 cfg := defaultConfig 40 for _, opt := range opts { 41 opt(&cfg) 42 } 43 44 l := &ronyLogger{ 45 lvl: zap.NewAtomicLevelAt(cfg.level), 46 skipCaller: cfg.skipCaller, 47 } 48 49 l.encoder = EncoderBuilder(). 50 WithTimeKey("ts"). 51 WithLevelKey("level"). 52 WithNameKey("name"). 53 WithCallerKey("caller"). 54 WithMessageKey("msg"). 55 ConsoleEncoder() 56 57 cores := append([]zapcore.Core{}, 58 zapcore.NewCore(l.encoder, zapcore.Lock(os.Stdout), l.lvl), 59 ) 60 61 if cfg.syslogTag != "" { 62 syslogCore, err := NewSyslogCore(l.lvl, l.encoder, cfg.syslogTag) 63 if err != nil { 64 fmt.Println("got error on enabling syslog:", err) 65 } else { 66 cores = append(cores, syslogCore) 67 } 68 } 69 70 if cfg.sentryDSN != "" { 71 sentryCore := NewSentryCore(cfg.sentryDSN, cfg.release, cfg.environment, cfg.sentryLevel, nil) 72 if sentryCore != nil { 73 cores = append(cores, sentryCore) 74 } 75 } 76 77 l.z = zap.New( 78 zapcore.NewTee(cores...), 79 zap.AddCaller(), 80 zap.AddStacktrace(ErrorLevel), 81 zap.AddCallerSkip(cfg.skipCaller), 82 ) 83 84 l.sz = l.z.Sugar() 85 86 return l 87 } 88 89 func newNOP() *ronyLogger { 90 l := &ronyLogger{} 91 l.z = zap.NewNop() 92 l.sz = l.z.Sugar() 93 94 return l 95 } 96 97 var ( 98 once sync.Once 99 ) 100 101 // ProvideDI is protected by sync.Once and provides the Logger interface for other packages. 102 func ProvideDI(opts ...Option) { 103 once.Do(func() { 104 di.MustProvide( 105 func() Logger { 106 return New(opts...) 107 }, 108 ) 109 }) 110 } 111 112 func (l *ronyLogger) Sugared() *sugaredRonyLogger { 113 return &sugaredRonyLogger{ 114 l: l, 115 } 116 } 117 118 func (l *ronyLogger) Sync() error { 119 return l.z.Sync() 120 } 121 122 func (l *ronyLogger) SetLevel(lvl Level) { 123 l.lvl.SetLevel(lvl) 124 } 125 126 func (l *ronyLogger) With(name string) Logger { 127 return l.WithSkip(name, l.skipCaller) 128 } 129 130 func (l *ronyLogger) WithSkip(name string, skipCaller int) Logger { 131 return l.with(l.z.Core(), name, skipCaller) 132 } 133 134 func (l *ronyLogger) WithCore(enc Encoder, w io.Writer) Logger { 135 core := zapcore.NewTee( 136 l.z.Core(), 137 zapcore.NewCore(enc, zapcore.AddSync(w), l.lvl), 138 ) 139 140 return l.with(core, "", l.skipCaller) 141 } 142 143 func (l *ronyLogger) with(core zapcore.Core, name string, skip int) Logger { 144 prefix := l.prefix 145 if name != "" { 146 prefix = fmt.Sprintf("%s[%s]", l.prefix, name) 147 } 148 childLogger := &ronyLogger{ 149 prefix: prefix, 150 skipCaller: l.skipCaller, 151 encoder: l.encoder.Clone(), 152 z: zap.New( 153 core, 154 zap.AddCaller(), 155 zap.AddStacktrace(ErrorLevel), 156 zap.AddCallerSkip(skip), 157 ), 158 sz: zap.New( 159 core, 160 zap.AddCaller(), 161 zap.AddStacktrace(ErrorLevel), 162 zap.AddCallerSkip(skip)).Sugar(), 163 lvl: l.lvl, 164 } 165 166 return childLogger 167 } 168 169 func (l *ronyLogger) addPrefix(in string) (out string) { 170 if l.prefix != "" { 171 sb := &strings.Builder{} 172 sb.WriteString(l.prefix) 173 sb.WriteRune(' ') 174 sb.WriteString(in) 175 out = sb.String() 176 177 return out 178 } 179 180 return in 181 } 182 183 func (l *ronyLogger) WarnOnErr(guideTxt string, err error, fields ...Field) { 184 if err != nil { 185 fields = append(fields, zap.Error(err)) 186 l.Warn(guideTxt, fields...) 187 } 188 } 189 190 func (l *ronyLogger) ErrorOnErr(guideTxt string, err error, fields ...Field) { 191 if err != nil { 192 fields = append(fields, zap.Error(err)) 193 l.Error(guideTxt, fields...) 194 } 195 } 196 197 func (l *ronyLogger) checkLevel(lvl Level) bool { 198 // Check the level first to reduce the cost of disabled log calls. 199 // Since Panic and higher may exit, we skip the optimization for those levels. 200 if lvl < zapcore.DPanicLevel && !l.z.Core().Enabled(lvl) { 201 return false 202 } 203 204 return true 205 } 206 207 func (l *ronyLogger) Check(lvl Level, msg string) *CheckedEntry { 208 if !l.checkLevel(lvl) { 209 return nil 210 } 211 212 return l.z.Check(lvl, l.addPrefix(msg)) 213 } 214 215 func (l *ronyLogger) Debug(msg string, fields ...Field) { 216 if !l.checkLevel(DebugLevel) { 217 return 218 } 219 if ce := l.z.Check(DebugLevel, l.addPrefix(msg)); ce != nil { 220 ce.Write(fields...) 221 } 222 } 223 224 func (l *ronyLogger) Info(msg string, fields ...Field) { 225 if !l.checkLevel(InfoLevel) { 226 return 227 } 228 if ce := l.z.Check(InfoLevel, l.addPrefix(msg)); ce != nil { 229 ce.Write(fields...) 230 } 231 } 232 233 func (l *ronyLogger) Warn(msg string, fields ...Field) { 234 if !l.checkLevel(WarnLevel) { 235 return 236 } 237 if ce := l.z.Check(WarnLevel, l.addPrefix(msg)); ce != nil { 238 ce.Write(fields...) 239 } 240 } 241 242 func (l *ronyLogger) Error(msg string, fields ...Field) { 243 if !l.checkLevel(ErrorLevel) { 244 return 245 } 246 if ce := l.z.Check(ErrorLevel, l.addPrefix(msg)); ce != nil { 247 ce.Write(fields...) 248 } 249 } 250 251 func (l *ronyLogger) Fatal(msg string, fields ...Field) { 252 l.z.Fatal(l.addPrefix(msg), fields...) 253 } 254 255 func (l *ronyLogger) RecoverPanic(funcName string, extraInfo interface{}, compensationFunc func()) { 256 if r := recover(); r != nil { 257 l.Error("Panic Recovered", 258 zap.String("Func", funcName), 259 zap.Any("Info", extraInfo), 260 zap.Any("Recover", r), 261 zap.ByteString("StackTrace", debug.Stack()), 262 ) 263 if compensationFunc != nil { 264 go compensationFunc() 265 } 266 } 267 } 268 269 type sugaredRonyLogger struct { 270 l *ronyLogger 271 } 272 273 func (l sugaredRonyLogger) Debugf(template string, args ...interface{}) { 274 l.l.sz.Debugf(l.l.addPrefix(template), args...) 275 } 276 277 func (l sugaredRonyLogger) Infof(template string, args ...interface{}) { 278 l.l.sz.Infof(l.l.addPrefix(template), args...) 279 } 280 281 func (l sugaredRonyLogger) Printf(template string, args ...interface{}) { 282 fmt.Printf(template, args...) 283 } 284 285 func (l sugaredRonyLogger) Warnf(template string, args ...interface{}) { 286 l.l.sz.Warnf(l.l.addPrefix(template), args...) 287 } 288 289 func (l sugaredRonyLogger) Errorf(template string, args ...interface{}) { 290 l.l.sz.Errorf(l.l.addPrefix(template), args...) 291 } 292 293 func (l sugaredRonyLogger) Fatalf(template string, args ...interface{}) { 294 l.l.sz.Fatalf(l.l.addPrefix(template), args...) 295 } 296 297 func (l sugaredRonyLogger) Debug(args ...interface{}) { 298 l.l.sz.Debug(args...) 299 } 300 301 func (l sugaredRonyLogger) Info(args ...interface{}) { 302 l.l.sz.Info(args...) 303 } 304 305 func (l sugaredRonyLogger) Warn(args ...interface{}) { 306 l.l.sz.Warn(args...) 307 } 308 309 func (l sugaredRonyLogger) Error(args ...interface{}) { 310 l.l.sz.Error(args...) 311 } 312 313 func (l sugaredRonyLogger) Fatal(args ...interface{}) { 314 l.l.sz.Fatal(args...) 315 } 316 317 func (l sugaredRonyLogger) Panic(args ...interface{}) { 318 l.l.sz.Panic(args...) 319 }