buffer.go

 1package logging
 2
 3import (
 4	"strings"
 5	"sync"
 6)
 7
 8type Buffer struct {
 9	mu         sync.Mutex
10	maxEntries int
11	entries    []Entry
12	subs       []chan Entry
13}
14
15func NewBuffer(maxEntries int) *Buffer {
16	if maxEntries < 1 {
17		maxEntries = DefaultMaxEntries
18	}
19	return &Buffer{maxEntries: maxEntries}
20}
21
22func (b *Buffer) MaxEntries() int {
23	return b.maxEntries
24}
25
26func (b *Buffer) Write(p []byte) (int, error) {
27	for _, line := range strings.Split(strings.TrimRight(string(p), "\n"), "\n") {
28		if line == "" {
29			continue
30		}
31		b.append(Entry{Text: line})
32	}
33	return len(p), nil
34}
35
36func (b *Buffer) Subscribe() <-chan Entry {
37	ch := make(chan Entry, 64)
38	b.mu.Lock()
39	b.subs = append(b.subs, ch)
40	b.mu.Unlock()
41	return ch
42}
43
44func (b *Buffer) Tail(n int) []Entry {
45	b.mu.Lock()
46	defer b.mu.Unlock()
47
48	if n <= 0 || len(b.entries) == 0 {
49		return nil
50	}
51	if n > len(b.entries) {
52		n = len(b.entries)
53	}
54
55	start := len(b.entries) - n
56	entries := make([]Entry, n)
57	copy(entries, b.entries[start:])
58	return entries
59}
60
61func (b *Buffer) append(entry Entry) {
62	b.mu.Lock()
63	if len(b.entries) >= b.maxEntries {
64		copy(b.entries, b.entries[1:])
65		b.entries[len(b.entries)-1] = entry
66	} else {
67		b.entries = append(b.entries, entry)
68	}
69
70	subs := append([]chan Entry(nil), b.subs...)
71	b.mu.Unlock()
72
73	for _, ch := range subs {
74		select {
75		case ch <- entry:
76		default:
77		}
78	}
79}