yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/huawei/obs/log.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Copyright 2019 Huawei Technologies Co.,Ltd. 16 // Licensed under the Apache License, Version 2.0 (the "License"); you may not use 17 // this file except in compliance with the License. You may obtain a copy of the 18 // License at 19 // 20 // http://www.apache.org/licenses/LICENSE-2.0 21 // 22 // Unless required by applicable law or agreed to in writing, software distributed 23 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 24 // CONDITIONS OF ANY KIND, either express or implied. See the License for the 25 // specific language governing permissions and limitations under the License. 26 27 package obs 28 29 import ( 30 "fmt" 31 "log" 32 "os" 33 "path/filepath" 34 "runtime" 35 "strings" 36 "sync" 37 ) 38 39 type Level int 40 41 const ( 42 LEVEL_OFF Level = 500 43 LEVEL_ERROR Level = 400 44 LEVEL_WARN Level = 300 45 LEVEL_INFO Level = 200 46 LEVEL_DEBUG Level = 100 47 ) 48 49 var logLevelMap = map[Level]string{ 50 LEVEL_OFF: "[OFF]: ", 51 LEVEL_ERROR: "[ERROR]: ", 52 LEVEL_WARN: "[WARN]: ", 53 LEVEL_INFO: "[INFO]: ", 54 LEVEL_DEBUG: "[DEBUG]: ", 55 } 56 57 type logConfType struct { 58 level Level 59 logToConsole bool 60 logFullPath string 61 maxLogSize int64 62 backups int 63 } 64 65 func getDefaultLogConf() logConfType { 66 return logConfType{ 67 level: LEVEL_WARN, 68 logToConsole: false, 69 logFullPath: "", 70 maxLogSize: 1024 * 1024 * 30, //30MB 71 backups: 10, 72 } 73 } 74 75 var logConf logConfType 76 77 type loggerWrapper struct { 78 fullPath string 79 fd *os.File 80 ch chan string 81 wg sync.WaitGroup 82 queue []string 83 logger *log.Logger 84 index int 85 cacheCount int 86 closed bool 87 } 88 89 func (lw *loggerWrapper) doInit() { 90 lw.queue = make([]string, 0, lw.cacheCount) 91 lw.logger = log.New(lw.fd, "", 0) 92 lw.ch = make(chan string, lw.cacheCount) 93 lw.wg.Add(1) 94 go lw.doWrite() 95 } 96 97 func (lw *loggerWrapper) rotate() { 98 stat, err := lw.fd.Stat() 99 if err != nil { 100 _err := lw.fd.Close() 101 if _err != nil { 102 doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err) 103 } 104 panic(err) 105 } 106 if stat.Size() >= logConf.maxLogSize { 107 _err := lw.fd.Sync() 108 if _err != nil { 109 panic(err) 110 } 111 _err = lw.fd.Close() 112 if _err != nil { 113 doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err) 114 } 115 if lw.index > logConf.backups { 116 lw.index = 1 117 } 118 _err = os.Rename(lw.fullPath, lw.fullPath+"."+IntToString(lw.index)) 119 if _err != nil { 120 panic(err) 121 } 122 lw.index++ 123 124 fd, err := os.OpenFile(lw.fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) 125 if err != nil { 126 panic(err) 127 } 128 lw.fd = fd 129 lw.logger.SetOutput(lw.fd) 130 } 131 } 132 133 func (lw *loggerWrapper) doFlush() { 134 lw.rotate() 135 for _, m := range lw.queue { 136 lw.logger.Println(m) 137 } 138 err := lw.fd.Sync() 139 if err != nil { 140 panic(err) 141 } 142 } 143 144 func (lw *loggerWrapper) doClose() { 145 lw.closed = true 146 close(lw.ch) 147 lw.wg.Wait() 148 } 149 150 func (lw *loggerWrapper) doWrite() { 151 defer lw.wg.Done() 152 for { 153 msg, ok := <-lw.ch 154 if !ok { 155 lw.doFlush() 156 _err := lw.fd.Close() 157 if _err != nil { 158 doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err) 159 } 160 break 161 } 162 if len(lw.queue) >= lw.cacheCount { 163 lw.doFlush() 164 lw.queue = make([]string, 0, lw.cacheCount) 165 } 166 lw.queue = append(lw.queue, msg) 167 } 168 169 } 170 171 func (lw *loggerWrapper) Printf(format string, v ...interface{}) { 172 if !lw.closed { 173 msg := fmt.Sprintf(format, v...) 174 lw.ch <- msg 175 } 176 } 177 178 var consoleLogger *log.Logger 179 var fileLogger *loggerWrapper 180 var lock = new(sync.RWMutex) 181 182 func isDebugLogEnabled() bool { 183 return logConf.level <= LEVEL_DEBUG 184 } 185 186 func isErrorLogEnabled() bool { 187 return logConf.level <= LEVEL_ERROR 188 } 189 190 func isWarnLogEnabled() bool { 191 return logConf.level <= LEVEL_WARN 192 } 193 194 func isInfoLogEnabled() bool { 195 return logConf.level <= LEVEL_INFO 196 } 197 198 func reset() { 199 if fileLogger != nil { 200 fileLogger.doClose() 201 fileLogger = nil 202 } 203 consoleLogger = nil 204 logConf = getDefaultLogConf() 205 } 206 207 func InitLog(logFullPath string, maxLogSize int64, backups int, level Level, logToConsole bool) error { 208 return InitLogWithCacheCnt(logFullPath, maxLogSize, backups, level, logToConsole, 50) 209 } 210 211 func InitLogWithCacheCnt(logFullPath string, maxLogSize int64, backups int, level Level, logToConsole bool, cacheCnt int) error { 212 lock.Lock() 213 defer lock.Unlock() 214 if cacheCnt <= 0 { 215 cacheCnt = 50 216 } 217 reset() 218 if fullPath := strings.TrimSpace(logFullPath); fullPath != "" { 219 _fullPath, err := filepath.Abs(fullPath) 220 if err != nil { 221 return err 222 } 223 224 if !strings.HasSuffix(_fullPath, ".log") { 225 _fullPath += ".log" 226 } 227 228 stat, err := os.Stat(_fullPath) 229 if err == nil && stat.IsDir() { 230 return fmt.Errorf("logFullPath:[%s] is a directory", _fullPath) 231 } else if err = os.MkdirAll(filepath.Dir(_fullPath), os.ModePerm); err != nil { 232 return err 233 } 234 235 fd, err := os.OpenFile(_fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) 236 if err != nil { 237 return err 238 } 239 240 if stat == nil { 241 stat, err = os.Stat(_fullPath) 242 if err != nil { 243 _err := fd.Close() 244 if _err != nil { 245 doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err) 246 } 247 return err 248 } 249 } 250 251 prefix := stat.Name() + "." 252 index := 1 253 walkFunc := func(path string, info os.FileInfo, err error) error { 254 if err == nil { 255 if name := info.Name(); strings.HasPrefix(name, prefix) { 256 if i := StringToInt(name[len(prefix):], 0); i >= index { 257 index = i + 1 258 } 259 } 260 } 261 return err 262 } 263 264 if err = filepath.Walk(filepath.Dir(_fullPath), walkFunc); err != nil { 265 _err := fd.Close() 266 if _err != nil { 267 doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err) 268 } 269 return err 270 } 271 272 fileLogger = &loggerWrapper{fullPath: _fullPath, fd: fd, index: index, cacheCount: cacheCnt, closed: false} 273 fileLogger.doInit() 274 } 275 if maxLogSize > 0 { 276 logConf.maxLogSize = maxLogSize 277 } 278 if backups > 0 { 279 logConf.backups = backups 280 } 281 logConf.level = level 282 if logToConsole { 283 consoleLogger = log.New(os.Stdout, "", log.LstdFlags) 284 } 285 return nil 286 } 287 288 func CloseLog() { 289 if logEnabled() { 290 lock.Lock() 291 defer lock.Unlock() 292 reset() 293 } 294 } 295 296 func SyncLog() { 297 } 298 299 func logEnabled() bool { 300 return consoleLogger != nil || fileLogger != nil 301 } 302 303 func DoLog(level Level, format string, v ...interface{}) { 304 doLog(level, format, v...) 305 } 306 307 func doLog(level Level, format string, v ...interface{}) { 308 if logEnabled() && logConf.level <= level { 309 msg := fmt.Sprintf(format, v...) 310 if _, file, line, ok := runtime.Caller(1); ok { 311 index := strings.LastIndex(file, "/") 312 if index >= 0 { 313 file = file[index+1:] 314 } 315 msg = fmt.Sprintf("%s:%d|%s", file, line, msg) 316 } 317 prefix := logLevelMap[level] 318 if consoleLogger != nil { 319 consoleLogger.Printf("%s%s", prefix, msg) 320 } 321 if fileLogger != nil { 322 nowDate := FormatUtcNow("2006-01-02T15:04:05Z") 323 fileLogger.Printf("%s %s%s", nowDate, prefix, msg) 324 } 325 } 326 }