logger: support forking of outlets

refs #10
This commit is contained in:
Christian Schwarz 2017-12-23 00:55:02 +01:00
parent 583a63a68f
commit 261d095108
3 changed files with 53 additions and 16 deletions

View File

@ -13,7 +13,7 @@ import (
) )
type LoggingConfig struct { type LoggingConfig struct {
Outlets logger.Outlets Outlets *logger.Outlets
} }
type MetadataFlags int64 type MetadataFlags int64

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/pkg/errors" "github.com/pkg/errors"
"sync"
"time" "time"
) )
@ -78,30 +79,52 @@ type Outlet interface {
WriteEntry(ctx context.Context, entry Entry) error WriteEntry(ctx context.Context, entry Entry) error
} }
type Outlets map[Level][]Outlet type Outlets struct {
mtx sync.RWMutex
func NewOutlets() Outlets { outs map[Level][]Outlet
return make(map[Level][]Outlet, len(AllLevels))
} }
func (os Outlets) Add(outlet Outlet, minLevel Level) { func NewOutlets() *Outlets {
for _, l := range AllLevels[minLevel:] { return &Outlets{
os[l] = append(os[l], outlet) mtx: sync.RWMutex{},
outs: make(map[Level][]Outlet, len(AllLevels)),
} }
} }
func (os Outlets) Get(level Level) []Outlet { func (os *Outlets) DeepCopy() (copy *Outlets) {
return os[level] copy = NewOutlets()
for level := range os.outs {
for i := range os.outs[level] {
copy.outs[level] = append(copy.outs[level], os.outs[level][i])
}
}
return copy
}
func (os *Outlets) Add(outlet Outlet, minLevel Level) {
os.mtx.Lock()
defer os.mtx.Unlock()
for _, l := range AllLevels[minLevel:] {
os.outs[l] = append(os.outs[l], outlet)
}
}
func (os *Outlets) Get(level Level) []Outlet {
os.mtx.RLock()
defer os.mtx.RUnlock()
return os.outs[level]
} }
// Return the first outlet added to this Outlets list using Add() // Return the first outlet added to this Outlets list using Add()
// with minLevel <= Error. // with minLevel <= Error.
// If no such outlet is in this Outlets list, a discarding outlet is returned. // If no such outlet is in this Outlets list, a discarding outlet is returned.
func (os Outlets) GetLoggerErrorOutlet() Outlet { func (os *Outlets) GetLoggerErrorOutlet() Outlet {
if len(os[Error]) < 1 { os.mtx.RLock()
defer os.mtx.RUnlock()
if len(os.outs[Error]) < 1 {
return nullOutlet{} return nullOutlet{}
} }
return os[Error][0] return os.outs[Error][0]
} }
type nullOutlet struct{} type nullOutlet struct{}

View File

@ -17,13 +17,13 @@ const DefaultUserFieldCapacity = 5
type Logger struct { type Logger struct {
fields Fields fields Fields
outlets Outlets outlets *Outlets
outletTimeout time.Duration outletTimeout time.Duration
mtx *sync.Mutex mtx *sync.Mutex
} }
func NewLogger(outlets Outlets, outletTimeout time.Duration) *Logger { func NewLogger(outlets *Outlets, outletTimeout time.Duration) *Logger {
return &Logger{ return &Logger{
make(Fields, DefaultUserFieldCapacity), make(Fields, DefaultUserFieldCapacity),
outlets, outlets,
@ -89,6 +89,20 @@ func (l *Logger) log(level Level, msg string) {
} }
func (l *Logger) WithOutlet(outlet Outlet, level Level) *Logger {
l.mtx.Lock()
defer l.mtx.Unlock()
newOutlets := l.outlets.DeepCopy()
newOutlets.Add(outlet, level)
child := &Logger{
fields: l.fields,
outlets: newOutlets,
outletTimeout: l.outletTimeout,
mtx: l.mtx,
}
return child
}
func (l *Logger) WithField(field string, val interface{}) *Logger { func (l *Logger) WithField(field string, val interface{}) *Logger {
l.mtx.Lock() l.mtx.Lock()
@ -101,7 +115,7 @@ func (l *Logger) WithField(field string, val interface{}) *Logger {
child := &Logger{ child := &Logger{
fields: make(Fields, len(l.fields)+1), fields: make(Fields, len(l.fields)+1),
outlets: l.outlets, // cannot be changed after logger initialized outlets: l.outlets,
outletTimeout: l.outletTimeout, outletTimeout: l.outletTimeout,
mtx: l.mtx, mtx: l.mtx,
} }