github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/log/log.go (about)

     1  // Copyright 2023 The GitBundle Inc. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // Use of this source code is governed by a MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  // Copyright 2014 The Gogs Authors. All rights reserved.
     7  // Use of this source code is governed by a MIT-style
     8  // license that can be found in the LICENSE file.
     9  
    10  package log
    11  
    12  import (
    13  	"fmt"
    14  	"os"
    15  	"runtime"
    16  	"strings"
    17  	"sync"
    18  )
    19  
    20  type loggerMap struct {
    21  	sync.Map
    22  }
    23  
    24  func (m *loggerMap) Load(k string) (*MultiChannelledLogger, bool) {
    25  	v, ok := m.Map.Load(k)
    26  	if !ok {
    27  		return nil, false
    28  	}
    29  	l, ok := v.(*MultiChannelledLogger)
    30  	return l, ok
    31  }
    32  
    33  func (m *loggerMap) Store(k string, v *MultiChannelledLogger) {
    34  	m.Map.Store(k, v)
    35  }
    36  
    37  func (m *loggerMap) Delete(k string) {
    38  	m.Map.Delete(k)
    39  }
    40  
    41  var (
    42  	// DEFAULT is the name of the default logger
    43  	DEFAULT = "default"
    44  	// NamedLoggers map of named loggers
    45  	NamedLoggers loggerMap
    46  	prefix       string
    47  )
    48  
    49  // NewLogger create a logger for the default logger
    50  func NewLogger(bufLen int64, name, provider, config string, noCaller bool) *MultiChannelledLogger {
    51  	err := NewNamedLogger(DEFAULT, bufLen, name, provider, config, noCaller)
    52  	if err != nil {
    53  		CriticalWithSkip(1, "Unable to create default logger: %v", err)
    54  		panic(err)
    55  	}
    56  	l, _ := NamedLoggers.Load(DEFAULT)
    57  	return l
    58  }
    59  
    60  // NewNamedLogger creates a new named logger for a given configuration
    61  func NewNamedLogger(name string, bufLen int64, subname, provider, config string, noCaller bool) error {
    62  	logger, ok := NamedLoggers.Load(name)
    63  	if !ok {
    64  		logger = newLogger(name, bufLen, noCaller)
    65  		NamedLoggers.Store(name, logger)
    66  	}
    67  
    68  	return logger.SetLogger(subname, provider, config)
    69  }
    70  
    71  // DelNamedLogger closes and deletes the named logger
    72  func DelNamedLogger(name string) {
    73  	l, ok := NamedLoggers.Load(name)
    74  	if ok {
    75  		NamedLoggers.Delete(name)
    76  		l.Close()
    77  	}
    78  }
    79  
    80  // DelLogger removes the named sublogger from the default logger
    81  func DelLogger(name string) error {
    82  	logger, _ := NamedLoggers.Load(DEFAULT)
    83  	found, err := logger.DelLogger(name)
    84  	if !found {
    85  		Trace("Log %s not found, no need to delete", name)
    86  	}
    87  	return err
    88  }
    89  
    90  // GetLogger returns either a named logger or the default logger
    91  func GetLogger(name string) *MultiChannelledLogger {
    92  	logger, ok := NamedLoggers.Load(name)
    93  	if ok {
    94  		return logger
    95  	}
    96  	logger, _ = NamedLoggers.Load(DEFAULT)
    97  	return logger
    98  }
    99  
   100  // GetLevel returns the minimum logger level
   101  func GetLevel() Level {
   102  	l, _ := NamedLoggers.Load(DEFAULT)
   103  	return l.GetLevel()
   104  }
   105  
   106  // GetStacktraceLevel returns the minimum logger level
   107  func GetStacktraceLevel() Level {
   108  	l, _ := NamedLoggers.Load(DEFAULT)
   109  	return l.GetStacktraceLevel()
   110  }
   111  
   112  // Trace records trace log
   113  func Trace(format string, v ...interface{}) {
   114  	Log(1, TRACE, format, v...)
   115  }
   116  
   117  // IsTrace returns true if at least one logger is TRACE
   118  func IsTrace() bool {
   119  	return GetLevel() <= TRACE
   120  }
   121  
   122  // Debug records debug log
   123  func Debug(format string, v ...interface{}) {
   124  	Log(1, DEBUG, format, v...)
   125  }
   126  
   127  // IsDebug returns true if at least one logger is DEBUG
   128  func IsDebug() bool {
   129  	return GetLevel() <= DEBUG
   130  }
   131  
   132  // Info records info log
   133  func Info(format string, v ...interface{}) {
   134  	Log(1, INFO, format, v...)
   135  }
   136  
   137  // IsInfo returns true if at least one logger is INFO
   138  func IsInfo() bool {
   139  	return GetLevel() <= INFO
   140  }
   141  
   142  // Warn records warning log
   143  func Warn(format string, v ...interface{}) {
   144  	Log(1, WARN, format, v...)
   145  }
   146  
   147  // IsWarn returns true if at least one logger is WARN
   148  func IsWarn() bool {
   149  	return GetLevel() <= WARN
   150  }
   151  
   152  // Error records error log
   153  func Error(format string, v ...interface{}) {
   154  	Log(1, ERROR, format, v...)
   155  }
   156  
   157  // ErrorWithSkip records error log from "skip" calls back from this function
   158  func ErrorWithSkip(skip int, format string, v ...interface{}) {
   159  	Log(skip+1, ERROR, format, v...)
   160  }
   161  
   162  // IsError returns true if at least one logger is ERROR
   163  func IsError() bool {
   164  	return GetLevel() <= ERROR
   165  }
   166  
   167  // Critical records critical log
   168  func Critical(format string, v ...interface{}) {
   169  	Log(1, CRITICAL, format, v...)
   170  }
   171  
   172  // CriticalWithSkip records critical log from "skip" calls back from this function
   173  func CriticalWithSkip(skip int, format string, v ...interface{}) {
   174  	Log(skip+1, CRITICAL, format, v...)
   175  }
   176  
   177  // IsCritical returns true if at least one logger is CRITICAL
   178  func IsCritical() bool {
   179  	return GetLevel() <= CRITICAL
   180  }
   181  
   182  // Fatal records fatal log and exit process
   183  func Fatal(format string, v ...interface{}) {
   184  	Log(1, FATAL, format, v...)
   185  	Close()
   186  	os.Exit(1)
   187  }
   188  
   189  // FatalWithSkip records fatal log from "skip" calls back from this function
   190  func FatalWithSkip(skip int, format string, v ...interface{}) {
   191  	Log(skip+1, FATAL, format, v...)
   192  	Close()
   193  	os.Exit(1)
   194  }
   195  
   196  // IsFatal returns true if at least one logger is FATAL
   197  func IsFatal() bool {
   198  	return GetLevel() <= FATAL
   199  }
   200  
   201  // Pause pauses all the loggers
   202  func Pause() {
   203  	NamedLoggers.Range(func(key, value interface{}) bool {
   204  		logger := value.(*MultiChannelledLogger)
   205  		logger.Pause()
   206  		logger.Flush()
   207  		return true
   208  	})
   209  }
   210  
   211  // Resume resumes all the loggers
   212  func Resume() {
   213  	NamedLoggers.Range(func(key, value interface{}) bool {
   214  		logger := value.(*MultiChannelledLogger)
   215  		logger.Resume()
   216  		return true
   217  	})
   218  }
   219  
   220  // ReleaseReopen releases and reopens logging files
   221  func ReleaseReopen() error {
   222  	var accumulatedErr error
   223  	NamedLoggers.Range(func(key, value interface{}) bool {
   224  		logger := value.(*MultiChannelledLogger)
   225  		if err := logger.ReleaseReopen(); err != nil {
   226  			if accumulatedErr == nil {
   227  				accumulatedErr = fmt.Errorf("Error reopening %s: %v", key.(string), err)
   228  			} else {
   229  				accumulatedErr = fmt.Errorf("Error reopening %s: %v & %v", key.(string), err, accumulatedErr)
   230  			}
   231  		}
   232  		return true
   233  	})
   234  	return accumulatedErr
   235  }
   236  
   237  // Close closes all the loggers
   238  func Close() {
   239  	l, ok := NamedLoggers.Load(DEFAULT)
   240  	if !ok {
   241  		return
   242  	}
   243  	NamedLoggers.Delete(DEFAULT)
   244  	l.Close()
   245  }
   246  
   247  // Log a message with defined skip and at logging level
   248  // A skip of 0 refers to the caller of this command
   249  func Log(skip int, level Level, format string, v ...interface{}) {
   250  	l, ok := NamedLoggers.Load(DEFAULT)
   251  	if ok {
   252  		l.Log(skip+1, level, format, v...)
   253  	}
   254  }
   255  
   256  // LoggerAsWriter is a io.Writer shim around the gitbundle log
   257  type LoggerAsWriter struct {
   258  	ourLoggers []*MultiChannelledLogger
   259  	level      Level
   260  }
   261  
   262  // NewLoggerAsWriter creates a Writer representation of the logger with setable log level
   263  func NewLoggerAsWriter(level string, ourLoggers ...*MultiChannelledLogger) *LoggerAsWriter {
   264  	if len(ourLoggers) == 0 {
   265  		l, _ := NamedLoggers.Load(DEFAULT)
   266  		ourLoggers = []*MultiChannelledLogger{l}
   267  	}
   268  	l := &LoggerAsWriter{
   269  		ourLoggers: ourLoggers,
   270  		level:      FromString(level),
   271  	}
   272  	return l
   273  }
   274  
   275  // Write implements the io.Writer interface to allow spoofing of chi
   276  func (l *LoggerAsWriter) Write(p []byte) (int, error) {
   277  	for _, logger := range l.ourLoggers {
   278  		// Skip = 3 because this presumes that we have been called by log.Println()
   279  		// If the caller has used log.Output or the like this will be wrong
   280  		logger.Log(3, l.level, string(p))
   281  	}
   282  	return len(p), nil
   283  }
   284  
   285  // Log takes a given string and logs it at the set log-level
   286  func (l *LoggerAsWriter) Log(msg string) {
   287  	for _, logger := range l.ourLoggers {
   288  		// Set the skip to reference the call just above this
   289  		_ = logger.Log(1, l.level, msg)
   290  	}
   291  }
   292  
   293  func init() {
   294  	_, filename, _, _ := runtime.Caller(0)
   295  	prefix = strings.TrimSuffix(filename, "modules/log/log.go")
   296  	if prefix == filename {
   297  		// in case the source code file is moved, we can not trim the suffix, the code above should also be updated.
   298  		// FIXME should be checked again
   299  		// panic("unable to detect correct package prefix, please update file: " + filename)
   300  	}
   301  }