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