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  }