github.com/livekit/protocol@v1.16.1-0.20240517185851-47e4c6bba773/logger/logger.go (about) 1 // Copyright 2023 LiveKit, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package logger 16 17 import ( 18 "log/slog" 19 "os" 20 "strings" 21 "sync" 22 "time" 23 24 "github.com/go-logr/logr" 25 "github.com/puzpuzpuz/xsync/v3" 26 "go.uber.org/zap" 27 "go.uber.org/zap/zapcore" 28 29 "github.com/livekit/protocol/logger/zaputil" 30 ) 31 32 var ( 33 discardLogger = logr.Discard() 34 defaultLogger Logger = LogRLogger(discardLogger) 35 pkgLogger Logger = LogRLogger(discardLogger) 36 ) 37 38 // InitFromConfig initializes a Zap-based logger 39 func InitFromConfig(conf *Config, name string) { 40 l, err := NewZapLogger(conf) 41 if err == nil { 42 SetLogger(l, name) 43 slog.SetDefault(slog.New(ToSlogHandler(l))) 44 } 45 } 46 47 // GetLogger returns the logger that was set with SetLogger with an extra depth of 1 48 func GetLogger() Logger { 49 return defaultLogger 50 } 51 52 // SetLogger lets you use a custom logger. Pass in a logr.Logger with default depth 53 func SetLogger(l Logger, name string) { 54 defaultLogger = l.WithCallDepth(1).WithName(name) 55 // pkg wrapper needs to drop two levels of depth 56 pkgLogger = l.WithCallDepth(2).WithName(name) 57 } 58 59 func Debugw(msg string, keysAndValues ...interface{}) { 60 pkgLogger.Debugw(msg, keysAndValues...) 61 } 62 63 func Infow(msg string, keysAndValues ...interface{}) { 64 pkgLogger.Infow(msg, keysAndValues...) 65 } 66 67 func Warnw(msg string, err error, keysAndValues ...interface{}) { 68 pkgLogger.Warnw(msg, err, keysAndValues...) 69 } 70 71 func Errorw(msg string, err error, keysAndValues ...interface{}) { 72 pkgLogger.Errorw(msg, err, keysAndValues...) 73 } 74 75 func ParseZapLevel(level string) zapcore.Level { 76 lvl := zapcore.InfoLevel 77 if level != "" { 78 _ = lvl.UnmarshalText([]byte(level)) 79 } 80 return lvl 81 } 82 83 type DeferredFieldResolver = zaputil.DeferredFieldResolver 84 85 type Logger interface { 86 Debugw(msg string, keysAndValues ...interface{}) 87 Infow(msg string, keysAndValues ...interface{}) 88 Warnw(msg string, err error, keysAndValues ...interface{}) 89 Errorw(msg string, err error, keysAndValues ...interface{}) 90 WithValues(keysAndValues ...interface{}) Logger 91 WithName(name string) Logger 92 // WithComponent creates a new logger with name as "<name>.<component>", and uses a log level as specified 93 WithComponent(component string) Logger 94 WithCallDepth(depth int) Logger 95 WithItemSampler() Logger 96 // WithoutSampler returns the original logger without sampling 97 WithoutSampler() Logger 98 WithDeferredValues() (Logger, DeferredFieldResolver) 99 } 100 101 type sharedConfig struct { 102 level zap.AtomicLevel 103 mu sync.Mutex 104 componentLevels map[string]zap.AtomicLevel 105 config *Config 106 } 107 108 func newSharedConfig(conf *Config) *sharedConfig { 109 sc := &sharedConfig{ 110 level: zap.NewAtomicLevelAt(ParseZapLevel(conf.Level)), 111 config: conf, 112 componentLevels: make(map[string]zap.AtomicLevel), 113 } 114 conf.AddUpdateObserver(sc.onConfigUpdate) 115 _ = sc.onConfigUpdate(conf) 116 return sc 117 } 118 119 func (c *sharedConfig) onConfigUpdate(conf *Config) error { 120 // update log levels 121 c.level.SetLevel(ParseZapLevel(conf.Level)) 122 123 // we have to update alla existing component levels 124 c.mu.Lock() 125 c.config = conf 126 for component, atomicLevel := range c.componentLevels { 127 effectiveLevel := c.level.Level() 128 parts := strings.Split(component, ".") 129 confSearch: 130 for len(parts) > 0 { 131 search := strings.Join(parts, ".") 132 if compLevel, ok := conf.ComponentLevels[search]; ok { 133 effectiveLevel = ParseZapLevel(compLevel) 134 break confSearch 135 } 136 parts = parts[:len(parts)-1] 137 } 138 atomicLevel.SetLevel(effectiveLevel) 139 } 140 c.mu.Unlock() 141 return nil 142 } 143 144 // ensure we have an atomic level in the map representing the full component path 145 // this makes it possible to update the log level after the fact 146 func (c *sharedConfig) ComponentLevel(component string) zap.AtomicLevel { 147 c.mu.Lock() 148 defer c.mu.Unlock() 149 if compLevel, ok := c.componentLevels[component]; ok { 150 return compLevel 151 } 152 153 // search up the hierarchy to find the first level that is set 154 atomicLevel := zap.NewAtomicLevelAt(c.level.Level()) 155 c.componentLevels[component] = atomicLevel 156 parts := strings.Split(component, ".") 157 for len(parts) > 0 { 158 search := strings.Join(parts, ".") 159 if compLevel, ok := c.config.ComponentLevels[search]; ok { 160 atomicLevel.SetLevel(ParseZapLevel(compLevel)) 161 return atomicLevel 162 } 163 parts = parts[:len(parts)-1] 164 } 165 return atomicLevel 166 } 167 168 type zapConfig struct { 169 conf *Config 170 sc *sharedConfig 171 writeEnablers *xsync.MapOf[string, *zaputil.WriteEnabler] 172 levelEnablers *xsync.MapOf[string, *zaputil.OrLevelEnabler] 173 tap *zaputil.WriteEnabler 174 } 175 176 type ZapLoggerOption func(*zapConfig) 177 178 func WithTap(tap *zaputil.WriteEnabler) ZapLoggerOption { 179 return func(zc *zapConfig) { 180 zc.tap = tap 181 } 182 } 183 184 type ZapComponentLeveler interface { 185 ComponentLevel(component string) zapcore.LevelEnabler 186 } 187 188 type ZapLogger interface { 189 Logger 190 ToZap() *zap.SugaredLogger 191 ComponentLeveler() ZapComponentLeveler 192 WithMinLevel(lvl zapcore.LevelEnabler) Logger 193 } 194 195 type zapLogger[T zaputil.Encoder[T]] struct { 196 zap *zap.SugaredLogger 197 *zapConfig 198 enc T 199 component string 200 deferred []*zaputil.Deferrer 201 sampler *zaputil.Sampler 202 minLevel zapcore.LevelEnabler 203 } 204 205 func FromZapLogger(log *zap.Logger, conf *Config, opts ...ZapLoggerOption) (ZapLogger, error) { 206 if log == nil { 207 log = zap.New(nil).WithOptions(zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)) 208 } 209 zap := log.Sugar() 210 211 zc := &zapConfig{ 212 conf: conf, 213 sc: newSharedConfig(conf), 214 writeEnablers: xsync.NewMapOf[string, *zaputil.WriteEnabler](), 215 levelEnablers: xsync.NewMapOf[string, *zaputil.OrLevelEnabler](), 216 tap: zaputil.NewDiscardWriteEnabler(), 217 } 218 for _, opt := range opts { 219 opt(zc) 220 } 221 222 var sampler *zaputil.Sampler 223 if conf.Sample { 224 var initial = 20 225 var interval = 100 226 if conf.ItemSampleInitial != 0 { 227 initial = conf.ItemSampleInitial 228 } 229 if conf.ItemSampleInterval != 0 { 230 interval = conf.ItemSampleInterval 231 } 232 sampler = zaputil.NewSampler(time.Second, initial, interval) 233 } 234 235 if conf.JSON { 236 return newZapLogger(zap, zc, zaputil.NewProductionEncoder(), sampler), nil 237 } else { 238 return newZapLogger(zap, zc, zaputil.NewDevelopmentEncoder(), sampler), nil 239 } 240 } 241 242 func NewZapLogger(conf *Config, opts ...ZapLoggerOption) (ZapLogger, error) { 243 return FromZapLogger(nil, conf, opts...) 244 } 245 246 func newZapLogger[T zaputil.Encoder[T]](zap *zap.SugaredLogger, zc *zapConfig, enc T, sampler *zaputil.Sampler) ZapLogger { 247 l := &zapLogger[T]{ 248 zap: zap, 249 zapConfig: zc, 250 enc: enc, 251 sampler: sampler, 252 } 253 l.zap = l.ToZap() 254 return l 255 } 256 257 func (l *zapLogger[T]) ToZap() *zap.SugaredLogger { 258 var console *zaputil.WriteEnabler 259 if l.minLevel == nil { 260 console, _ = l.writeEnablers.LoadOrCompute(l.component, func() *zaputil.WriteEnabler { 261 return zaputil.NewWriteEnabler(os.Stderr, l.sc.ComponentLevel(l.component)) 262 }) 263 } else { 264 enab := zaputil.OrLevelEnabler{l.minLevel, l.sc.ComponentLevel(l.component)} 265 console = zaputil.NewWriteEnabler(os.Stderr, enab) 266 } 267 268 c := l.enc.Core(console, l.tap) 269 for i := range l.deferred { 270 c = zaputil.NewDeferredValueCore(c, l.deferred[i]) 271 } 272 if l.sampler != nil { 273 c = zaputil.NewSamplerCore(c, l.sampler) 274 } 275 276 return l.zap.WithOptions(zap.WrapCore(func(zapcore.Core) zapcore.Core { return c })) 277 } 278 279 type zapLoggerComponentLeveler[T zaputil.Encoder[T]] struct { 280 zl *zapLogger[T] 281 } 282 283 func (l zapLoggerComponentLeveler[T]) ComponentLevel(component string) zapcore.LevelEnabler { 284 if l.zl.component != "" { 285 component = l.zl.component + "." + component 286 } 287 288 enab, _ := l.zl.levelEnablers.LoadOrCompute(component, func() *zaputil.OrLevelEnabler { 289 return &zaputil.OrLevelEnabler{l.zl.sc.ComponentLevel(component), l.zl.tap} 290 }) 291 return enab 292 } 293 294 func (l *zapLogger[T]) ComponentLeveler() ZapComponentLeveler { 295 return zapLoggerComponentLeveler[T]{l} 296 } 297 298 func (l *zapLogger[T]) Debugw(msg string, keysAndValues ...interface{}) { 299 l.zap.Debugw(msg, keysAndValues...) 300 } 301 302 func (l *zapLogger[T]) WithMinLevel(lvl zapcore.LevelEnabler) Logger { 303 dup := *l 304 dup.minLevel = lvl 305 dup.zap = dup.ToZap() 306 return &dup 307 } 308 309 func (l *zapLogger[T]) Infow(msg string, keysAndValues ...interface{}) { 310 l.zap.Infow(msg, keysAndValues...) 311 } 312 313 func (l *zapLogger[T]) Warnw(msg string, err error, keysAndValues ...interface{}) { 314 if err != nil { 315 keysAndValues = append(keysAndValues, "error", err) 316 } 317 l.zap.Warnw(msg, keysAndValues...) 318 } 319 320 func (l *zapLogger[T]) Errorw(msg string, err error, keysAndValues ...interface{}) { 321 if err != nil { 322 keysAndValues = append(keysAndValues, "error", err) 323 } 324 l.zap.Errorw(msg, keysAndValues...) 325 } 326 327 func (l *zapLogger[T]) WithValues(keysAndValues ...interface{}) Logger { 328 dup := *l 329 dup.enc = dup.enc.WithValues(keysAndValues...) 330 dup.zap = dup.ToZap() 331 return &dup 332 } 333 334 func (l *zapLogger[T]) WithName(name string) Logger { 335 dup := *l 336 dup.zap = dup.zap.Named(name) 337 return &dup 338 } 339 340 func (l *zapLogger[T]) WithComponent(component string) Logger { 341 dup := *l 342 dup.zap = dup.zap.Named(component) 343 if dup.component == "" { 344 dup.component = component 345 } else { 346 dup.component = dup.component + "." + component 347 } 348 dup.zap = dup.ToZap() 349 return &dup 350 } 351 352 func (l *zapLogger[T]) WithCallDepth(depth int) Logger { 353 dup := *l 354 dup.zap = dup.zap.WithOptions(zap.AddCallerSkip(depth)) 355 return &dup 356 } 357 358 func (l *zapLogger[T]) WithItemSampler() Logger { 359 if l.conf.ItemSampleSeconds == 0 { 360 return l 361 } 362 dup := *l 363 dup.sampler = zaputil.NewSampler( 364 time.Duration(l.conf.ItemSampleSeconds)*time.Second, 365 l.conf.ItemSampleInitial, 366 l.conf.ItemSampleInterval, 367 ) 368 dup.zap = dup.ToZap() 369 return &dup 370 } 371 372 func (l *zapLogger[T]) WithoutSampler() Logger { 373 dup := *l 374 dup.sampler = nil 375 dup.zap = dup.ToZap() 376 return &dup 377 } 378 379 func (l *zapLogger[T]) WithDeferredValues() (Logger, DeferredFieldResolver) { 380 dup := *l 381 def, resolve := zaputil.NewDeferrer() 382 dup.deferred = append(dup.deferred[0:len(dup.deferred):len(dup.deferred)], def) 383 dup.zap = dup.ToZap() 384 return &dup, resolve 385 } 386 387 type LogRLogger logr.Logger 388 389 func (l LogRLogger) toLogr() logr.Logger { 390 if logr.Logger(l).GetSink() == nil { 391 return discardLogger 392 } 393 return logr.Logger(l) 394 } 395 396 func (l LogRLogger) Debugw(msg string, keysAndValues ...interface{}) { 397 l.toLogr().V(1).Info(msg, keysAndValues...) 398 } 399 400 func (l LogRLogger) Infow(msg string, keysAndValues ...interface{}) { 401 l.toLogr().Info(msg, keysAndValues...) 402 } 403 404 func (l LogRLogger) Warnw(msg string, err error, keysAndValues ...interface{}) { 405 if err != nil { 406 keysAndValues = append(keysAndValues, "error", err) 407 } 408 l.toLogr().Info(msg, keysAndValues...) 409 } 410 411 func (l LogRLogger) Errorw(msg string, err error, keysAndValues ...interface{}) { 412 l.toLogr().Error(err, msg, keysAndValues...) 413 } 414 415 func (l LogRLogger) WithValues(keysAndValues ...interface{}) Logger { 416 return LogRLogger(l.toLogr().WithValues(keysAndValues...)) 417 } 418 419 func (l LogRLogger) WithName(name string) Logger { 420 return LogRLogger(l.toLogr().WithName(name)) 421 } 422 423 func (l LogRLogger) WithComponent(component string) Logger { 424 return LogRLogger(l.toLogr().WithName(component)) 425 } 426 427 func (l LogRLogger) WithCallDepth(depth int) Logger { 428 return LogRLogger(l.toLogr().WithCallDepth(depth)) 429 } 430 431 func (l LogRLogger) WithItemSampler() Logger { 432 // logr does not support sampling 433 return l 434 } 435 436 func (l LogRLogger) WithoutSampler() Logger { 437 return l 438 } 439 440 func (l LogRLogger) WithDeferredValues() (Logger, DeferredFieldResolver) { 441 return l, func(args ...any) {} 442 }