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  }