diff --git a/log.go b/log.go index 6020a89d..f46b9343 100644 --- a/log.go +++ b/log.go @@ -14,6 +14,7 @@ import ( type FileLogWriter struct { *log.Logger + mw *MuxWriter // The opened file filename string @@ -34,6 +35,24 @@ type FileLogWriter struct { startLock sync.Mutex //only one log can writer to the file } +type MuxWriter struct { + sync.Mutex + fd *os.File +} + +func (l *MuxWriter) Write(b []byte) (int, error) { + l.Lock() + defer l.Unlock() + return l.fd.Write(b) +} + +func (l *MuxWriter) SetFd(fd *os.File) { + if l.fd != nil { + l.fd.Close() + } + l.fd = fd +} + func NewFileWriter(fname string, rotate bool) *FileLogWriter { w := &FileLogWriter{ filename: fname, @@ -43,6 +62,10 @@ func NewFileWriter(fname string, rotate bool) *FileLogWriter { maxdays: 7, rotate: rotate, } + // use MuxWriter instead direct use os.File for lock write when rotate + w.mw = new(MuxWriter) + // set MuxWriter as Logger's io.Writer + w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime) return w } @@ -71,9 +94,16 @@ func (w *FileLogWriter) SetRotateMaxDays(maxdays int64) *FileLogWriter { } func (w *FileLogWriter) StartLogger() error { - if err := w.DoRotate(false); err != nil { + fd, err := w.createLogFile() + if err != nil { return err } + w.mw.SetFd(fd) + err = w.initFd() + if err != nil { + return err + } + BeeLogger = w return nil } @@ -83,7 +113,7 @@ func (w *FileLogWriter) docheck(size int) { if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) || (w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) || (w.daily && time.Now().Day() != w.daily_opendate) { - if err := w.DoRotate(true); err != nil { + if err := w.DoRotate(); err != nil { fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) return } @@ -101,37 +131,14 @@ func (w *FileLogWriter) Printf(format string, v ...interface{}) { w.Logger.Printf(format, v...) } -func (w *FileLogWriter) DoRotate(rotate bool) error { - if rotate { - _, err := os.Lstat(w.filename) - if err == nil { // file exists - // Find the next available number - num := 1 - fname := "" - for ; err == nil && num <= 999; num++ { - fname = w.filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num) - _, err = os.Lstat(fname) - } - // return error if the last file checked still existed - if err == nil { - return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename) - } - - // Rename the file to its newfound home - err = os.Rename(w.filename, fname) - if err != nil { - return fmt.Errorf("Rotate: %s\n", err) - } - go w.deleteOldLog() - } - } - +func (w *FileLogWriter) createLogFile() (*os.File, error) { // Open the log file fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) - if err != nil { - return err - } - w.Logger = log.New(fd, "", log.Ldate|log.Ltime) + return fd, err +} + +func (w *FileLogWriter) initFd() error { + fd := w.mw.fd finfo, err := fd.Stat() if err != nil { return fmt.Errorf("get stat err: %s\n", err) @@ -144,11 +151,50 @@ func (w *FileLogWriter) DoRotate(rotate bool) error { fmt.Println(err) } w.maxlines_curlines = len(strings.Split(string(content), "\n")) - } else { w.maxlines_curlines = 0 } - BeeLogger = w + return nil +} + +func (w *FileLogWriter) DoRotate() error { + _, err := os.Lstat(w.filename) + if err == nil { // file exists + // Find the next available number + num := 1 + fname := "" + for ; err == nil && num <= 999; num++ { + fname = w.filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num) + _, err = os.Lstat(fname) + } + // return error if the last file checked still existed + if err == nil { + return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename) + } + + // block Logger's io.Writer + w.mw.Lock() + defer w.mw.Unlock() + + fd := w.mw.fd + fd.Close() + + // close fd before rename + // Rename the file to its newfound home + err = os.Rename(w.filename, fname) + if err != nil { + return fmt.Errorf("Rotate: %s\n", err) + } + + // re-start logger + err = w.StartLogger() + if err != nil { + return fmt.Errorf("Rotate StartLogger: %s\n", err) + } + + go w.deleteOldLog() + } + return nil }