github.com/XiaoMi/Gaea@v1.2.5/log/xlog/file.go (about) 1 // Copyright 2019 The Gaea Authors. All Rights Reserved. 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 package xlog 16 17 import ( 18 "fmt" 19 "os" 20 "path/filepath" 21 "strconv" 22 "sync" 23 "time" 24 ) 25 26 // XFileLog is the file logger 27 type XFileLog struct { 28 filename string 29 path string 30 level int 31 32 skip int 33 runtime bool 34 file *os.File 35 errFile *os.File 36 hostname string 37 service string 38 split sync.Once 39 mu sync.Mutex 40 } 41 42 // constants of XFileLog 43 const ( 44 XFileLogDefaultLogID = "900000001" 45 SpliterDelay = 5 46 CleanDays = -3 47 ) 48 49 // NewXFileLog is the constructor of XFileLog 50 //生成一个日志实例,service用来标识业务的服务名。 51 //比如:logger := xlog.NewXFileLog("gaea") 52 func NewXFileLog() XLogger { 53 return &XFileLog{ 54 skip: XLogDefSkipNum, 55 } 56 } 57 58 // Init implements XLogger 59 func (p *XFileLog) Init(config map[string]string) (err error) { 60 61 path, ok := config["path"] 62 if !ok { 63 err = fmt.Errorf("init XFileLog failed, not found path") 64 return 65 } 66 67 filename, ok := config["filename"] 68 if !ok { 69 err = fmt.Errorf("init XFileLog failed, not found filename") 70 return 71 } 72 73 level, ok := config["level"] 74 if !ok { 75 err = fmt.Errorf("init XFileLog failed, not found level") 76 return 77 } 78 79 service, _ := config["service"] 80 if len(service) > 0 { 81 p.service = service 82 } 83 84 runtime, ok := config["runtime"] 85 if !ok || runtime == "true" || runtime == "TRUE" { 86 p.runtime = true 87 } else { 88 p.runtime = false 89 } 90 91 skip, _ := config["skip"] 92 if len(skip) > 0 { 93 skipNum, err := strconv.Atoi(skip) 94 if err == nil { 95 p.skip = skipNum 96 } 97 } 98 99 isDir, err := isDir(path) 100 if err != nil || !isDir { 101 err = os.MkdirAll(path, 0755) 102 if err != nil { 103 return newError("Mkdir failed, err:%v", err) 104 } 105 } 106 107 p.path = path 108 p.filename = filename 109 p.level = LevelFromStr(level) 110 111 hostname, _ := os.Hostname() 112 p.hostname = hostname 113 body := func() { 114 go p.spliter() 115 } 116 doSplit, ok := config["dosplit"] 117 if !ok { 118 doSplit = "true" 119 } 120 if doSplit == "true" { 121 p.split.Do(body) 122 } 123 return p.ReOpen() 124 } 125 126 // split the log file 127 func (p *XFileLog) spliter() { 128 preHour := time.Now().Hour() 129 splitTime := time.Now().Format("2006010215") 130 defer p.Close() 131 for { 132 time.Sleep(time.Second * SpliterDelay) 133 if time.Now().Hour() != preHour { 134 p.clean() 135 p.rename(splitTime) 136 preHour = time.Now().Hour() 137 splitTime = time.Now().Format("2006010215") 138 } 139 } 140 } 141 142 // SetLevel implements XLogger 143 func (p *XFileLog) SetLevel(level string) { 144 p.level = LevelFromStr(level) 145 } 146 147 // SetSkip implements XLogger 148 func (p *XFileLog) SetSkip(skip int) { 149 p.skip = skip 150 } 151 152 func (p *XFileLog) openFile(filename string) (*os.File, error) { 153 file, err := os.OpenFile(filename, 154 os.O_CREATE|os.O_APPEND|os.O_WRONLY, 155 0644, 156 ) 157 158 if err != nil { 159 return nil, newError("open %s failed, err:%v", filename, err) 160 } 161 162 return file, err 163 } 164 165 func delayClose(fp *os.File) { 166 if fp == nil { 167 return 168 } 169 time.Sleep(1000 * time.Millisecond) 170 fp.Close() 171 } 172 173 func (p *XFileLog) clean() (err error) { 174 deadline := time.Now().AddDate(0, 0, CleanDays) 175 var files []string 176 files, err = filepath.Glob(fmt.Sprintf("%s/%s.log*", p.path, p.filename)) 177 if err != nil { 178 return 179 } 180 var fileInfo os.FileInfo 181 for _, file := range files { 182 if filepath.Base(file) == fmt.Sprintf("%s.log", p.filename) { 183 continue 184 } 185 if filepath.Base(file) == fmt.Sprintf("%s.log.wf", p.filename) { 186 continue 187 } 188 if fileInfo, err = os.Stat(file); err == nil { 189 if fileInfo.ModTime().Before(deadline) { 190 os.Remove(file) 191 } else if fileInfo.Size() == 0 { 192 os.Remove(file) 193 } 194 } 195 } 196 return 197 } 198 199 func (p *XFileLog) rename(shuffix string) (err error) { 200 p.mu.Lock() 201 defer p.mu.Unlock() 202 defer p.ReOpen() 203 if p.file == nil { 204 return 205 } 206 var fileInfo os.FileInfo 207 normalLog := p.path + "/" + p.filename + ".log" 208 warnLog := normalLog + ".wf" 209 newLog := fmt.Sprintf("%s/%s.log-%s.log", p.path, p.filename, shuffix) 210 newWarnLog := fmt.Sprintf("%s/%s.log.wf-%s.log.wf", p.path, p.filename, shuffix) 211 if fileInfo, err = os.Stat(normalLog); err == nil && fileInfo.Size() == 0 { 212 return 213 } 214 if _, err = os.Stat(newLog); err == nil { 215 return 216 } 217 if err = os.Rename(normalLog, newLog); err != nil { 218 return 219 } 220 if fileInfo, err = os.Stat(warnLog); err == nil && fileInfo.Size() == 0 { 221 return 222 } 223 if _, err = os.Stat(newWarnLog); err == nil { 224 return 225 } 226 if err = os.Rename(warnLog, newWarnLog); err != nil { 227 return 228 } 229 return 230 } 231 232 // ReOpen implements XLogger 233 func (p *XFileLog) ReOpen() error { 234 go delayClose(p.file) 235 go delayClose(p.errFile) 236 237 normalLog := p.path + "/" + p.filename + ".log" 238 file, err := p.openFile(normalLog) 239 if err != nil { 240 return err 241 } 242 243 p.file = file 244 warnLog := normalLog + ".wf" 245 p.errFile, err = p.openFile(warnLog) 246 if err != nil { 247 p.file.Close() 248 p.file = nil 249 return err 250 } 251 252 return nil 253 } 254 255 // Warn implements XLogger 256 func (p *XFileLog) Warn(format string, a ...interface{}) error { 257 if p.level > WarnLevel { 258 return nil 259 } 260 261 return p.warnx(XFileLogDefaultLogID, format, a...) 262 } 263 264 // Warnx implements XLogger 265 func (p *XFileLog) Warnx(logID, format string, a ...interface{}) error { 266 if p.level > WarnLevel { 267 return nil 268 } 269 270 return p.warnx(logID, format, a...) 271 } 272 273 func (p *XFileLog) warnx(logID, format string, a ...interface{}) error { 274 logText := formatValue(format, a...) 275 fun, filename, lineno := getRuntimeInfo(p.skip) 276 logText = formatLineInfo(p.runtime, fun, filepath.Base(filename), logText, lineno) 277 //logText = fmt.Sprintf("[%s:%s:%d] %s", fun, filepath.Base(filename), lineno, logText) 278 279 return p.write(WarnLevel, &logText, logID) 280 } 281 282 // Fatal implements XLogger 283 func (p *XFileLog) Fatal(format string, a ...interface{}) error { 284 if p.level > FatalLevel { 285 return nil 286 } 287 288 return p.fatalx(XFileLogDefaultLogID, format, a...) 289 } 290 291 // Fatalx implements XLogger 292 func (p *XFileLog) Fatalx(logID, format string, a ...interface{}) error { 293 if p.level > FatalLevel { 294 return nil 295 } 296 297 return p.fatalx(logID, format, a...) 298 } 299 300 func (p *XFileLog) fatalx(logID, format string, a ...interface{}) error { 301 logText := formatValue(format, a...) 302 fun, filename, lineno := getRuntimeInfo(p.skip) 303 logText = formatLineInfo(p.runtime, fun, filepath.Base(filename), logText, lineno) 304 //logText = fmt.Sprintf("[%s:%s:%d] %s", fun, filepath.Base(filename), lineno, logText) 305 306 return p.write(FatalLevel, &logText, logID) 307 } 308 309 // Notice implements XLogger 310 func (p *XFileLog) Notice(format string, a ...interface{}) error { 311 if p.level > NoticeLevel { 312 return nil 313 } 314 return p.noticex(XFileLogDefaultLogID, format, a...) 315 } 316 317 // Noticex implements XLogger 318 func (p *XFileLog) Noticex(logID, format string, a ...interface{}) error { 319 if p.level > NoticeLevel { 320 return nil 321 } 322 return p.noticex(logID, format, a...) 323 } 324 325 func (p *XFileLog) noticex(logID, format string, a ...interface{}) error { 326 logText := formatValue(format, a...) 327 fun, filename, lineno := getRuntimeInfo(p.skip) 328 logText = formatLineInfo(p.runtime, fun, filepath.Base(filename), logText, lineno) 329 330 return p.write(NoticeLevel, &logText, logID) 331 } 332 333 // Trace implements XLogger 334 func (p *XFileLog) Trace(format string, a ...interface{}) error { 335 return p.tracex(XFileLogDefaultLogID, format, a...) 336 } 337 338 // Tracex implements XLogger 339 func (p *XFileLog) Tracex(logID, format string, a ...interface{}) error { 340 return p.tracex(logID, format, a...) 341 } 342 343 func (p *XFileLog) tracex(logID, format string, a ...interface{}) error { 344 if p.level > TraceLevel { 345 return nil 346 } 347 348 logText := formatValue(format, a...) 349 fun, filename, lineno := getRuntimeInfo(p.skip) 350 logText = formatLineInfo(p.runtime, fun, filepath.Base(filename), logText, lineno) 351 //logText = fmt.Sprintf("[%s:%s:%d] %s", fun, filepath.Base(filename), lineno, logText) 352 353 return p.write(TraceLevel, &logText, logID) 354 } 355 356 // Debug implements XLogger 357 func (p *XFileLog) Debug(format string, a ...interface{}) error { 358 return p.debugx(XFileLogDefaultLogID, format, a...) 359 } 360 361 func (p *XFileLog) debugx(logID, format string, a ...interface{}) error { 362 if p.level > DebugLevel { 363 return nil 364 } 365 366 logText := formatValue(format, a...) 367 fun, filename, lineno := getRuntimeInfo(p.skip) 368 logText = formatLineInfo(p.runtime, fun, filepath.Base(filename), logText, lineno) 369 370 return p.write(DebugLevel, &logText, logID) 371 } 372 373 // Debugx implements XLogger 374 func (p *XFileLog) Debugx(logID, format string, a ...interface{}) error { 375 return p.debugx(logID, format, a...) 376 } 377 378 // Close implements XLogger 379 func (p *XFileLog) Close() { 380 p.mu.Lock() 381 defer p.mu.Unlock() 382 if p.file != nil { 383 p.file.Close() 384 p.file = nil 385 } 386 387 if p.errFile != nil { 388 p.errFile.Close() 389 p.errFile = nil 390 } 391 } 392 393 // GetHost getter of hostname 394 func (p *XFileLog) GetHost() string { 395 return p.hostname 396 } 397 398 func (p *XFileLog) write(level int, msg *string, logID string) error { 399 levelText := levelTextArray[level] 400 time := time.Now().Format("2006-01-02 15:04:05") 401 402 logText := formatLog(msg, time, p.service, p.hostname, levelText, logID) 403 file := p.file 404 if level >= WarnLevel { 405 file = p.errFile 406 } 407 408 file.Write([]byte(logText)) 409 return nil 410 } 411 412 func isDir(path string) (bool, error) { 413 stat, err := os.Stat(path) 414 if err != nil { 415 return false, err 416 } 417 return stat.IsDir(), nil 418 }