zrepl/internal/logger/datastructures.go
2024-10-18 19:21:17 +02:00

187 lines
3.6 KiB
Go

package logger
import (
"bytes"
"encoding/json"
"fmt"
"sync"
"time"
"github.com/fatih/color"
"github.com/pkg/errors"
)
type Level int
func (l Level) MarshalJSON() ([]byte, error) {
return json.Marshal(l.String())
}
func (l *Level) UnmarshalJSON(input []byte) (err error) {
var s string
if err = json.Unmarshal(input, &s); err != nil {
return err
}
*l, err = ParseLevel(s)
return err
}
// implement flag.Value
// implement github.com/spf13/pflag.Value
func (l *Level) Set(s string) error {
newl, err := ParseLevel(s)
if err != nil {
return err
}
*l = newl
return nil
}
// implement github.com/spf13/pflag.Value
func (l *Level) Type() string {
var buf bytes.Buffer
for i, l := range AllLevels {
fmt.Fprintf(&buf, "%s", l)
if i != len(AllLevels)-1 {
fmt.Fprintf(&buf, "|")
}
}
return fmt.Sprintf("(%s)", buf.String())
}
const (
Debug Level = iota
Info
Warn
Error
)
func (l Level) Short() string {
switch l {
case Debug:
return "DEBG"
case Info:
return "INFO"
case Warn:
return "WARN"
case Error:
return "ERRO"
default:
return l.String()
}
}
func (l Level) String() string {
switch l {
case Debug:
return "debug"
case Info:
return "info"
case Warn:
return "warn"
case Error:
return "error"
default:
return fmt.Sprintf("unknown level %d", l)
}
}
func ParseLevel(s string) (l Level, err error) {
for _, l := range AllLevels {
if s == l.String() {
return l, nil
}
}
return -1, errors.Errorf("unknown level '%s'", s)
}
// Levels ordered least severe to most severe
var AllLevels []Level = []Level{Debug, Info, Warn, Error}
type Fields map[string]interface{}
type Entry struct {
Level Level
Message string
Time time.Time
Fields Fields
}
func (e Entry) Color() *color.Color {
c := color.New()
switch e.Level {
case Debug:
c.Add(color.FgHiBlue)
case Info:
c.Add(color.FgHiGreen)
case Warn:
c.Add(color.FgHiYellow)
case Error:
c.Add(color.FgHiRed)
}
return c
}
// 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(entry Entry) error
}
type Outlets struct {
mtx sync.RWMutex
outs map[Level][]Outlet
}
func NewOutlets() *Outlets {
return &Outlets{
mtx: sync.RWMutex{},
outs: make(map[Level][]Outlet, len(AllLevels)),
}
}
func (os *Outlets) DeepCopy() (copy *Outlets) {
copy = NewOutlets()
for level := range os.outs {
copy.outs[level] = append(copy.outs[level], os.outs[level]...)
}
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()
// with minLevel <= Error.
// If no such outlet is in this Outlets list, a discarding outlet is returned.
func (os *Outlets) GetLoggerErrorOutlet() Outlet {
os.mtx.RLock()
defer os.mtx.RUnlock()
if len(os.outs[Error]) < 1 {
return nullOutlet{}
}
return os.outs[Error][0]
}
type nullOutlet struct{}
func (nullOutlet) WriteEntry(entry Entry) error { return nil }