github.com/zaquestion/lab@v0.25.1/internal/logger/logger.go (about)

     1  package logger
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"log"
     7  	"os"
     8  	"runtime"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  )
    13  
    14  // Logger levels available
    15  const (
    16  	LogLevelNone = iota
    17  	LogLevelError
    18  	LogLevelInfo
    19  	LogLevelDebug
    20  )
    21  
    22  // Logger represents lab's internal logger structure, which has a different
    23  // log.Logger for each level, allowing different destination (file or fd) in
    24  // different levels and also different prefixes.
    25  type Logger struct {
    26  	level       int
    27  	errorLogger *log.Logger
    28  	warnLogger  *log.Logger
    29  	infoLogger  *log.Logger
    30  	debugLogger *log.Logger
    31  }
    32  
    33  // Internal instance that is used by anyone getting it through GetInstance()
    34  var internalLogger *Logger
    35  
    36  // A way to avoid multiple initialization of the same logger
    37  var once sync.Once
    38  
    39  // GetInstance returns the default lab internal logger
    40  func GetInstance() *Logger {
    41  	once.Do(func() {
    42  		internalLogger = &Logger{
    43  			// Set INFO as default level. The user can change it
    44  			level: LogLevelInfo,
    45  			// Setting Lmsgprefix preffix make the prefix to be printed before
    46  			// the actual message, but after the LstdFlags (date and time)
    47  			errorLogger: log.New(os.Stderr, "ERROR: ", log.LstdFlags|log.Lmsgprefix),
    48  			warnLogger:  log.New(os.Stderr, "WARNING: ", log.LstdFlags|log.Lmsgprefix),
    49  			infoLogger:  log.New(os.Stderr, "", log.LstdFlags|log.Lmsgprefix),
    50  			debugLogger: log.New(os.Stderr, "DEBUG: ", log.LstdFlags|log.Lmsgprefix),
    51  		}
    52  	})
    53  	return internalLogger
    54  }
    55  
    56  // SetLogLevel set the level of the internal logger.
    57  // Allowed values are LogLevel{Error,Info,Debug,None}.
    58  func (l *Logger) SetLogLevel(level int) error {
    59  	if !(level >= LogLevelNone && level <= LogLevelDebug) {
    60  		return errors.New("invalid log level")
    61  	}
    62  	l.level = level
    63  	return nil
    64  }
    65  
    66  // LogLevel return de current log level of the internal logger
    67  func (l *Logger) LogLevel() int {
    68  	return l.level
    69  }
    70  
    71  // SetStdDest sets what's the desired output stream for the internal log.
    72  // It can be any io.Writer value.
    73  func (l *Logger) SetStdDest(dest io.Writer) {
    74  	l.errorLogger.SetOutput(dest)
    75  	l.warnLogger.SetOutput(dest)
    76  	l.infoLogger.SetOutput(dest)
    77  	l.debugLogger.SetOutput(dest)
    78  }
    79  
    80  // printKeysAndValues prints the keys and valus, as pairs, passed to those
    81  // functions in the way expected by go-retryablehttp LeveledLogger interface
    82  func printKeysAndValues(l *log.Logger, keysAndValues ...interface{}) {
    83  	for i := 0; i < len(keysAndValues)/2; i += 2 {
    84  		l.Printf("\t%s = %s\n", keysAndValues[i], keysAndValues[i+1])
    85  	}
    86  }
    87  
    88  // addFileLinePrefix prepend the file name and line number to the message being
    89  // printed.
    90  func addFileLinePrefix(msg string) string {
    91  	var file string
    92  
    93  	// Using runtime.Caller() with calldepth == 2 is enough for getting the
    94  	// logger function callers
    95  	_, filePath, line, ok := runtime.Caller(2)
    96  	if ok {
    97  		fileParts := strings.Split(filePath, "/")
    98  		file = fileParts[len(fileParts)-1]
    99  	} else {
   100  		// Not sure if there's a better name or line number for an unknown caller
   101  		file = "???"
   102  		line = 0
   103  	}
   104  
   105  	prefix := []string{file, ":", strconv.Itoa(line), ":"}
   106  	// When called from Error, Warn, Info or Debug(), the Print() used
   107  	// doesn't know about this additional prefix we're adding, so we
   108  	// need to add the space between it and the msg ourselves.
   109  	if len(strings.TrimSpace(msg)) > 0 {
   110  		prefix = append(prefix, " ")
   111  	}
   112  
   113  	prefixedMsg := append(prefix, msg)
   114  	return strings.Join(prefixedMsg, "")
   115  }
   116  
   117  // Fatal prints the values and exit the program with os.Exit()
   118  func (l *Logger) Fatal(values ...interface{}) {
   119  	values = append([]interface{}{addFileLinePrefix(" ")}, values...)
   120  	l.errorLogger.Fatal(values...)
   121  }
   122  
   123  // Fatalf prints formated strings and exit the program with os.Exit()
   124  func (l *Logger) Fatalf(format string, values ...interface{}) {
   125  	values = append([]interface{}{addFileLinePrefix("")}, values...)
   126  	l.errorLogger.Fatalf("%s "+format, values...)
   127  }
   128  
   129  // Fatalln prints the values in a new line and exit the program with os.Exit()
   130  func (l *Logger) Fatalln(values ...interface{}) {
   131  	values = append([]interface{}{addFileLinePrefix(" ")}, values...)
   132  	l.errorLogger.Fatalln(values...)
   133  }
   134  
   135  // Error prints error messages (prefixed with "ERROR:").
   136  // These parameters match the retryablehttp.LeveledLogger.
   137  // Error message are always printed, regardless the log level.
   138  func (l *Logger) Error(msg string, keysAndValues ...interface{}) {
   139  	if l.level >= LogLevelError {
   140  		l.errorLogger.Print(addFileLinePrefix(msg))
   141  		printKeysAndValues(l.errorLogger, keysAndValues...)
   142  	}
   143  }
   144  
   145  // Errorf prints formated error message (prefixed with "ERROR:").
   146  // Error message are always printed, regardless the log level.
   147  func (l *Logger) Errorf(format string, values ...interface{}) {
   148  	if l.level >= LogLevelError {
   149  		values = append([]interface{}{addFileLinePrefix("")}, values...)
   150  		l.errorLogger.Printf("%s "+format, values...)
   151  	}
   152  }
   153  
   154  // Errorln prints error values in a new line (prefixed with "ERROR:").
   155  // Error message are always printed, regardless the log level.
   156  func (l *Logger) Errorln(values ...interface{}) {
   157  	if l.level >= LogLevelError {
   158  		values = append([]interface{}{addFileLinePrefix("")}, values...)
   159  		l.errorLogger.Println(values...)
   160  	}
   161  }
   162  
   163  // Warn prints warning messages (prefixed with "WARNING:").
   164  // These parameters match the retryablehttp.LeveledLogger.
   165  // Warning messages require at least LogLevelInfo level.
   166  func (l *Logger) Warn(msg string, keysAndValues ...interface{}) {
   167  	if l.level >= LogLevelInfo {
   168  		l.warnLogger.Print(addFileLinePrefix(msg))
   169  		printKeysAndValues(l.warnLogger, keysAndValues...)
   170  	}
   171  }
   172  
   173  // Warnf prints formated warning message (prefixed with "WARNING:").
   174  // Warning messages require at least LogLevelInfo level.
   175  func (l *Logger) Warnf(format string, values ...interface{}) {
   176  	if l.level >= LogLevelInfo {
   177  		values = append([]interface{}{addFileLinePrefix("")}, values...)
   178  		l.warnLogger.Printf("%s "+format, values...)
   179  	}
   180  }
   181  
   182  // Warnln prints warning values in a new line (prefixed with "WARNING:").
   183  // Warning messages require at least LogLevelInfo level.
   184  func (l *Logger) Warnln(values ...interface{}) {
   185  	if l.level >= LogLevelInfo {
   186  		values = append([]interface{}{addFileLinePrefix("")}, values...)
   187  		l.warnLogger.Println(values...)
   188  	}
   189  }
   190  
   191  // Info prints informational messages (prefixed with "INFO:").
   192  // These parameters match the retryablehttp.LeveledLogger.
   193  // Info messages require at least LogLevelInfo level.
   194  func (l *Logger) Info(msg string, keysAndValues ...interface{}) {
   195  	if l.level >= LogLevelInfo {
   196  		l.infoLogger.Print(addFileLinePrefix(msg))
   197  		printKeysAndValues(l.infoLogger, keysAndValues...)
   198  	}
   199  }
   200  
   201  // Infof prints formated informational message (prefixed with "INFO:").
   202  // Info messages require at least LogLevelInfo level.
   203  func (l *Logger) Infof(format string, values ...interface{}) {
   204  	if l.level >= LogLevelInfo {
   205  		values = append([]interface{}{addFileLinePrefix("")}, values...)
   206  		l.infoLogger.Printf("%s "+format, values...)
   207  	}
   208  }
   209  
   210  // Infoln prints info values in a new line (prefixed with "INFO:").
   211  // Info messages require at least LogLevelInfo level.
   212  func (l *Logger) Infoln(values ...interface{}) {
   213  	if l.level >= LogLevelInfo {
   214  		values = append([]interface{}{addFileLinePrefix("")}, values...)
   215  		l.infoLogger.Println(values...)
   216  	}
   217  }
   218  
   219  // Debug prints warning messages (prefixed with "DEBUG:").
   220  // These parameters match the retryablehttp.LeveledLogger.
   221  // Debug messages require at least LogLevelDebug level.
   222  func (l *Logger) Debug(msg string, keysAndValues ...interface{}) {
   223  	if l.level >= LogLevelDebug {
   224  		l.debugLogger.Print(addFileLinePrefix(msg))
   225  		printKeysAndValues(l.debugLogger, keysAndValues...)
   226  	}
   227  }
   228  
   229  // Debugf prints formated debug message (prefixed with "DEBUG:").
   230  // Debug messages require at least LogLevelDebug level.
   231  func (l *Logger) Debugf(format string, values ...interface{}) {
   232  	if l.level >= LogLevelDebug {
   233  		values = append([]interface{}{addFileLinePrefix("")}, values...)
   234  		l.debugLogger.Printf("%s "+format, values...)
   235  	}
   236  }
   237  
   238  // Debugln prints debug values in a new line (prefixed with "DEBUG:").
   239  // Debug messages require at least LogLevelDebug level.
   240  func (l *Logger) Debugln(values ...interface{}) {
   241  	if l.level >= LogLevelDebug {
   242  		values = append([]interface{}{addFileLinePrefix("")}, values...)
   243  		l.debugLogger.Println(values...)
   244  	}
   245  }