github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/daemon/logger/loggerutils/rotatefilewriter.go (about) 1 package loggerutils 2 3 import ( 4 "os" 5 "strconv" 6 "sync" 7 8 "github.com/docker/docker/pkg/pubsub" 9 ) 10 11 // RotateFileWriter is Logger implementation for default Docker logging. 12 type RotateFileWriter struct { 13 f *os.File // store for closing 14 mu sync.Mutex 15 capacity int64 //maximum size of each file 16 currentSize int64 // current size of the latest file 17 maxFiles int //maximum number of files 18 notifyRotate *pubsub.Publisher 19 } 20 21 //NewRotateFileWriter creates new RotateFileWriter 22 func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateFileWriter, error) { 23 log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640) 24 if err != nil { 25 return nil, err 26 } 27 28 size, err := log.Seek(0, os.SEEK_END) 29 if err != nil { 30 return nil, err 31 } 32 33 return &RotateFileWriter{ 34 f: log, 35 capacity: capacity, 36 currentSize: size, 37 maxFiles: maxFiles, 38 notifyRotate: pubsub.NewPublisher(0, 1), 39 }, nil 40 } 41 42 //WriteLog write log message to File 43 func (w *RotateFileWriter) Write(message []byte) (int, error) { 44 w.mu.Lock() 45 if err := w.checkCapacityAndRotate(); err != nil { 46 w.mu.Unlock() 47 return -1, err 48 } 49 50 n, err := w.f.Write(message) 51 if err == nil { 52 w.currentSize += int64(n) 53 } 54 w.mu.Unlock() 55 return n, err 56 } 57 58 func (w *RotateFileWriter) checkCapacityAndRotate() error { 59 if w.capacity == -1 { 60 return nil 61 } 62 63 if w.currentSize >= w.capacity { 64 name := w.f.Name() 65 if err := w.f.Close(); err != nil { 66 return err 67 } 68 if err := rotate(name, w.maxFiles); err != nil { 69 return err 70 } 71 file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 06400) 72 if err != nil { 73 return err 74 } 75 w.f = file 76 w.currentSize = 0 77 w.notifyRotate.Publish(struct{}{}) 78 } 79 80 return nil 81 } 82 83 func rotate(name string, maxFiles int) error { 84 if maxFiles < 2 { 85 return nil 86 } 87 for i := maxFiles - 1; i > 1; i-- { 88 toPath := name + "." + strconv.Itoa(i) 89 fromPath := name + "." + strconv.Itoa(i-1) 90 if err := backup(fromPath, toPath); err != nil && !os.IsNotExist(err) { 91 return err 92 } 93 } 94 95 if err := backup(name, name+".1"); err != nil { 96 return err 97 } 98 return nil 99 } 100 101 // backup renames a file from fromPath to toPath 102 func backup(fromPath, toPath string) error { 103 if _, err := os.Stat(fromPath); os.IsNotExist(err) { 104 return err 105 } 106 107 if _, err := os.Stat(toPath); !os.IsNotExist(err) { 108 err := os.Remove(toPath) 109 if err != nil { 110 return err 111 } 112 } 113 114 return os.Rename(fromPath, toPath) 115 } 116 117 // LogPath returns the location the given writer logs to. 118 func (w *RotateFileWriter) LogPath() string { 119 return w.f.Name() 120 } 121 122 // MaxFiles return maximum number of files 123 func (w *RotateFileWriter) MaxFiles() int { 124 return w.maxFiles 125 } 126 127 //NotifyRotate returns the new subscriber 128 func (w *RotateFileWriter) NotifyRotate() chan interface{} { 129 return w.notifyRotate.Subscribe() 130 } 131 132 //NotifyRotateEvict removes the specified subscriber from receiving any more messages. 133 func (w *RotateFileWriter) NotifyRotateEvict(sub chan interface{}) { 134 w.notifyRotate.Evict(sub) 135 } 136 137 // Close closes underlying file and signals all readers to stop. 138 func (w *RotateFileWriter) Close() error { 139 return w.f.Close() 140 }