Tizen_4.0 base
[platform/upstream/docker-engine.git] / daemon / logger / loggerutils / rotatefilewriter.go
1 package loggerutils
2
3 import (
4         "errors"
5         "os"
6         "strconv"
7         "sync"
8
9         "github.com/docker/docker/pkg/pubsub"
10 )
11
12 // RotateFileWriter is Logger implementation for default Docker logging.
13 type RotateFileWriter struct {
14         f            *os.File // store for closing
15         closed       bool
16         mu           sync.Mutex
17         capacity     int64 //maximum size of each file
18         currentSize  int64 // current size of the latest file
19         maxFiles     int   //maximum number of files
20         notifyRotate *pubsub.Publisher
21 }
22
23 //NewRotateFileWriter creates new RotateFileWriter
24 func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateFileWriter, error) {
25         log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
26         if err != nil {
27                 return nil, err
28         }
29
30         size, err := log.Seek(0, os.SEEK_END)
31         if err != nil {
32                 return nil, err
33         }
34
35         return &RotateFileWriter{
36                 f:            log,
37                 capacity:     capacity,
38                 currentSize:  size,
39                 maxFiles:     maxFiles,
40                 notifyRotate: pubsub.NewPublisher(0, 1),
41         }, nil
42 }
43
44 //WriteLog write log message to File
45 func (w *RotateFileWriter) Write(message []byte) (int, error) {
46         w.mu.Lock()
47         if w.closed {
48                 w.mu.Unlock()
49                 return -1, errors.New("cannot write because the output file was closed")
50         }
51         if err := w.checkCapacityAndRotate(); err != nil {
52                 w.mu.Unlock()
53                 return -1, err
54         }
55
56         n, err := w.f.Write(message)
57         if err == nil {
58                 w.currentSize += int64(n)
59         }
60         w.mu.Unlock()
61         return n, err
62 }
63
64 func (w *RotateFileWriter) checkCapacityAndRotate() error {
65         if w.capacity == -1 {
66                 return nil
67         }
68
69         if w.currentSize >= w.capacity {
70                 name := w.f.Name()
71                 if err := w.f.Close(); err != nil {
72                         return err
73                 }
74                 if err := rotate(name, w.maxFiles); err != nil {
75                         return err
76                 }
77                 file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 06400)
78                 if err != nil {
79                         return err
80                 }
81                 w.f = file
82                 w.currentSize = 0
83                 w.notifyRotate.Publish(struct{}{})
84         }
85
86         return nil
87 }
88
89 func rotate(name string, maxFiles int) error {
90         if maxFiles < 2 {
91                 return nil
92         }
93         for i := maxFiles - 1; i > 1; i-- {
94                 toPath := name + "." + strconv.Itoa(i)
95                 fromPath := name + "." + strconv.Itoa(i-1)
96                 if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) {
97                         return err
98                 }
99         }
100
101         if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) {
102                 return err
103         }
104         return nil
105 }
106
107 // LogPath returns the location the given writer logs to.
108 func (w *RotateFileWriter) LogPath() string {
109         w.mu.Lock()
110         defer w.mu.Unlock()
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         w.mu.Lock()
132         defer w.mu.Unlock()
133         if w.closed {
134                 return nil
135         }
136         if err := w.f.Close(); err != nil {
137                 return err
138         }
139         w.closed = true
140         return nil
141 }