-
Notifications
You must be signed in to change notification settings - Fork 4
/
log.go
143 lines (122 loc) · 2.33 KB
/
log.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package logging
import (
"context"
"io"
"sync"
)
// TODO (bradrydzewski) writing to subscribers is currently a blocking
// operation and does not protect against slow clients from locking
// the stream. This should be resolved.
// TODO (bradrydzewski) implement a mux.Info to fetch information and
// statistics for the multiplexier. Streams, subscribers, etc
// mux.Info()
// TODO (bradrydzewski) refactor code to place publisher and subscriber
// operations in separate files with more encapsulated logic.
// sub.push()
// sub.join()
// sub.start()... event loop
type subscriber struct {
handler Handler
}
type stream struct {
sync.Mutex
path string
hist []*Entry
subs map[*subscriber]struct{}
done chan struct{}
wait sync.WaitGroup
}
type log struct {
sync.Mutex
streams map[string]*stream
}
// New returns a new logger.
func New() Log {
return &log{
streams: map[string]*stream{},
}
}
func (l *log) Open(c context.Context, path string) error {
l.Lock()
_, ok := l.streams[path]
if !ok {
l.streams[path] = &stream{
path: path,
subs: make(map[*subscriber]struct{}),
done: make(chan struct{}),
}
}
l.Unlock()
return nil
}
func (l *log) Write(c context.Context, path string, entry *Entry) error {
l.Lock()
s, ok := l.streams[path]
l.Unlock()
if !ok {
return ErrNotFound
}
s.Lock()
s.hist = append(s.hist, entry)
for sub := range s.subs {
go sub.handler(entry)
}
s.Unlock()
return nil
}
func (l *log) Tail(c context.Context, path string, handler Handler) error {
l.Lock()
s, ok := l.streams[path]
l.Unlock()
if !ok {
return ErrNotFound
}
sub := &subscriber{
handler: handler,
}
s.Lock()
if len(s.hist) != 0 {
sub.handler(s.hist...)
}
s.subs[sub] = struct{}{}
s.Unlock()
select {
case <-c.Done():
case <-s.done:
}
s.Lock()
delete(s.subs, sub)
s.Unlock()
return nil
}
func (l *log) Close(c context.Context, path string) error {
l.Lock()
s, ok := l.streams[path]
l.Unlock()
if !ok {
return ErrNotFound
}
s.Lock()
close(s.done)
s.Unlock()
l.Lock()
delete(l.streams, path)
l.Unlock()
return nil
}
func (l *log) Snapshot(c context.Context, path string, w io.Writer) error {
l.Lock()
s, ok := l.streams[path]
l.Unlock()
if !ok {
return ErrNotFound
}
s.Lock()
for _, entry := range s.hist {
w.Write(entry.Data)
w.Write(cr)
}
s.Unlock()
return nil
}
var cr = []byte{'\n'}