1package appenders 2 3import ( 4 "fmt" 5 "github.com/ian-kent/go-log/layout" 6 "github.com/ian-kent/go-log/levels" 7 "os" 8 "strconv" 9 "strings" 10 "sync" 11) 12 13type rollingFileAppender struct { 14 Appender 15 layout layout.Layout 16 MaxFileSize int64 17 MaxBackupIndex int 18 19 filename string 20 file *os.File 21 append bool 22 writeMutex sync.Mutex 23 24 bytesWritten int64 25} 26 27func RollingFile(filename string, append bool) *rollingFileAppender { 28 a := &rollingFileAppender{ 29 layout: layout.Default(), 30 MaxFileSize: 104857600, 31 MaxBackupIndex: 1, 32 append: append, 33 bytesWritten: 0, 34 } 35 err := a.SetFilename(filename) 36 if err != nil { 37 fmt.Printf("Error opening file: %s\n", err) 38 return nil 39 } 40 return a 41} 42 43func (a *rollingFileAppender) Close() { 44 if a.file != nil { 45 a.file.Close() 46 a.file = nil 47 } 48} 49 50func (a *rollingFileAppender) Write(level levels.LogLevel, message string, args ...interface{}) { 51 m := a.Layout().Format(level, message, args...) 52 if !strings.HasSuffix(m, "\n") { 53 m += "\n" 54 } 55 56 a.writeMutex.Lock() 57 a.file.Write([]byte(m)) 58 59 a.bytesWritten += int64(len(m)) 60 if a.bytesWritten >= a.MaxFileSize { 61 a.bytesWritten = 0 62 a.rotateFile() 63 } 64 65 a.writeMutex.Unlock() 66} 67 68func (a *rollingFileAppender) Layout() layout.Layout { 69 return a.layout 70} 71 72func (a *rollingFileAppender) SetLayout(layout layout.Layout) { 73 a.layout = layout 74} 75 76func (a *rollingFileAppender) Filename() string { 77 return a.filename 78} 79 80func (a *rollingFileAppender) SetFilename(filename string) error { 81 if a.filename != filename || a.file == nil { 82 a.closeFile() 83 a.filename = filename 84 err := a.openFile() 85 return err 86 } 87 return nil 88} 89 90func (a *rollingFileAppender) rotateFile() { 91 a.closeFile() 92 93 lastFile := a.filename + "." + strconv.Itoa(a.MaxBackupIndex) 94 if _, err := os.Stat(lastFile); err == nil { 95 os.Remove(lastFile) 96 } 97 98 for n := a.MaxBackupIndex; n > 0; n-- { 99 f1 := a.filename + "." + strconv.Itoa(n) 100 f2 := a.filename + "." + strconv.Itoa(n+1) 101 os.Rename(f1, f2) 102 } 103 104 os.Rename(a.filename, a.filename+".1") 105 106 a.openFile() 107} 108func (a *rollingFileAppender) closeFile() { 109 if a.file != nil { 110 a.file.Close() 111 a.file = nil 112 } 113} 114func (a *rollingFileAppender) openFile() error { 115 mode := os.O_WRONLY | os.O_APPEND | os.O_CREATE 116 if !a.append { 117 mode = os.O_WRONLY | os.O_CREATE 118 } 119 f, err := os.OpenFile(a.filename, mode, 0666) 120 a.file = f 121 return err 122} 123