github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/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 maxFiles int //maximum number of files 17 notifyRotate *pubsub.Publisher 18 } 19 20 //NewRotateFileWriter creates new RotateFileWriter 21 func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateFileWriter, error) { 22 log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640) 23 if err != nil { 24 return &RotateFileWriter{}, err 25 } 26 27 return &RotateFileWriter{ 28 f: log, 29 capacity: capacity, 30 maxFiles: maxFiles, 31 notifyRotate: pubsub.NewPublisher(0, 1), 32 }, nil 33 } 34 35 //WriteLog write log message to File 36 func (w *RotateFileWriter) Write(message []byte) (int, error) { 37 w.mu.Lock() 38 defer w.mu.Unlock() 39 if err := w.checkCapacityAndRotate(); err != nil { 40 return -1, err 41 } 42 43 return w.f.Write(message) 44 } 45 46 func (w *RotateFileWriter) checkCapacityAndRotate() error { 47 if w.capacity == -1 { 48 return nil 49 } 50 51 meta, err := w.f.Stat() 52 if err != nil { 53 return err 54 } 55 56 if meta.Size() >= w.capacity { 57 name := w.f.Name() 58 if err := w.f.Close(); err != nil { 59 return err 60 } 61 if err := rotate(name, w.maxFiles); err != nil { 62 return err 63 } 64 file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 06400) 65 if err != nil { 66 return err 67 } 68 w.f = file 69 w.notifyRotate.Publish(struct{}{}) 70 } 71 72 return nil 73 } 74 75 func rotate(name string, maxFiles int) error { 76 if maxFiles < 2 { 77 return nil 78 } 79 for i := maxFiles - 1; i > 1; i-- { 80 toPath := name + "." + strconv.Itoa(i) 81 fromPath := name + "." + strconv.Itoa(i-1) 82 if err := backup(fromPath, toPath); err != nil && !os.IsNotExist(err) { 83 return err 84 } 85 } 86 87 if err := backup(name, name+".1"); err != nil { 88 return err 89 } 90 return nil 91 } 92 93 // backup renames a file from fromPath to toPath 94 func backup(fromPath, toPath string) error { 95 if _, err := os.Stat(fromPath); os.IsNotExist(err) { 96 return err 97 } 98 99 if _, err := os.Stat(toPath); !os.IsNotExist(err) { 100 err := os.Remove(toPath) 101 if err != nil { 102 return err 103 } 104 } 105 106 return os.Rename(fromPath, toPath) 107 } 108 109 // LogPath returns the location the given writer logs to. 110 func (w *RotateFileWriter) LogPath() string { 111 return w.f.Name() 112 } 113 114 // MaxFiles return maximum number of files 115 func (w *RotateFileWriter) MaxFiles() int { 116 return w.maxFiles 117 } 118 119 //NotifyRotate returns the new subscriber 120 func (w *RotateFileWriter) NotifyRotate() chan interface{} { 121 return w.notifyRotate.Subscribe() 122 } 123 124 //NotifyRotateEvict removes the specified subscriber from receiving any more messages. 125 func (w *RotateFileWriter) NotifyRotateEvict(sub chan interface{}) { 126 w.notifyRotate.Evict(sub) 127 } 128 129 // Close closes underlying file and signals all readers to stop. 130 func (w *RotateFileWriter) Close() error { 131 return w.f.Close() 132 }