-
Notifications
You must be signed in to change notification settings - Fork 0
/
alog.go
107 lines (94 loc) · 2.75 KB
/
alog.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
// Package alog provides a simple asynchronous logger that will write to provided io.Writers without blocking calling
// goroutines.
package alog
import (
"fmt"
"io"
"os"
"strings"
"sync"
"time"
)
// Alog is a type that defines a logger. It can be used to write log messages synchronously (via the Write method)
// or asynchronously via the channel returned by the MessageChannel accessor.
type Alog struct {
dest io.Writer
m *sync.Mutex
msgCh chan string
errorCh chan error
shutdownCh chan struct{}
shutdownCompleteCh chan struct{}
}
// New creates a new Alog object that writes to the provided io.Writer.
// If nil is provided the output will be directed to os.Stdout.
func New(w io.Writer) *Alog {
if w == nil {
w = os.Stdout
}
return &Alog{
dest: w,
m: &sync.Mutex{},
msgCh: make(chan string),
errorCh: make(chan error),
shutdownCh: make(chan struct{}),
shutdownCompleteCh: make(chan struct{}),
}
}
// Start begins the message loop for the asynchronous logger. It should be initiated as a goroutine to prevent
// the caller from being blocked.
func (al Alog) Start() {
wg := &sync.WaitGroup{}
runLoop:
for {
select {
case msg := <-al.msgCh:
wg.Add(1)
go al.write(msg, wg)
case <-al.shutdownCh:
wg.Wait()
al.shutdown()
break runLoop
}
}
}
func (al Alog) formatMessage(msg string) string {
if !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
return fmt.Sprintf("[%v] - %v", time.Now().Format("2006-01-02 15:04:05"), msg)
}
func (al Alog) write(msg string, wg *sync.WaitGroup) {
defer wg.Done()
al.m.Lock()
defer al.m.Unlock()
_, err := al.dest.Write([]byte(al.formatMessage(msg)))
if err != nil {
go func(err error) {
al.errorCh <- err
}(err)
}
}
func (al Alog) shutdown() {
close(al.msgCh)
al.shutdownCompleteCh <- struct{}{}
}
// MessageChannel returns a channel that accepts messages that should be written to the log.
func (al Alog) MessageChannel() chan<- string {
return al.msgCh
}
// ErrorChannel returns a channel that will be populated when an error is raised during a write operation.
// This channel should always be monitored in some way to prevent deadlock goroutines from being generated
// when errors occur.
func (al Alog) ErrorChannel() <-chan error {
return al.errorCh
}
// Stop shuts down the logger. It will wait for all pending messages to be written and then return.
// The logger will no longer function after this method has been called.
func (al Alog) Stop() {
al.shutdownCh <- struct{}{}
<-al.shutdownCompleteCh
}
// Write synchronously sends the message to the log output
func (al Alog) Write(msg string) (int, error) {
return al.dest.Write([]byte(al.formatMessage(msg)))
}