github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/openstack/obs/log.go (about) 1 package obs 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "path/filepath" 8 "runtime" 9 "strings" 10 "sync" 11 ) 12 13 type Level int 14 15 const ( 16 LEVEL_OFF Level = 500 17 LEVEL_ERROR Level = 400 18 LEVEL_WARN Level = 300 19 LEVEL_INFO Level = 200 20 LEVEL_DEBUG Level = 100 21 ) 22 23 var logLevelMap = map[Level]string{ 24 LEVEL_OFF: "[OFF]: ", 25 LEVEL_ERROR: "[ERROR]: ", 26 LEVEL_WARN: "[WARN]: ", 27 LEVEL_INFO: "[INFO]: ", 28 LEVEL_DEBUG: "[DEBUG]: ", 29 } 30 31 type logConfType struct { 32 level Level 33 logToConsole bool 34 logFullPath string 35 maxLogSize int64 36 backups int 37 } 38 39 func getDefaultLogConf() logConfType { 40 return logConfType{ 41 level: LEVEL_WARN, 42 logToConsole: false, 43 logFullPath: "", 44 maxLogSize: 1024 * 1024 * 30, // 30MB 45 backups: 10, 46 } 47 } 48 49 var logConf logConfType 50 51 type loggerWrapper struct { 52 fullPath string 53 fd *os.File 54 ch chan string 55 wg sync.WaitGroup 56 queue []string 57 logger *log.Logger 58 index int 59 cacheCount int 60 closed bool 61 } 62 63 func (lw *loggerWrapper) doInit() { 64 lw.queue = make([]string, 0, lw.cacheCount) 65 lw.logger = log.New(lw.fd, "", 0) 66 lw.ch = make(chan string, lw.cacheCount) 67 lw.wg.Add(1) 68 go lw.doWrite() 69 } 70 71 func (lw *loggerWrapper) rotate() { 72 stat, err := lw.fd.Stat() 73 if err != nil { 74 _ = lw.fd.Close() 75 panic(err) 76 } 77 if stat.Size() >= logConf.maxLogSize { 78 _err := lw.fd.Sync() 79 if _err != nil { 80 panic(err) 81 } 82 _ = lw.fd.Close() 83 if lw.index > logConf.backups { 84 lw.index = 1 85 } 86 _err = os.Rename(lw.fullPath, lw.fullPath+"."+IntToString(lw.index)) 87 if _err != nil { 88 panic(err) 89 } 90 lw.index += 1 91 92 fd, err := os.OpenFile(lw.fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) 93 if err != nil { 94 panic(err) 95 } 96 lw.fd = fd 97 lw.logger.SetOutput(lw.fd) 98 } 99 } 100 101 func (lw *loggerWrapper) doFlush() { 102 lw.rotate() 103 for _, m := range lw.queue { 104 lw.logger.Println(m) 105 } 106 err := lw.fd.Sync() 107 if err != nil { 108 panic(err) 109 } 110 } 111 112 func (lw *loggerWrapper) doClose() { 113 lw.closed = true 114 close(lw.ch) 115 lw.wg.Wait() 116 } 117 118 func (lw *loggerWrapper) doWrite() { 119 defer lw.wg.Done() 120 for { 121 msg, ok := <-lw.ch 122 if !ok { 123 lw.doFlush() 124 _ = lw.fd.Close() 125 break 126 } 127 if len(lw.queue) >= lw.cacheCount { 128 lw.doFlush() 129 lw.queue = make([]string, 0, lw.cacheCount) 130 } 131 lw.queue = append(lw.queue, msg) 132 } 133 134 } 135 136 func (lw *loggerWrapper) Printf(format string, v ...interface{}) { 137 if !lw.closed { 138 msg := fmt.Sprintf(format, v...) 139 lw.ch <- msg 140 } 141 } 142 143 var consoleLogger *log.Logger 144 var fileLogger *loggerWrapper 145 var lock = new(sync.RWMutex) 146 147 func isDebugLogEnabled() bool { 148 return logConf.level <= LEVEL_DEBUG 149 } 150 151 func isErrorLogEnabled() bool { 152 return logConf.level <= LEVEL_ERROR 153 } 154 155 func isWarnLogEnabled() bool { 156 return logConf.level <= LEVEL_WARN 157 } 158 159 func isInfoLogEnabled() bool { 160 return logConf.level <= LEVEL_INFO 161 } 162 163 func reset() { 164 if fileLogger != nil { 165 fileLogger.doClose() 166 fileLogger = nil 167 } 168 consoleLogger = nil 169 logConf = getDefaultLogConf() 170 } 171 172 func InitLog(logFullPath string, maxLogSize int64, backups int, level Level, logToConsole bool) error { 173 return InitLogWithCacheCnt(logFullPath, maxLogSize, backups, level, logToConsole, 50) 174 } 175 176 func InitLogWithCacheCnt(logFullPath string, maxLogSize int64, backups int, level Level, logToConsole bool, cacheCnt int) error { 177 lock.Lock() 178 defer lock.Unlock() 179 if cacheCnt <= 0 { 180 cacheCnt = 50 181 } 182 reset() 183 if fullPath := strings.TrimSpace(logFullPath); fullPath != "" { 184 _fullPath, err := filepath.Abs(fullPath) 185 if err != nil { 186 return err 187 } 188 189 if !strings.HasSuffix(_fullPath, ".log") { 190 _fullPath += ".log" 191 } 192 193 stat, err := os.Stat(_fullPath) 194 if err == nil && stat.IsDir() { 195 return fmt.Errorf("logFullPath:[%s] is a directory", _fullPath) 196 } else if err := os.MkdirAll(filepath.Dir(_fullPath), os.ModePerm); err != nil { 197 return err 198 } 199 200 fd, err := os.OpenFile(_fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) 201 if err != nil { 202 return err 203 } 204 205 if stat == nil { 206 stat, err = os.Stat(_fullPath) 207 if err != nil { 208 _ = fd.Close() 209 return err 210 } 211 } 212 213 prefix := stat.Name() + "." 214 index := 1 215 walkFunc := func(path string, info os.FileInfo, err error) error { 216 if err == nil { 217 if name := info.Name(); strings.HasPrefix(name, prefix) { 218 if i := StringToInt(name[len(prefix):], 0); i >= index { 219 index = i + 1 220 } 221 } 222 } 223 return err 224 } 225 226 if err = filepath.Walk(filepath.Dir(_fullPath), walkFunc); err != nil { 227 _ = fd.Close() 228 return err 229 } 230 231 fileLogger = &loggerWrapper{fullPath: _fullPath, fd: fd, index: index, cacheCount: cacheCnt, closed: false} 232 fileLogger.doInit() 233 } 234 if maxLogSize > 0 { 235 logConf.maxLogSize = maxLogSize 236 } 237 if backups > 0 { 238 logConf.backups = backups 239 } 240 logConf.level = level 241 if logToConsole { 242 consoleLogger = log.New(os.Stdout, "", log.LstdFlags) 243 } 244 return nil 245 } 246 247 func CloseLog() { 248 if logEnabled() { 249 lock.Lock() 250 defer lock.Unlock() 251 reset() 252 } 253 } 254 255 func SyncLog() { 256 } 257 258 func logEnabled() bool { 259 return consoleLogger != nil || fileLogger != nil 260 } 261 262 func DoLog(level Level, format string, v ...interface{}) { 263 doLog(level, format, v...) 264 } 265 266 func doLog(level Level, format string, v ...interface{}) { 267 if logEnabled() && logConf.level <= level { 268 msg := fmt.Sprintf(format, v...) 269 if _, file, line, ok := runtime.Caller(1); ok { 270 index := strings.LastIndex(file, "/") 271 if index >= 0 { 272 file = file[index+1:] 273 } 274 msg = fmt.Sprintf("%s:%d|%s", file, line, msg) 275 } 276 prefix := logLevelMap[level] 277 if consoleLogger != nil { 278 consoleLogger.Printf("%s%s", prefix, msg) 279 } 280 if fileLogger != nil { 281 nowDate := FormatUtcNow("2006-01-02T15:04:05Z") 282 fileLogger.Printf("%s %s%s", nowDate, prefix, msg) 283 } 284 } 285 }