diff --git a/demo/main.go b/demo/main.go index 63c601e..3a4c7c5 100644 --- a/demo/main.go +++ b/demo/main.go @@ -15,8 +15,26 @@ func CreateLog(file string, level simplelog.Level) *simplelog.Log { } } -func main() { - log := CreateLog("./log/MS.log", simplelog.LevelInfo) +func test_timed(name string) { + log := new(simplelog.Log).InitTimed(name, simplelog.WhenMinute, 2, simplelog.LevelDebug) + n := time.Now() + for time.Since(n) < 10*time.Minute { + log.Debug("hello world") + time.Sleep(30 * time.Second) + } +} + +func test_timedRotating(name string) { + log := new(simplelog.Log).InitTimedRotating(name, simplelog.WhenMinute, 2, 3, simplelog.LevelDebug) + n := time.Now() + for time.Since(n) < 10*time.Minute { + log.Debug("hello world") + time.Sleep(30 * time.Second) + } +} + +func test_rotating(name string) { + log := CreateLog(name, simplelog.LevelInfo) for i := 0; i < 10000000; i++ { log.Trace("hello world") log.Debug("hello world") @@ -27,3 +45,9 @@ func main() { time.Sleep(8 * time.Millisecond) } } + +func main() { + go test_rotating("./log/demo.log") + go test_timedRotating("./trlog/demo.log") + test_timed("./tlog/demo.log") +} diff --git a/filehandler.go b/filehandler.go index 3ab2d8e..ca71b5e 100644 --- a/filehandler.go +++ b/filehandler.go @@ -5,6 +5,8 @@ import ( "io" "os" "path" + "path/filepath" + "strings" "time" ) @@ -53,12 +55,12 @@ type RotatingFileHandler struct { fd *os.File fileName string - maxBytes int - curBytes int - backupCount int + maxBytes int64 + curBytes int64 + backupCount uint } -func (h *RotatingFileHandler) InitRotating(name string, maxBytes int, backupCount int) (*RotatingFileHandler, error) { +func (h *RotatingFileHandler) InitRotating(name string, maxBytes int64, backupCount uint) (*RotatingFileHandler, error) { h.fileName = name h.maxBytes = maxBytes h.backupCount = backupCount @@ -74,7 +76,7 @@ func (h *RotatingFileHandler) InitRotating(name string, maxBytes int, backupCoun h.fd.Close() return nil, err } - if f.Size() < int64(h.maxBytes) { + if f.Size() < h.maxBytes { h.fd.Seek(0, io.SeekEnd) } else { h._doRollover() @@ -99,7 +101,7 @@ func (h *RotatingFileHandler) InitRotating(name string, maxBytes int, backupCoun if err != nil { return nil, err } - h.curBytes = int(f.Size()) + h.curBytes = f.Size() return h, nil } @@ -107,7 +109,7 @@ func (h *RotatingFileHandler) InitRotating(name string, maxBytes int, backupCoun func (h *RotatingFileHandler) Write(p []byte) (n int, err error) { h.doRollover() n, err = h.fd.Write(p) - h.curBytes += n + h.curBytes += int64(n) return } @@ -132,7 +134,7 @@ func (h *RotatingFileHandler) doRollover() { if h.maxBytes <= 0 { return } else if f.Size() < int64(h.maxBytes) { - h.curBytes = int(f.Size()) + h.curBytes = f.Size() return } @@ -153,92 +155,156 @@ func (h *RotatingFileHandler) _doRollover() { dfn := fmt.Sprintf("%s.1", h.fileName) os.Rename(h.fileName, dfn) - h.fd, _ = os.OpenFile(h.fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + h.fd, _ = os.OpenFile(h.fileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) + h.curBytes = 0 + } else { + h.fd.Close() + h.fd, _ = os.OpenFile(h.fileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) h.curBytes = 0 - f, err := h.fd.Stat() - if err != nil { - return - } - h.curBytes = int(f.Size()) } } -// TimedRotatingFileHandler writes log to a file, -// it will backup current and open a new one, with a period time you sepecified. -// -// refer: http://docs.python.org/2/library/logging.handlers.html. -// same like python TimedRotatingFileHandler. -type TimedRotatingFileHandler struct { +// TimedFileHandler writes log to a file. +type TimedFileHandler struct { fd *os.File - baseName string - interval int64 + dir string + name string + interval time.Duration suffix string - rolloverAt int64 + rolloverAt time.Time } +type WhenInterval int + const ( - WhenSecond = iota + WhenSecond WhenInterval = iota WhenMinute WhenHour WhenDay ) -func (h *TimedRotatingFileHandler) InitTimedRotating(name string, when int8, interval int) (*TimedRotatingFileHandler, error) { +func (h *TimedFileHandler) InitTimed(name string, when WhenInterval, interval int64) (*TimedFileHandler, error) { dir := path.Dir(name) os.Mkdir(dir, 0777) - h.baseName = name + h.dir, h.name = filepath.Split(name) switch when { case WhenSecond: - h.interval = 1 - h.suffix = "2006-01-02_15-04-05" + h.interval = 1 * time.Second + h.suffix = "2006-01-02-15-04-05" case WhenMinute: - h.interval = 60 - h.suffix = "2006-01-02_15-04" + h.interval = 1 * time.Minute + h.suffix = "2006-01-02-15-04" case WhenHour: - h.interval = 3600 - h.suffix = "2006-01-02_15" + h.interval = 1 * time.Hour + h.suffix = "2006-01-02-15" case WhenDay: - h.interval = 3600 * 24 + h.interval = 1 * time.Hour * 24 h.suffix = "2006-01-02" default: return nil, fmt.Errorf("invalid when_rotate: %d", when) } - h.interval = h.interval * int64(interval) + h.interval = time.Duration(interval) * h.interval + h.rolloverAt = time.Now() var err error - h.fd, err = os.OpenFile(h.baseName, os.O_CREATE|os.O_WRONLY, 0666) + h.fd, err = os.OpenFile(filepath.Join(h.dir, h.rolloverAt.Format(h.suffix)+"_"+h.name), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { return nil, err } - fInfo, _ := h.fd.Stat() - h.rolloverAt = fInfo.ModTime().Unix() + h.interval - return h, nil } -func (h *TimedRotatingFileHandler) doRollover() { - //refer http://hg.python.org/cpython/file/2.7/Lib/logging/handlers.py - now := time.Now() - - if h.rolloverAt <= now.Unix() { - fName := h.baseName + now.Format(h.suffix) +func (h *TimedFileHandler) doRollover() { + if time.Since(h.rolloverAt) > time.Duration(h.interval) { h.fd.Close() - e := os.Rename(h.baseName, fName) - if e != nil { - panic(e) - } - - h.fd, _ = os.OpenFile(h.baseName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) - - h.rolloverAt = time.Now().Unix() + h.interval + h.rolloverAt = time.Now() + h.fd, _ = os.OpenFile(filepath.Join(h.dir, h.rolloverAt.Format(h.suffix)+"_"+h.name), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) } } +func (h *TimedFileHandler) Write(b []byte) (n int, err error) { + h.doRollover() + return h.fd.Write(b) +} + +func (h *TimedFileHandler) Close() error { + return h.fd.Close() +} + +type TimedRotatingFileHandler struct { + fd *os.File + + dir string + name string + interval time.Duration + suffix string + rolloverAt time.Time + backupCount uint +} + +func (h *TimedRotatingFileHandler) InitTimedRotating(name string, when WhenInterval, interval int64, backupCount uint) (*TimedRotatingFileHandler, error) { + h.backupCount = backupCount + dir := path.Dir(name) + os.Mkdir(dir, 0777) + + h.dir, h.name = filepath.Split(name) + + switch when { + case WhenSecond: + h.interval = 1 * time.Second + h.suffix = "2006-01-02-15-04-05" + case WhenMinute: + h.interval = 1 * time.Minute + h.suffix = "2006-01-02-15-04" + case WhenHour: + h.interval = 1 * time.Hour + h.suffix = "2006-01-02-15" + case WhenDay: + h.interval = 1 * time.Hour * 24 + h.suffix = "2006-01-02" + default: + return nil, fmt.Errorf("invalid when_rotate: %d", when) + } + + h.interval = time.Duration(interval) * h.interval + + h.rolloverAt = time.Now() + var err error + h.fd, err = os.OpenFile(filepath.Join(h.dir, h.rolloverAt.Format(h.suffix)+"_"+h.name), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + if err != nil { + return nil, err + } + + h._doRollover() + return h, nil +} + +func (h *TimedRotatingFileHandler) _doRollover() { + fs, err := h.ListDir(h.dir, h.suffix) + if err != nil { + return + } + if len(fs) > int(h.backupCount) { + for i := 0; i < len(fs)-int(h.backupCount); i++ { + os.Remove(fs[i]) + } + } +} + +func (h *TimedRotatingFileHandler) doRollover() { + if time.Since(h.rolloverAt) > time.Duration(h.interval) { + h.fd.Close() + h.rolloverAt = time.Now() + h.fd, _ = os.OpenFile(filepath.Join(h.dir, h.rolloverAt.Format(h.suffix)+"_"+h.name), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + } + h._doRollover() +} + func (h *TimedRotatingFileHandler) Write(b []byte) (n int, err error) { h.doRollover() return h.fd.Write(b) @@ -247,3 +313,37 @@ func (h *TimedRotatingFileHandler) Write(b []byte) (n int, err error) { func (h *TimedRotatingFileHandler) Close() error { return h.fd.Close() } + +func (h *TimedRotatingFileHandler) ListDir(dir, suffix string) (files []string, err error) { + files = []string{} + + _dir, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + + for _, file := range _dir { + if file.IsDir() { + // 不需要递归读取子目录下的文件 + continue + } + names := strings.Split(file.Name(), "_") + if len(names) != 2 { + // 文件名格式不对 + continue + } + _, err := time.Parse(h.suffix, names[0]) + if err != nil { + // 文件名格式不对 + continue + } + + if h.name != names[1] { + // 文件名格式不对 + continue + } + files = append(files, filepath.Join(dir, file.Name())) + } + + return files, nil +} diff --git a/simplelog.go b/simplelog.go index 456dadb..9a65630 100644 --- a/simplelog.go +++ b/simplelog.go @@ -123,7 +123,7 @@ func (l *Log) InitFile(name string, level Level, flag int) *Log { return l.Init(handler, level, flag) } -func (l *Log) InitRotating(name string, maxBytes, backupCount int, level Level) *Log { +func (l *Log) InitRotating(name string, maxBytes int64, backupCount uint, level Level) *Log { handler, e := new(RotatingFileHandler).InitRotating(name, maxBytes, backupCount) if e != nil { panic(e) @@ -132,8 +132,17 @@ func (l *Log) InitRotating(name string, maxBytes, backupCount int, level Level) return l.Init(handler, level, Ltime|Lfile|Llevel) } -func (l *Log) InitTimedRotating(name string, when int8, interval int, level Level) *Log { - handler, e := new(TimedRotatingFileHandler).InitTimedRotating(name, when, interval) +func (l *Log) InitTimed(name string, when WhenInterval, interval int64, level Level) *Log { + handler, e := new(TimedFileHandler).InitTimed(name, when, interval) + if e != nil { + panic(e) + } + + return l.Init(handler, level, Ltime|Lfile|Llevel) +} + +func (l *Log) InitTimedRotating(name string, when WhenInterval, interval int64, backupCount uint, level Level) *Log { + handler, e := new(TimedRotatingFileHandler).InitTimedRotating(name, when, interval, backupCount) if e != nil { panic(e) }