github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 "github.com/juju/utils/deque" 14 ) 15 16 // LogRecord represents a log message in an agent which is to be 17 // transmitted to the JES. 18 type LogRecord struct { 19 Time time.Time 20 Module string 21 Location string // e.g. "foo.go:42" 22 Level loggo.Level 23 Message string 24 25 // Number of messages dropped after this one due to buffer limit. 26 DroppedAfter int 27 } 28 29 // LogRecordCh defines the channel type used to send log message 30 // structs within the unit and machine agents. 31 type LogRecordCh chan *LogRecord 32 33 const writerName = "buffered-logs" 34 35 // InstallBufferedLogWriter creates a new BufferedLogWriter, registers 36 // it with Loggo and returns its output channel. 37 func InstallBufferedLogWriter(maxLen int) (LogRecordCh, error) { 38 writer := NewBufferedLogWriter(maxLen) 39 err := loggo.RegisterWriter(writerName, writer) 40 if err != nil { 41 return nil, errors.Annotate(err, "failed to set up log buffering") 42 } 43 return writer.Logs(), nil 44 } 45 46 // UninstallBufferedLogWriter removes the BufferedLogWriter previously 47 // installed by InstallBufferedLogWriter and closes it. 48 func UninstallBufferedLogWriter() error { 49 writer, err := loggo.RemoveWriter(writerName) 50 if err != nil { 51 return errors.Annotate(err, "failed to uninstall log buffering") 52 } 53 bufWriter, ok := writer.(*BufferedLogWriter) 54 if !ok { 55 return errors.New("unexpected writer installed as buffered log writer") 56 } 57 bufWriter.Close() 58 return nil 59 } 60 61 // BufferedLogWriter is a loggo.Writer which buffers log messages in 62 // memory. These messages are retrieved by reading from the channel 63 // returned by the Logs method. 64 // 65 // Up to maxLen log messages will be buffered. If this limit is 66 // exceeded, the oldest records will be automatically discarded. 67 type BufferedLogWriter struct { 68 maxLen int 69 in LogRecordCh 70 out LogRecordCh 71 } 72 73 // NewBufferedLogWriter returns a new BufferedLogWriter which will 74 // cache up to maxLen log messages. 75 func NewBufferedLogWriter(maxLen int) *BufferedLogWriter { 76 w := &BufferedLogWriter{ 77 maxLen: maxLen, 78 in: make(LogRecordCh), 79 out: make(LogRecordCh), 80 } 81 go w.loop() 82 return w 83 } 84 85 func (w *BufferedLogWriter) loop() { 86 buffer := deque.New() 87 var outCh LogRecordCh // Output channel - set when there's something to send. 88 var outRec *LogRecord // Next LogRecord to send to the output channel. 89 90 for { 91 // If there's something in the buffer and there's nothing 92 // queued up to send, set up the next LogRecord to send. 93 if outCh == nil { 94 if item, haveItem := buffer.PopFront(); haveItem { 95 outRec = item.(*LogRecord) 96 outCh = w.out 97 } 98 } 99 100 select { 101 case inRec, ok := <-w.in: 102 if !ok { 103 // Input channel has been closed; finish up. 104 close(w.out) 105 return 106 } 107 108 buffer.PushBack(inRec) 109 110 if buffer.Len() > w.maxLen { 111 // The buffer has exceeded the limit - discard the 112 // next LogRecord from the front of the queue. 113 buffer.PopFront() 114 outRec.DroppedAfter++ 115 } 116 117 case outCh <- outRec: 118 outCh = nil // Signal that send happened. 119 } 120 } 121 122 } 123 124 // Write sends a new log message to the writer. This implements the loggo.Writer interface. 125 func (w *BufferedLogWriter) Write(entry loggo.Entry) { 126 w.in <- &LogRecord{ 127 Time: entry.Timestamp, 128 Module: entry.Module, 129 Location: fmt.Sprintf("%s:%d", filepath.Base(entry.Filename), entry.Line), 130 Level: entry.Level, 131 Message: entry.Message, 132 } 133 } 134 135 // Logs returns a channel which emits log messages that have been sent 136 // to the BufferedLogWriter instance. 137 func (w *BufferedLogWriter) Logs() LogRecordCh { 138 return w.out 139 } 140 141 // Close cleans up the BufferedLogWriter instance. The output channel 142 // returned by the Logs method will be closed and any further Write 143 // calls will panic. 144 func (w *BufferedLogWriter) Close() { 145 close(w.in) 146 }