1package agent 2 3import ( 4 "sync" 5) 6 7// LogHandler interface is used for clients that want to subscribe 8// to logs, for example to stream them over an IPC mechanism 9type LogHandler interface { 10 HandleLog(string) 11} 12 13// logWriter implements io.Writer so it can be used as a log sink. 14// It maintains a circular buffer of logs, and a set of handlers to 15// which it can stream the logs to. 16type logWriter struct { 17 sync.Mutex 18 logs []string 19 index int 20 handlers map[LogHandler]struct{} 21} 22 23// NewLogWriter creates a logWriter with the given buffer capacity 24func NewLogWriter(buf int) *logWriter { 25 return &logWriter{ 26 logs: make([]string, buf), 27 index: 0, 28 handlers: make(map[LogHandler]struct{}), 29 } 30} 31 32// RegisterHandler adds a log handler to receive logs, and sends 33// the last buffered logs to the handler 34func (l *logWriter) RegisterHandler(lh LogHandler) { 35 l.Lock() 36 defer l.Unlock() 37 38 // Do nothing if already registered 39 if _, ok := l.handlers[lh]; ok { 40 return 41 } 42 43 // Register 44 l.handlers[lh] = struct{}{} 45 46 // Send the old logs 47 if l.logs[l.index] != "" { 48 for i := l.index; i < len(l.logs); i++ { 49 lh.HandleLog(l.logs[i]) 50 } 51 } 52 for i := 0; i < l.index; i++ { 53 lh.HandleLog(l.logs[i]) 54 } 55} 56 57// DeregisterHandler removes a LogHandler and prevents more invocations 58func (l *logWriter) DeregisterHandler(lh LogHandler) { 59 l.Lock() 60 defer l.Unlock() 61 delete(l.handlers, lh) 62} 63 64// Write is used to accumulate new logs 65func (l *logWriter) Write(p []byte) (n int, err error) { 66 l.Lock() 67 defer l.Unlock() 68 69 // Strip off newlines at the end if there are any since we store 70 // individual log lines in the agent. 71 n = len(p) 72 if p[n-1] == '\n' { 73 p = p[:n-1] 74 } 75 76 l.logs[l.index] = string(p) 77 l.index = (l.index + 1) % len(l.logs) 78 79 for lh, _ := range l.handlers { 80 lh.HandleLog(string(p)) 81 } 82 return 83} 84