...

Source file src/github.com/launchdarkly/go-sdk-common/v3/ldlog/logging.go

Documentation: github.com/launchdarkly/go-sdk-common/v3/ldlog

     1  package ldlog
     2  
     3  import (
     4  	"log"
     5  	"os"
     6  	"strings"
     7  )
     8  
     9  // BaseLogger is a generic logger interface with no level mechanism. Since its methods are a
    10  // subset of Go's [log.Logger], you may use [log.New] to create a BaseLogger.
    11  //
    12  // This is identical to the Logger interface in the main SDK package. It is redefined here so
    13  // the ldlog package does not have to refer back to the main package.
    14  type BaseLogger interface {
    15  	// Logs a message on a single line. This is equivalent to log.Logger.Println.
    16  	Println(values ...interface{})
    17  	// Logs a message on a single line, applying a format string. This is equivalent to log.Logger.Printf.
    18  	Printf(format string, values ...interface{})
    19  }
    20  
    21  // LogLevel describes one of the possible thresholds of log message, from LogDebug to LogError.
    22  type LogLevel int
    23  
    24  // Name returns a descriptive name for this log level.
    25  func (level LogLevel) Name() string {
    26  	switch level {
    27  	case Debug:
    28  		return "Debug"
    29  	case Info:
    30  		return "Info"
    31  	case Warn:
    32  		return "Warn"
    33  	case Error:
    34  		return "Error"
    35  	case None:
    36  		return "None"
    37  	}
    38  	return "?"
    39  }
    40  
    41  // String is the default string representation of LogLevel, which is the same as [LogLevel.Name].
    42  func (level LogLevel) String() string {
    43  	return level.Name()
    44  }
    45  
    46  const (
    47  	_ = iota
    48  	// Debug is the least significant logging level, containing verbose output you will normally
    49  	// not need to see. This level is disabled by default.
    50  	Debug LogLevel = iota
    51  	// Info is the logging level for informational messages about normal operations. This level
    52  	// is enabled by default.
    53  	Info LogLevel = iota
    54  	// Warn is the logging level for more significant messages about an uncommon condition that
    55  	// is not necessarily an error. This level is enabled by default.
    56  	Warn LogLevel = iota
    57  	// Error is the logging level for error conditions that should not happen during normal
    58  	// operation of the SDK. This level is enabled by default.
    59  	Error LogLevel = iota
    60  	// None means no messages at all should be logged.
    61  	None LogLevel = iota
    62  )
    63  
    64  // Loggers is a configurable logging component with a level filter.
    65  //
    66  // By default, Loggers sends output to standard error and enables all levels except [Debug].
    67  // You may call any of its Set methods to change this configuration.
    68  type Loggers struct {
    69  	debugLog   levelLogger
    70  	infoLog    levelLogger
    71  	warnLog    levelLogger
    72  	errorLog   levelLogger
    73  	baseLogger BaseLogger
    74  	minLevel   LogLevel
    75  	prefix     string
    76  	inited     bool
    77  }
    78  
    79  type levelLogger struct {
    80  	baseLogger     BaseLogger
    81  	enabled        bool
    82  	prefix         string
    83  	overrideLogger bool
    84  }
    85  
    86  var nullLog = levelLogger{enabled: false} //nolint:gochecknoglobals
    87  
    88  // NewDisabledLoggers returns a Loggers instance that will never generate output.
    89  func NewDisabledLoggers() Loggers {
    90  	ret := Loggers{}
    91  	ret.SetBaseLogger(nullLog)
    92  	ret.SetMinLevel(None)
    93  	return ret
    94  }
    95  
    96  // Debug logs a message at Debug level, if that level is enabled. It calls the BaseLogger's Println.
    97  func (l Loggers) Debug(values ...interface{}) {
    98  	l.ForLevel(Debug).Println(values...)
    99  }
   100  
   101  // Debugf logs a message at Debug level with a format string, if that level is enabled. It calls the
   102  // BaseLogger's Printf.
   103  func (l Loggers) Debugf(format string, values ...interface{}) {
   104  	l.ForLevel(Debug).Printf(format, values...)
   105  }
   106  
   107  // Info logs a message at Info level, if that level is enabled. It calls the BaseLogger's Println.
   108  func (l Loggers) Info(values ...interface{}) {
   109  	l.ForLevel(Info).Println(values...)
   110  }
   111  
   112  // Infof logs a message at Info level with a format string, if that level is enabled. It calls the
   113  // BaseLogger's Printf.
   114  func (l Loggers) Infof(format string, values ...interface{}) {
   115  	l.ForLevel(Info).Printf(format, values...)
   116  }
   117  
   118  // Warn logs a message at Warn level, if that level is enabled. It calls the BaseLogger's Println.
   119  func (l Loggers) Warn(values ...interface{}) {
   120  	l.ForLevel(Warn).Println(values...)
   121  }
   122  
   123  // Warnf logs a message at Warn level with a format string, if that level is enabled. It calls the
   124  // BaseLogger's Printf.
   125  func (l Loggers) Warnf(format string, values ...interface{}) {
   126  	l.ForLevel(Warn).Printf(format, values...)
   127  }
   128  
   129  // Error logs a message at Error level, if that level is enabled. It calls the BaseLogger's Println.
   130  func (l Loggers) Error(values ...interface{}) {
   131  	l.ForLevel(Error).Println(values...)
   132  }
   133  
   134  // Errorf logs a message at Error level with a format string, if that level is enabled. It calls the
   135  // BaseLogger's Printf.
   136  func (l Loggers) Errorf(format string, values ...interface{}) {
   137  	l.ForLevel(Error).Printf(format, values...)
   138  }
   139  
   140  // NewDefaultLoggers returns a new Loggers instance with default properties.
   141  //
   142  // This is different from an empty Loggers{} instance in that the latter will not produce any output
   143  // until you call either [Loggers.SetBaseLogger], [Loggers.SetMinLevel], or [Loggers.Init] on it.
   144  // Calling NewDefaultLoggers() ensures that the default minimum level of [Info] is enabled.
   145  func NewDefaultLoggers() Loggers {
   146  	ret := Loggers{}
   147  	ret.SetMinLevel(Info)
   148  	return ret
   149  }
   150  
   151  // Init ensures that the Loggers instance is ready to use.
   152  //
   153  // This is necessary only if you have a Loggers instance that was not produced by [NewDefaultLoggers]
   154  // and that may not have had [Loggers.SetBaseLogger] or [Loggers.SetMinLevel] called on it. It ensures
   155  // that the default properties have been set. If you have already set any properties, Init does nothing.
   156  func (l *Loggers) Init() {
   157  	l.ensureInited()
   158  }
   159  
   160  // SetBaseLogger specifies the default destination for output at all log levels. This does not apply
   161  // to any levels whose BaseLogger has been overridden with [Loggers.SetBaseLoggerForLevel]. All messages
   162  // written to this logger will be prefixed with "NAME: " where NAME is DEBUG, INFO, etc.
   163  //
   164  // If baseLogger is nil, nothing is changed.
   165  func (l *Loggers) SetBaseLogger(baseLogger BaseLogger) {
   166  	l.ensureInited()
   167  	if baseLogger == nil {
   168  		return
   169  	}
   170  	l.baseLogger = baseLogger
   171  	for _, levelLogger := range l.allLevels() {
   172  		if !levelLogger.overrideLogger {
   173  			levelLogger.baseLogger = baseLogger
   174  		}
   175  	}
   176  }
   177  
   178  // SetBaseLoggerForLevel specifies the default destination for output at the given log level. All
   179  // messages written to this logger will be prefixed with "NAME: " where NAME is DEBUG, INFO, etc.
   180  //
   181  // If baseLogger is nil, this level will use the default from [Loggers.SetBaseLogger].
   182  func (l *Loggers) SetBaseLoggerForLevel(level LogLevel, baseLogger BaseLogger) {
   183  	l.ensureInited()
   184  	levelLogger := l.levelLogger(level)
   185  	if levelLogger != nil {
   186  		if baseLogger == nil {
   187  			levelLogger.baseLogger = l.baseLogger
   188  			levelLogger.overrideLogger = false
   189  		} else {
   190  			levelLogger.baseLogger = baseLogger
   191  			levelLogger.overrideLogger = true
   192  		}
   193  	}
   194  }
   195  
   196  // ForLevel returns a BaseLogger that writes messages at the specified level. Use this if you have
   197  // code that already uses the Printf/Println methods. All of the existing level configuration still
   198  // applies, so, for instance, loggers.ForLevel(Debug).Println("x") is exactly the same as
   199  // loggers.Debug("x").
   200  //
   201  // If the level is not a valid log level, the return value is non-nil but will produce no output.
   202  func (l Loggers) ForLevel(level LogLevel) BaseLogger {
   203  	if level >= l.minLevel {
   204  		lll := l.levelLogger(level)
   205  		if lll != nil {
   206  			return *lll
   207  		}
   208  	}
   209  	return nullLog
   210  }
   211  
   212  // SetMinLevel specifies the minimum level for log output, where [Debug] is the lowest and [Error]
   213  // is the highest. Log messages at a level lower than this will be suppressed. The default is
   214  // [Info].
   215  func (l *Loggers) SetMinLevel(minLevel LogLevel) {
   216  	l.ensureInited()
   217  	l.minLevel = minLevel
   218  	l.configureLevels()
   219  }
   220  
   221  // GetMinLevel returns the minimum level that has been specified for log output. The default is [Info].
   222  func (l Loggers) GetMinLevel() LogLevel {
   223  	if l.minLevel == 0 {
   224  		return Info // this instance hasn't been initialized, use the default
   225  	}
   226  	return l.minLevel
   227  }
   228  
   229  // IsDebugEnabled returns true if the minimum log level is [Debug], or false if it is higher.
   230  //
   231  // This allows for greater efficiency in code that can produce verbose debug output. When the Debug
   232  // level is disabled, calling [Loggers.Debug] or [Loggers.Debugf] does not produce any output but they
   233  // can still cause unwanted overhead due to having to convert their parameters to interface{} values. To
   234  // avoid that overhead, you can choose to not bother calling Debug or Debugf at all if IsDebugEnabled
   235  // returns false.
   236  func (l Loggers) IsDebugEnabled() bool {
   237  	return l.GetMinLevel() == Debug
   238  }
   239  
   240  // SetPrefix specifies a string to be added before every log message, after the LEVEL: prefix.
   241  // Do not include a trailing space.
   242  func (l *Loggers) SetPrefix(prefix string) {
   243  	l.ensureInited()
   244  	l.prefix = prefix
   245  	l.configureLevels()
   246  }
   247  
   248  func (l *Loggers) ensureInited() {
   249  	if l.inited {
   250  		return
   251  	}
   252  	l.minLevel = Info
   253  	l.baseLogger = log.New(os.Stderr, "[LaunchDarkly] ", log.LstdFlags)
   254  	for _, levelLogger := range l.allLevels() {
   255  		levelLogger.baseLogger = l.baseLogger
   256  	}
   257  	l.configureLevels()
   258  	l.inited = true
   259  }
   260  
   261  func (l *Loggers) configureLevels() {
   262  	for level, levelLogger := range l.allLevels() {
   263  		levelLogger.enabled = level >= l.minLevel
   264  		levelLogger.prefix = strings.ToUpper(level.Name()) + ":"
   265  		if l.prefix != "" {
   266  			levelLogger.prefix = levelLogger.prefix + " " + l.prefix
   267  		}
   268  	}
   269  }
   270  
   271  func (l *Loggers) allLevels() map[LogLevel]*levelLogger {
   272  	return map[LogLevel]*levelLogger{
   273  		Debug: &l.debugLog,
   274  		Info:  &l.infoLog,
   275  		Warn:  &l.warnLog,
   276  		Error: &l.errorLog,
   277  	}
   278  }
   279  
   280  func (l *Loggers) levelLogger(level LogLevel) *levelLogger {
   281  	switch level {
   282  	case Debug:
   283  		return &l.debugLog
   284  	case Info:
   285  		return &l.infoLog
   286  	case Warn:
   287  		return &l.warnLog
   288  	case Error:
   289  		return &l.errorLog
   290  	}
   291  	return nil
   292  }
   293  
   294  func (ll levelLogger) Println(values ...interface{}) {
   295  	if ll.enabled && ll.baseLogger != nil {
   296  		if len(values) == 1 {
   297  			ll.baseLogger.Println(ll.prefix, values[0])
   298  		} else {
   299  			vs := make([]interface{}, len(values)+1)
   300  			vs[0] = ll.prefix
   301  			for i := range values {
   302  				vs[i+1] = values[i]
   303  			}
   304  			ll.baseLogger.Println(vs...)
   305  		}
   306  	}
   307  }
   308  
   309  func (ll levelLogger) Printf(format string, args ...interface{}) {
   310  	if ll.enabled && ll.baseLogger != nil {
   311  		ll.baseLogger.Printf(ll.prefix+" "+format, args...)
   312  	}
   313  }
   314  

View as plain text