github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/logsender/bufferedlogwriter.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENSE file for details. 3 4 package logsender 5 6 import ( 7 "fmt" 8 "path/filepath" 9 "sync" 10 "time" 11 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 "github.com/juju/utils/deque" 15 ) 16 17 // LogRecord represents a log message in an agent which is to be 18 // transmitted to the JES. 19 type LogRecord struct { 20 Time time.Time 21 Module string 22 Location string // e.g. "foo.go:42" 23 Level loggo.Level 24 Message string 25 26 // Number of messages dropped after this one due to buffer limit. 27 DroppedAfter int 28 } 29 30 // LogStats contains statistics on logging. 31 type LogStats struct { 32 // Enqueued is the number of log messages enqueued. 33 Enqueued uint64 34 35 // Sent is the number of log messages sent. 36 Sent uint64 37 38 // Dropped is the number of log messages dropped from the queue. 39 Dropped uint64 40 } 41 42 // LogRecordCh defines the channel type used to send log message 43 // structs within the unit and machine agents. 44 type LogRecordCh chan *LogRecord 45 46 const writerName = "buffered-logs" 47 48 // InstallBufferedLogWriter creates and returns a new BufferedLogWriter, 49 // registering it with Loggo. 50 func InstallBufferedLogWriter(maxLen int) (*BufferedLogWriter, error) { 51 writer := NewBufferedLogWriter(maxLen) 52 err := loggo.RegisterWriter(writerName, writer) 53 if err != nil { 54 return nil, errors.Annotate(err, "failed to set up log buffering") 55 } 56 return writer, nil 57 } 58 59 // UninstallBufferedLogWriter removes the BufferedLogWriter previously 60 // installed by InstallBufferedLogWriter and closes it. 61 func UninstallBufferedLogWriter() error { 62 writer, err := loggo.RemoveWriter(writerName) 63 if err != nil { 64 return errors.Annotate(err, "failed to uninstall log buffering") 65 } 66 bufWriter, ok := writer.(*BufferedLogWriter) 67 if !ok { 68 return errors.New("unexpected writer installed as buffered log writer") 69 } 70 bufWriter.Close() 71 return nil 72 } 73 74 // BufferedLogWriter is a loggo.Writer which buffers log messages in 75 // memory. These messages are retrieved by reading from the channel 76 // returned by the Logs method. 77 // 78 // Up to maxLen log messages will be buffered. If this limit is 79 // exceeded, the oldest records will be automatically discarded. 80 type BufferedLogWriter struct { 81 maxLen int 82 in LogRecordCh 83 out LogRecordCh 84 85 mu sync.Mutex 86 stats LogStats 87 } 88 89 // NewBufferedLogWriter returns a new BufferedLogWriter which will 90 // cache up to maxLen log messages. 91 func NewBufferedLogWriter(maxLen int) *BufferedLogWriter { 92 w := &BufferedLogWriter{ 93 maxLen: maxLen, 94 in: make(LogRecordCh), 95 out: make(LogRecordCh), 96 } 97 go w.loop() 98 return w 99 } 100 101 func (w *BufferedLogWriter) loop() { 102 buffer := deque.New() 103 var outCh LogRecordCh // Output channel - set when there's something to send. 104 var outRec *LogRecord // Next LogRecord to send to the output channel. 105 106 for { 107 // If there's something in the buffer and there's nothing 108 // queued up to send, set up the next LogRecord to send. 109 if outCh == nil { 110 if item, haveItem := buffer.PopFront(); haveItem { 111 outRec = item.(*LogRecord) 112 outCh = w.out 113 } 114 } 115 116 select { 117 case inRec, ok := <-w.in: 118 if !ok { 119 // Input channel has been closed; finish up. 120 close(w.out) 121 return 122 } 123 124 buffer.PushBack(inRec) 125 126 w.mu.Lock() 127 w.stats.Enqueued++ 128 if buffer.Len() > w.maxLen { 129 // The buffer has exceeded the limit - discard the 130 // next LogRecord from the front of the queue. 131 buffer.PopFront() 132 outRec.DroppedAfter++ 133 w.stats.Dropped++ 134 } 135 w.mu.Unlock() 136 137 case outCh <- outRec: 138 outCh = nil // Signal that send happened. 139 140 w.mu.Lock() 141 w.stats.Sent++ 142 w.mu.Unlock() 143 } 144 } 145 146 } 147 148 // Write sends a new log message to the writer. This implements the loggo.Writer interface. 149 func (w *BufferedLogWriter) Write(entry loggo.Entry) { 150 w.in <- &LogRecord{ 151 Time: entry.Timestamp, 152 Module: entry.Module, 153 Location: fmt.Sprintf("%s:%d", filepath.Base(entry.Filename), entry.Line), 154 Level: entry.Level, 155 Message: entry.Message, 156 } 157 } 158 159 // Logs returns a channel which emits log messages that have been sent 160 // to the BufferedLogWriter instance. 161 func (w *BufferedLogWriter) Logs() LogRecordCh { 162 return w.out 163 } 164 165 // Capacity returns the capacity of the BufferedLogWriter. 166 func (w *BufferedLogWriter) Capacity() int { 167 return w.maxLen 168 } 169 170 // Stats returns the current LogStats for this BufferedLogWriter. 171 func (w *BufferedLogWriter) Stats() LogStats { 172 w.mu.Lock() 173 defer w.mu.Unlock() 174 return w.stats 175 } 176 177 // Close cleans up the BufferedLogWriter instance. The output channel 178 // returned by the Logs method will be closed and any further Write 179 // calls will panic. 180 func (w *BufferedLogWriter) Close() { 181 close(w.in) 182 }