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