/
multilog.go
160 lines (132 loc) · 4.06 KB
/
multilog.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package server
import "github.com/zalgonoise/zlog/log/event"
type multiLogger struct {
loggers []LogServer
}
func (ml *multiLogger) addLoggers(l ...LogServer) {
ml.loggers = make([]LogServer, 0, len(l))
for _, logger := range l {
ml.addLogger(logger)
}
}
func (ml *multiLogger) addLogger(l LogServer) {
if l == nil {
return
}
if iml, ok := l.(*multiLogger); ok {
for _, logger := range iml.loggers {
ml.addLogger(logger)
}
return
}
ml.loggers = append(ml.loggers, l)
}
func (ml *multiLogger) build() LogServer {
if len(ml.loggers) == 0 {
return nil
}
if len(ml.loggers) == 1 {
return ml.loggers[0]
}
return ml
}
// Multilogger function takes in any number of LogServer interfaces, merging them together
// and returning a single LogServer.
//
// This is a LogServer multiplexer.
func MultiLogger(loggers ...LogServer) LogServer {
if len(loggers) == 0 {
return nil
}
if len(loggers) == 1 {
return loggers[0]
}
ml := new(multiLogger)
ml.addLoggers(loggers...)
return ml.build()
}
// Serve is the implementation of the `Serve()` method, from the LogServer interface
//
// It will cycle through all configured loggers and launching their `Serve()` method
// as a goroutine, except for the last one. This is a blocking operation.
func (l *multiLogger) Serve() {
var idxLimit = len(l.loggers) - 2
for i := 0; i < len(l.loggers); i++ {
if i == idxLimit {
l.loggers[i].Serve()
}
go l.loggers[i].Serve()
}
}
// Stop is the implementation of the `Stop()` method, from the LogServer interface
//
// It will cycle through all configured loggers and launching their `Stop()` method
// as a goroutine, except for the last one. This is a blocking operation.
func (l *multiLogger) Stop() {
var idxLimit = len(l.loggers) - 2
for i := 0; i < len(l.loggers); i++ {
if i == idxLimit {
l.loggers[i].Stop()
}
go l.loggers[i].Stop()
}
}
// Channels is the implementation of the `Channels()` method, from the LogServer interface
//
// It creates new event and error channels similar to a `Channels()` call, but launches three
// listeners (as goroutines) to monitor for messages and fan-out to all configured loggers
// respectively.
//
// Both `*event.Event` channels (for the _actual logger_ and the service logger) will listen
// for messages as normal, but a received message is fanned-out to all configured loggers, to
// their respective output event channel.
//
// The error channel (a read one) works the opposite way: all error channels are iterated through
// and a goroutine is launched for each of them. On _any_ error received, a copy is sent to the
//
// output error channel
func (l *multiLogger) Channels() (logCh, logSvCh chan *event.Event, errCh chan error) {
// make output channels
logCh = make(chan *event.Event)
logSvCh = make(chan *event.Event)
errCh = make(chan error)
// make channel slices according to configured loggers' length
logChSet := make([]chan *event.Event, len(l.loggers))
logSvChSet := make([]chan *event.Event, len(l.loggers))
errChSet := make([]chan error, len(l.loggers))
// get the channels from each configured logger
for _, l := range l.loggers {
lCh, lSvCh, eCh := l.Channels()
logChSet = append(logChSet, lCh)
logSvChSet = append(logSvChSet, lSvCh)
errChSet = append(errChSet, eCh)
}
// kick off goroutine for log channel
// any message received on the returned channel will be fanned out to all loggers
go func(ch chan *event.Event, chSet []chan *event.Event) {
for msg := range ch {
for _, c := range chSet {
c <- msg
}
}
}(logCh, logChSet)
// kick off goroutine for service logger channel
// any message received on the returned channel will be fanned out to all loggers
go func(ch chan *event.Event, chSet []chan *event.Event) {
for msg := range ch {
for _, c := range chSet {
c <- msg
}
}
}(logSvCh, logSvChSet)
// kick off goroutine for error channel
// any message received on any channel will be pushed to the output one
for _, ch := range errChSet {
go func(in, out chan error) {
for err := range in {
errCh <- err
}
}(ch, errCh)
}
return logCh, logSvCh, errCh
}