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