github.com/keakon/golog@v0.0.0-20230330091222-cac71197c18d/logger.go (about)

     1  package golog
     2  
     3  import (
     4  	"io"
     5  	"sort"
     6  	"time"
     7  )
     8  
     9  // Level specifies the log level.
    10  type Level uint8
    11  
    12  // All the log levels.
    13  const (
    14  	DebugLevel Level = iota
    15  	InfoLevel
    16  	WarnLevel
    17  	ErrorLevel
    18  	CritLevel
    19  
    20  	disabledLevel Level = 255
    21  )
    22  
    23  var (
    24  	levelNames = []byte("DIWEC")
    25  
    26  	internalLogger *Logger
    27  )
    28  
    29  // A Record is an item which contains required context for the logger.
    30  type Record struct {
    31  	date    string
    32  	time    string
    33  	file    string
    34  	message string
    35  	args    []interface{}
    36  	tm      time.Time
    37  	line    int
    38  	level   Level
    39  }
    40  
    41  // A Logger is a leveled logger with several handlers.
    42  type Logger struct {
    43  	handlers   []*Handler
    44  	minLevel   Level // the min level of the logger and its handlers
    45  	level      Level // the lowest acceptable level of the logger
    46  	isInternal bool
    47  }
    48  
    49  // NewLogger creates a new Logger of the given level.
    50  // Messages with lower level than the logger will be ignored.
    51  func NewLogger(lv Level) *Logger {
    52  	return &Logger{level: lv, minLevel: disabledLevel} // disable all levels for empty logger
    53  }
    54  
    55  // AddHandler adds a Handler to the Logger.
    56  func (l *Logger) AddHandler(h *Handler) {
    57  	h.isInternal = l.isInternal
    58  	l.handlers = append(l.handlers, h)
    59  
    60  	if len(l.handlers) > 1 {
    61  		sort.Slice(l.handlers, func(i, j int) bool {
    62  			return l.handlers[i].level < l.handlers[j].level
    63  		})
    64  	}
    65  
    66  	minLevel := l.handlers[0].level
    67  	if l.level >= minLevel {
    68  		l.minLevel = l.level
    69  	} else {
    70  		l.minLevel = minLevel
    71  	}
    72  }
    73  
    74  // IsEnabledFor returns whether it's enabled for the level.
    75  func (l *Logger) IsEnabledFor(level Level) bool {
    76  	return l.minLevel <= level
    77  }
    78  
    79  // GetMinLevel returns its minLevel.
    80  // Records lower than its minLevel will be ignored.
    81  func (l *Logger) GetMinLevel() Level {
    82  	return l.minLevel
    83  }
    84  
    85  // Log logs a message with context.
    86  // A logger should check the message level before call its Log().
    87  // The line param should be uint32.
    88  // It's not thread-safe, concurrent messages may be written in a random order
    89  // through different handlers or writers.
    90  // But two messages won't be mixed in a single line.
    91  func (l *Logger) Log(lv Level, file string, line int, msg string, args ...interface{}) {
    92  	r := recordPool.Get().(*Record)
    93  	r.level = lv
    94  	if fastTimer.isRunning {
    95  		r.date = fastTimer.date
    96  		r.time = fastTimer.time
    97  	} else {
    98  		r.tm = now()
    99  	}
   100  	r.file = file
   101  	r.line = line
   102  	r.message = msg
   103  	r.args = args
   104  
   105  	for _, h := range l.handlers {
   106  		if !h.Handle(r) {
   107  			break
   108  		}
   109  	}
   110  
   111  	recordPool.Put(r)
   112  }
   113  
   114  // Close closes its handlers.
   115  // It's safe to call this method more than once.
   116  func (l *Logger) Close() {
   117  	for _, h := range l.handlers {
   118  		h.Close()
   119  	}
   120  	l.handlers = nil
   121  }
   122  
   123  // Debug logs a debug level message. It uses fmt.Fprint() to format args.
   124  func (l *Logger) Debug(args ...interface{}) {
   125  	if l.IsEnabledFor(DebugLevel) {
   126  		file, line := Caller(1) // deeper caller will be more expensive
   127  		l.Log(DebugLevel, file, line, "", args...)
   128  	}
   129  }
   130  
   131  // Debugf logs a debug level message. It uses fmt.Fprintf() to format msg and args.
   132  func (l *Logger) Debugf(msg string, args ...interface{}) {
   133  	if l.IsEnabledFor(DebugLevel) {
   134  		file, line := Caller(1)
   135  		l.Log(DebugLevel, file, line, msg, args...)
   136  	}
   137  }
   138  
   139  // Info logs a info level message. It uses fmt.Fprint() to format args.
   140  func (l *Logger) Info(args ...interface{}) {
   141  	if l.IsEnabledFor(InfoLevel) {
   142  		file, line := Caller(1)
   143  		l.Log(InfoLevel, file, line, "", args...)
   144  	}
   145  }
   146  
   147  // Infof logs a info level message. It uses fmt.Fprintf() to format msg and args.
   148  func (l *Logger) Infof(msg string, args ...interface{}) {
   149  	if l.IsEnabledFor(InfoLevel) {
   150  		file, line := Caller(1)
   151  		l.Log(InfoLevel, file, line, msg, args...)
   152  	}
   153  }
   154  
   155  // Warn logs a warning level message. It uses fmt.Fprint() to format args.
   156  func (l *Logger) Warn(args ...interface{}) {
   157  	if l.IsEnabledFor(WarnLevel) {
   158  		file, line := Caller(1)
   159  		l.Log(WarnLevel, file, line, "", args...)
   160  	}
   161  }
   162  
   163  // Warnf logs a warning level message. It uses fmt.Fprintf() to format msg and args.
   164  func (l *Logger) Warnf(msg string, args ...interface{}) {
   165  	if l.IsEnabledFor(WarnLevel) {
   166  		file, line := Caller(1)
   167  		l.Log(WarnLevel, file, line, msg, args...)
   168  	}
   169  }
   170  
   171  // Error logs an error level message. It uses fmt.Fprint() to format args.
   172  func (l *Logger) Error(args ...interface{}) {
   173  	if l.IsEnabledFor(ErrorLevel) {
   174  		file, line := Caller(1)
   175  		l.Log(ErrorLevel, file, line, "", args...)
   176  	}
   177  }
   178  
   179  // Errorf logs a error level message. It uses fmt.Fprintf() to format msg and args.
   180  func (l *Logger) Errorf(msg string, args ...interface{}) {
   181  	if l.IsEnabledFor(ErrorLevel) {
   182  		file, line := Caller(1)
   183  		l.Log(ErrorLevel, file, line, msg, args...)
   184  	}
   185  }
   186  
   187  // Crit logs a critical level message. It uses fmt.Fprint() to format args.
   188  func (l *Logger) Crit(args ...interface{}) {
   189  	if l.IsEnabledFor(CritLevel) {
   190  		file, line := Caller(1)
   191  		l.Log(CritLevel, file, line, "", args...)
   192  	}
   193  }
   194  
   195  // Critf logs a critical level message. It uses fmt.Fprintf() to format msg and args.
   196  func (l *Logger) Critf(msg string, args ...interface{}) {
   197  	if l.IsEnabledFor(CritLevel) {
   198  		file, line := Caller(1)
   199  		l.Log(CritLevel, file, line, msg, args...)
   200  	}
   201  }
   202  
   203  // NewLoggerWithWriter creates an info level logger with a writer.
   204  func NewLoggerWithWriter(w io.WriteCloser) *Logger {
   205  	h := NewHandler(InfoLevel, DefaultFormatter)
   206  	h.AddWriter(w)
   207  	l := NewLogger(InfoLevel)
   208  	l.AddHandler(h)
   209  	return l
   210  }
   211  
   212  // NewStdoutLogger creates a logger with a stdout writer.
   213  func NewStdoutLogger() *Logger {
   214  	return NewLoggerWithWriter(NewStdoutWriter())
   215  }
   216  
   217  // NewStderrLogger creates a logger with a stderr writer.
   218  func NewStderrLogger() *Logger {
   219  	return NewLoggerWithWriter(NewStderrWriter())
   220  }
   221  
   222  // SetInternalLogger sets a logger as the internalLogger which is used to log internal errors.
   223  // The logger and its handlers will be marked as internal, so do not reuse them.
   224  // The internalLogger may discard its own errors to prevent recursive log.
   225  func SetInternalLogger(l *Logger) {
   226  	if internalLogger != nil {
   227  		internalLogger.isInternal = false
   228  		for _, h := range internalLogger.handlers {
   229  			h.isInternal = false
   230  		}
   231  	}
   232  
   233  	if l != nil {
   234  		l.isInternal = true
   235  		for _, h := range l.handlers {
   236  			h.isInternal = true
   237  		}
   238  	}
   239  
   240  	internalLogger = l
   241  }