71 lines
1.2 KiB
Go
Executable File
71 lines
1.2 KiB
Go
Executable File
package ripsawlogger
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
bufferSize = 128
|
|
flushTimeoutMilliseconds = 2500
|
|
)
|
|
|
|
type asyncLogger struct {
|
|
channel chan LogEntry
|
|
logger Logger
|
|
m sync.Mutex
|
|
}
|
|
|
|
// Creates a logger that performs writes asynchronously and non-blocking-ly
|
|
func NewAsyncLogger(next Logger) Logger {
|
|
|
|
entries := make(chan LogEntry, bufferSize)
|
|
|
|
go func(logger Logger, entries chan LogEntry) {
|
|
for {
|
|
// wait for some data and forward it to the right place
|
|
logger.Log(<-entries)
|
|
}
|
|
}(next, entries)
|
|
|
|
return &asyncLogger{channel: entries, logger: next}
|
|
}
|
|
|
|
func (rl *asyncLogger) Flush() {
|
|
|
|
// prevent additional writes
|
|
rl.m.Lock()
|
|
defer rl.m.Unlock()
|
|
|
|
timeoutStart := time.Now()
|
|
|
|
// flush downstream logs when we finish
|
|
defer rl.logger.Flush()
|
|
|
|
// wait for the channel to clear out
|
|
for {
|
|
|
|
length := len(rl.channel)
|
|
|
|
// if the channel is empty, then we're done
|
|
if length == 0 {
|
|
return
|
|
}
|
|
|
|
// if we've exhausted our timeout, just finish
|
|
if time.Now().Sub(timeoutStart) >= flushTimeoutMilliseconds*time.Millisecond {
|
|
return
|
|
}
|
|
|
|
// wait a slight amount
|
|
time.Sleep(30 * time.Millisecond)
|
|
}
|
|
|
|
}
|
|
|
|
func (rl *asyncLogger) Log(data LogEntry) {
|
|
rl.m.Lock()
|
|
defer rl.m.Unlock()
|
|
rl.channel <- data
|
|
}
|