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}