gopkg.in/dotcloud/docker.v1@v1.13.1/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 := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) { 91 return err 92 } 93 } 94 95 if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) { 96 return err 97 } 98 return nil 99 } 100 101 // LogPath returns the location the given writer logs to. 102 func (w *RotateFileWriter) LogPath() string { 103 return w.f.Name() 104 } 105 106 // MaxFiles return maximum number of files 107 func (w *RotateFileWriter) MaxFiles() int { 108 return w.maxFiles 109 } 110 111 //NotifyRotate returns the new subscriber 112 func (w *RotateFileWriter) NotifyRotate() chan interface{} { 113 return w.notifyRotate.Subscribe() 114 } 115 116 //NotifyRotateEvict removes the specified subscriber from receiving any more messages. 117 func (w *RotateFileWriter) NotifyRotateEvict(sub chan interface{}) { 118 w.notifyRotate.Evict(sub) 119 } 120 121 // Close closes underlying file and signals all readers to stop. 122 func (w *RotateFileWriter) Close() error { 123 return w.f.Close() 124 }