mirror of
https://github.com/zrepl/zrepl.git
synced 2025-01-02 20:38:50 +01:00
parent
f5ead68586
commit
a7f70a566d
@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LoggingConfig struct {
|
type LoggingConfig struct {
|
||||||
Outlets logger.Outlets
|
Outlets *logger.Outlets
|
||||||
}
|
}
|
||||||
|
|
||||||
type MetadataFlags int64
|
type MetadataFlags int64
|
||||||
@ -26,10 +26,21 @@ const (
|
|||||||
MetadataAll MetadataFlags = ^0
|
MetadataAll MetadataFlags = ^0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func LoggerErrorOutlet() logger.Outlet {
|
||||||
|
|
||||||
|
formatter := &HumanFormatter{}
|
||||||
|
writer := os.Stderr
|
||||||
|
formatter.SetMetadataFlags(MetadataAll)
|
||||||
|
return WriterOutlet{
|
||||||
|
formatter, writer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func parseLogging(i interface{}) (c *LoggingConfig, err error) {
|
func parseLogging(i interface{}) (c *LoggingConfig, err error) {
|
||||||
|
|
||||||
c = &LoggingConfig{}
|
c = &LoggingConfig{}
|
||||||
c.Outlets = logger.NewOutlets()
|
|
||||||
|
c.Outlets = logger.NewOutlets(LoggerErrorOutlet())
|
||||||
|
|
||||||
var asList []interface{}
|
var asList []interface{}
|
||||||
if err = mapstructure.Decode(i, &asList); err != nil {
|
if err = mapstructure.Decode(i, &asList); err != nil {
|
||||||
|
@ -65,7 +65,7 @@ func init() {
|
|||||||
|
|
||||||
func testCmdGlobalInit(cmd *cobra.Command, args []string) {
|
func testCmdGlobalInit(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
out := logger.NewOutlets()
|
out := logger.NewOutlets(WriterOutlet{&NoFormatter{}, os.Stderr})
|
||||||
out.Add(WriterOutlet{&NoFormatter{}, os.Stdout}, logger.Info)
|
out.Add(WriterOutlet{&NoFormatter{}, os.Stdout}, logger.Info)
|
||||||
log := logger.NewLogger(out, 1*time.Second)
|
log := logger.NewLogger(out, 1*time.Second)
|
||||||
testCmdGlobal.log = log
|
testCmdGlobal.log = log
|
||||||
|
@ -78,14 +78,28 @@ 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 {
|
||||||
|
reg map[Level][]Outlet
|
||||||
func NewOutlets() Outlets {
|
e Outlet
|
||||||
return make(Outlets, len(AllLevels))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (os Outlets) Add(outlet Outlet, minLevel Level) {
|
func NewOutlets(loggerErrorOutlet Outlet) *Outlets {
|
||||||
for _, l := range AllLevels[minLevel:] {
|
return &Outlets{
|
||||||
os[l] = append(os[l], outlet)
|
make(map[Level][]Outlet, len(AllLevels)),
|
||||||
|
loggerErrorOutlet,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (os *Outlets) Add(outlet Outlet, minLevel Level) {
|
||||||
|
for _, l := range AllLevels[minLevel:] {
|
||||||
|
os.reg[l] = append(os.reg[l], outlet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (os *Outlets) Get(level Level) []Outlet {
|
||||||
|
return os.reg[level]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (os *Outlets) GetLoggerErrorOutlet() Outlet {
|
||||||
|
return os.e
|
||||||
|
}
|
||||||
|
@ -3,7 +3,6 @@ package logger
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -15,17 +14,16 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const DefaultUserFieldCapacity = 5
|
const DefaultUserFieldCapacity = 5
|
||||||
const InternalErrorPrefix = "github.com/zrepl/zrepl/logger: "
|
|
||||||
|
|
||||||
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,
|
||||||
@ -34,6 +32,29 @@ func NewLogger(outlets Outlets, outletTimeout time.Duration) *Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type outletResult struct {
|
||||||
|
Outlet Outlet
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) logInternalError(outlet Outlet, err string) {
|
||||||
|
fields := Fields{}
|
||||||
|
if outlet != nil {
|
||||||
|
if _, ok := outlet.(fmt.Stringer); ok {
|
||||||
|
fields["outlet"] = fmt.Sprintf("%s", outlet)
|
||||||
|
}
|
||||||
|
fields["outlet_type"] = fmt.Sprintf("%T", outlet)
|
||||||
|
}
|
||||||
|
fields[FieldError] = err
|
||||||
|
entry := Entry{
|
||||||
|
Error,
|
||||||
|
"outlet error",
|
||||||
|
time.Now(),
|
||||||
|
fields,
|
||||||
|
}
|
||||||
|
l.outlets.GetLoggerErrorOutlet().WriteEntry(context.Background(), entry)
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Logger) log(level Level, msg string) {
|
func (l *Logger) log(level Level, msg string) {
|
||||||
|
|
||||||
l.mtx.Lock()
|
l.mtx.Lock()
|
||||||
@ -42,24 +63,24 @@ func (l *Logger) log(level Level, msg string) {
|
|||||||
entry := Entry{level, msg, time.Now(), l.fields}
|
entry := Entry{level, msg, time.Now(), l.fields}
|
||||||
|
|
||||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(l.outletTimeout))
|
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(l.outletTimeout))
|
||||||
ech := make(chan error)
|
ech := make(chan outletResult)
|
||||||
|
|
||||||
louts := l.outlets[level]
|
louts := l.outlets.Get(level)
|
||||||
for i := range louts {
|
for i := range louts {
|
||||||
go func(ctx context.Context, outlet Outlet, entry Entry) {
|
go func(ctx context.Context, outlet Outlet, entry Entry) {
|
||||||
ech <- outlet.WriteEntry(ctx, entry)
|
ech <- outletResult{outlet, outlet.WriteEntry(ctx, entry)}
|
||||||
}(ctx, louts[i], entry)
|
}(ctx, louts[i], entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
for fin := 0; fin < len(louts); fin++ {
|
for fin := 0; fin < len(louts); fin++ {
|
||||||
select {
|
select {
|
||||||
case err := <-ech:
|
case res := <-ech:
|
||||||
if err != nil {
|
if res.Error != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%s outlet error: %s\n", InternalErrorPrefix, err)
|
l.logInternalError(res.Outlet, res.Error.Error())
|
||||||
}
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
if ctx.Err() == context.DeadlineExceeded {
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
fmt.Fprintf(os.Stderr, "%s outlets exceeded deadline, keep waiting anyways", InternalErrorPrefix)
|
l.logInternalError(nil, "one or more outlets exceeded timeout but will keep waiting anyways")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,7 +95,8 @@ func (l *Logger) WithField(field string, val interface{}) *Logger {
|
|||||||
defer l.mtx.Unlock()
|
defer l.mtx.Unlock()
|
||||||
|
|
||||||
if _, ok := l.fields[field]; ok {
|
if _, ok := l.fields[field]; ok {
|
||||||
fmt.Fprintf(os.Stderr, "%s caller overwrites field '%s'. Stack:\n%s\n", InternalErrorPrefix, field, string(debug.Stack()))
|
l.logInternalError(nil,
|
||||||
|
fmt.Sprintf("caller overwrites field '%s'. Stack: %s", field, string(debug.Stack())))
|
||||||
}
|
}
|
||||||
|
|
||||||
child := &Logger{
|
child := &Logger{
|
||||||
|
Loading…
Reference in New Issue
Block a user