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