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 }