logger.Outlet: WriteEntry must not block
- make TCPOutlet fully asynchronous, dropping messages if connection is not fast enough - syslog is just fine for now, local anyways - stdout same thing refs #26
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
@@ -82,10 +81,17 @@ type Entry struct {
|
||||
Fields Fields
|
||||
}
|
||||
|
||||
// An outlet receives log entries produced by the Logger and writes them to some destination.
|
||||
type Outlet interface {
|
||||
// Write the entry to the destination.
|
||||
//
|
||||
// Logger waits for all outlets to return from WriteEntry() before returning from the log call.
|
||||
// An implementation of Outlet must assert that it does not block in WriteEntry.
|
||||
// Otherwise, it will slow down the program.
|
||||
//
|
||||
// Note: os.Stderr is also used by logger.Logger for reporting errors returned by outlets
|
||||
// => you probably don't want to log there
|
||||
WriteEntry(ctx context.Context, entry Entry) error
|
||||
WriteEntry(entry Entry) error
|
||||
}
|
||||
|
||||
type Outlets struct {
|
||||
@@ -138,4 +144,4 @@ func (os *Outlets) GetLoggerErrorOutlet() Outlet {
|
||||
|
||||
type nullOutlet struct{}
|
||||
|
||||
func (nullOutlet) WriteEntry(ctx context.Context, entry Entry) error { return nil }
|
||||
func (nullOutlet) WriteEntry(entry Entry) error { return nil }
|
||||
|
||||
+9
-20
@@ -1,7 +1,6 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
@@ -52,7 +51,7 @@ func (l *Logger) logInternalError(outlet Outlet, err string) {
|
||||
time.Now(),
|
||||
fields,
|
||||
}
|
||||
l.outlets.GetLoggerErrorOutlet().WriteEntry(context.Background(), entry)
|
||||
l.outlets.GetLoggerErrorOutlet().WriteEntry(entry)
|
||||
}
|
||||
|
||||
func (l *Logger) log(level Level, msg string) {
|
||||
@@ -62,30 +61,20 @@ func (l *Logger) log(level Level, msg string) {
|
||||
|
||||
entry := Entry{level, msg, time.Now(), l.fields}
|
||||
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(l.outletTimeout))
|
||||
ech := make(chan outletResult)
|
||||
|
||||
louts := l.outlets.Get(level)
|
||||
ech := make(chan outletResult, len(louts))
|
||||
for i := range louts {
|
||||
go func(ctx context.Context, outlet Outlet, entry Entry) {
|
||||
ech <- outletResult{outlet, outlet.WriteEntry(ctx, entry)}
|
||||
}(ctx, louts[i], entry)
|
||||
go func(outlet Outlet, entry Entry) {
|
||||
ech <- outletResult{outlet, outlet.WriteEntry(entry)}
|
||||
}(louts[i], entry)
|
||||
}
|
||||
|
||||
for fin := 0; fin < len(louts); fin++ {
|
||||
select {
|
||||
case res := <-ech:
|
||||
if res.Error != nil {
|
||||
l.logInternalError(res.Outlet, res.Error.Error())
|
||||
}
|
||||
case <-ctx.Done():
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
l.logInternalError(nil, "one or more outlets exceeded timeout but will keep waiting anyways")
|
||||
}
|
||||
res := <-ech
|
||||
if res.Error != nil {
|
||||
l.logInternalError(res.Outlet, res.Error.Error())
|
||||
}
|
||||
}
|
||||
|
||||
cancel() // make go vet happy
|
||||
close(ech)
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user