Files
2025-09-22 15:25:31 +05:30

449 lines
14 KiB
Go

// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package deck is a Flexible Logging Framework for Go.
package deck
import (
"fmt"
"log"
"os"
"sync"
)
// A Level is a recognized log level (Info, Error, etc). Behavior of a given level is
// backend-dependent, but they are generally used to determine how to tag, route, or
// mark-up individual log lines.
//
// Levels should not be confused with the V() attribute. V() is used to set log
// verbosity, which can be used to include or exclude logging of events, regardless
// of their associated level.
type Level uint
const (
DEBUG Level = iota
INFO
WARNING
ERROR
FATAL
)
var (
defaultDeck *Deck
)
func init() {
defaultDeck = New()
}
// Default returns the default (global) deck.
func Default() *Deck {
return defaultDeck
}
// Composer is the interface that groups Compose and Write methods.
type Composer interface {
Compose(s *AttribStore) error
Write() error
}
// Backend is the interface that identifies logging backends with NewMessage and Close methods.
type Backend interface {
New(Level, string) Composer
Close() error
}
// The Deck is the highest level of the logging hierarchy, consisting of one or more backends.
// All logs written to the deck get flushed to each backend. Multiple decks can be configured with
// their own sets of backends.
type Deck struct {
backends []Backend
verbosity int
mu sync.Mutex
}
// New returns a new initialized log deck.
func New() *Deck {
return &Deck{}
}
// Add adds a backend to the default log deck.
func Add(b Backend) {
defaultDeck.Add(b)
}
// Add adds an additional backend to the deck.
func (d *Deck) Add(b Backend) {
d.mu.Lock()
defer d.mu.Unlock()
d.backends = append(d.backends, b)
}
// SetVerbosity sets the internal verbosity level of the default deck.
func SetVerbosity(v int) {
defaultDeck.verbosity = v
}
// SetVerbosity sets the internal verbosity level of the deck.
//
// Messages are committed if the message's own verbosity level (default 0) is
// equal to or less than the deck's configured level.
func (d *Deck) SetVerbosity(v int) {
d.verbosity = v
}
func (d *Deck) mkLog(lvl Level, message string) *Log {
msg := NewLog(d.verbosity)
if len(d.backends) < 1 {
fmt.Fprintln(os.Stderr, "WARNING: no backends configured, printing to log")
log.Print(message)
}
for _, b := range d.backends {
msg.backends = append(msg.backends, b.New(lvl, message))
}
return msg
}
// InfoA constructs a message in the default deck at the INFO level.
func InfoA(message ...any) *Log {
return defaultDeck.InfoA(message...)
}
// Info immediately logs a message with no attributes to the default deck at the INFO level.
func Info(message ...any) {
defaultDeck.InfoA(message...).With(Depth(1)).Go()
}
// InfoA constructs a message at the INFO level.
func (d *Deck) InfoA(message ...any) *Log {
return d.mkLog(INFO, fmt.Sprint(message...))
}
// Info immediately logs a message with no attributes at the INFO level.
func (d *Deck) Info(message ...any) {
d.InfoA(message...).With(Depth(1)).Go()
}
// InfofA constructs a message according to the format specifier in the default deck at the INFO level.
func InfofA(format string, message ...any) *Log {
return defaultDeck.InfofA(format, message...)
}
// Infof immediately logs a message with no attributes according to the format specifier to the default deck at the INFO level.
func Infof(format string, message ...any) {
defaultDeck.InfofA(format, message...).With(Depth(1)).Go()
}
// InfofA constructs a message according to the format specifier at the INFO level.
func (d *Deck) InfofA(format string, message ...any) *Log {
return d.mkLog(INFO, fmt.Sprintf(format, message...))
}
// Infof immediately logs a message with no attributes according to the format specifier at the INFO level.
func (d *Deck) Infof(format string, message ...any) {
d.InfofA(format, message...).With(Depth(1)).Go()
}
// InfolnA constructs a message with a trailing newline in the default deck at the INFO level.
func InfolnA(message ...any) *Log {
return defaultDeck.InfolnA(message...)
}
// Infoln immediately logs a message with no attributes and with a trailing newline to the default deck at the INFO level.
func Infoln(message ...any) {
defaultDeck.InfolnA(message...).With(Depth(1)).Go()
}
// InfolnA constructs a message with a trailing newline at the INFO level.
func (d *Deck) InfolnA(message ...any) *Log {
return d.mkLog(INFO, fmt.Sprintln(message...))
}
// Infoln immediately logs a message with no attributes and with a trailing newline at the INFO level.
func (d *Deck) Infoln(message ...any) {
d.InfolnA(message...).With(Depth(1)).Go()
}
// ErrorA constructs a message in the default deck at the ERROR level.
func ErrorA(message ...any) *Log {
return defaultDeck.ErrorA(message...)
}
// Error immediately logs a message with no attributes to the default deck at the ERROR level.
func Error(message ...any) {
defaultDeck.ErrorA(message...).With(Depth(1)).Go()
}
// ErrorA constructs a message at the ERROR level.
func (d *Deck) ErrorA(message ...any) *Log {
return d.mkLog(ERROR, fmt.Sprint(message...))
}
// Error immediately logs a message with no attributes at the ERROR level.
func (d *Deck) Error(message ...any) {
d.ErrorA(message...).With(Depth(1)).Go()
}
// ErrorfA constructs a message according to the format specifier in the default deck at the ERROR level.
func ErrorfA(format string, message ...any) *Log {
return defaultDeck.ErrorfA(format, message...)
}
// Errorf immediately logs a message with no attributes according to the format specifier to the default deck at the ERROR level.
func Errorf(format string, message ...any) {
defaultDeck.ErrorfA(format, message...).With(Depth(1)).Go()
}
// ErrorfA constructs a message according to the format specifier at the ERROR level.
func (d *Deck) ErrorfA(format string, message ...any) *Log {
return d.mkLog(ERROR, fmt.Sprintf(format, message...))
}
// Errorf immediately logs a message with no attributes according to the format specifier at the ERROR level.
func (d *Deck) Errorf(format string, message ...any) {
d.ErrorfA(format, message...).With(Depth(1)).Go()
}
// ErrorlnA constructs a message with a trailing newline in the default deck at the ERROR level.
func ErrorlnA(message ...any) *Log {
return defaultDeck.ErrorlnA(message...)
}
// Errorln immediately logs a message with no attributes and with a trailing newline to the default deck at the ERROR level.
func Errorln(message ...any) {
defaultDeck.ErrorlnA(message...).With(Depth(1)).Go()
}
// ErrorlnA constructs a message with a trailing newline at the ERROR level.
func (d *Deck) ErrorlnA(message ...any) *Log {
return d.mkLog(ERROR, fmt.Sprintln(message...))
}
// Errorln immediately logs a message with no attributes and with a trailing newline at the ERROR level.
func (d *Deck) Errorln(message ...any) {
d.ErrorlnA(message...).With(Depth(1)).Go()
}
// WarningA constructs a message in the default deck at the WARNING level.
func WarningA(message ...any) *Log {
return defaultDeck.WarningA(message...)
}
// Warning immediately logs a message with no attributes to the default deck at the WARNING level.
func Warning(message ...any) {
defaultDeck.WarningA(message...).With(Depth(1)).Go()
}
// WarningA constructs a message at the WARNING level.
func (d *Deck) WarningA(message ...any) *Log {
return d.mkLog(WARNING, fmt.Sprint(message...))
}
// Warning immediately logs a message with no attributes at the WARNING level.
func (d *Deck) Warning(message ...any) {
d.WarningA(message...).With(Depth(1)).Go()
}
// WarningfA constructs a message according to the format specifier in the default deck at the WARNING level.
func WarningfA(format string, message ...any) *Log {
return defaultDeck.WarningfA(format, message...)
}
// Warningf immediately logs a message with no attributes according to the format specifier to the default deck at the WARNING level.
func Warningf(format string, message ...any) {
defaultDeck.WarningfA(format, message...).With(Depth(1)).Go()
}
// WarningfA constructs a message according to the format specifier at the WARNING level.
func (d *Deck) WarningfA(format string, message ...any) *Log {
return d.mkLog(WARNING, fmt.Sprintf(format, message...))
}
// Warningf immediately logs a message with no attributes according to the format specifier at the WARNING level.
func (d *Deck) Warningf(format string, message ...any) {
d.WarningfA(format, message...).With(Depth(1)).Go()
}
// WarninglnA constructs a message with a trailing newline in the default deck at the WARNING level.
func WarninglnA(message ...any) *Log {
return defaultDeck.WarninglnA(message...)
}
// Warningln immediately logs a message with no attributes with a trailing newline to the default deck at the WARNING level.
func Warningln(message ...any) {
defaultDeck.WarninglnA(message...).With(Depth(1)).Go()
}
// WarninglnA constructs a message with a trailing newline at the WARNING level.
func (d *Deck) WarninglnA(message ...any) *Log {
return d.mkLog(WARNING, fmt.Sprintln(message...))
}
// Warningln immediately logs a message with no attributes and with a trailing newline at the WARNING level.
func (d *Deck) Warningln(message ...any) {
d.WarninglnA(message...).Go()
}
// FatalA constructs a message in the default deck at the FATAL level.
func FatalA(message ...any) *Log {
return defaultDeck.FatalA(message...)
}
// Fatal immediately logs a message with no attributes to the default deck at the FATAL level.
func Fatal(message ...any) {
defaultDeck.FatalA(message...).With(Depth(1)).Go()
}
// FatalA constructs a message at the FATAL level.
func (d *Deck) FatalA(message ...any) *Log {
return d.mkLog(FATAL, fmt.Sprint(message...))
}
// Fatal immediately logs a message with no attributes at the FATAL level.
func (d *Deck) Fatal(message ...any) {
d.FatalA(message...).With(Depth(1)).Go()
}
// FatalfA constructs a message according to the format specifier in the default deck at the FATAL level.
func FatalfA(format string, message ...any) *Log {
return defaultDeck.FatalfA(format, message...)
}
// Fatalf immediately logs a message with no attributes according to the format specifier to the default deck at the FATAL level.
func Fatalf(format string, message ...any) {
defaultDeck.FatalfA(format, message...).With(Depth(1)).Go()
}
// FatalfA constructs a message according to the format specifier at the FATAL level.
func (d *Deck) FatalfA(format string, message ...any) *Log {
return d.mkLog(FATAL, fmt.Sprintf(format, message...))
}
// Fatalf immediately logs a message with no attributes according to the format specifier at the FATAL level.
func (d *Deck) Fatalf(format string, message ...any) {
d.FatalfA(format, message...).With(Depth(1)).Go()
}
// FatallnA constructs a message with a trailing newline in the default deck at the FATAL level.
func FatallnA(message ...any) *Log {
return defaultDeck.FatallnA(message...)
}
// Fatalln immediately logs a message with no attributes and with a trailing newline to the default deck at the FATAL level.
func Fatalln(message ...any) {
defaultDeck.FatallnA(message...).With(Depth(1)).Go()
}
// FatallnA constructs a message with a trailing newline at the FATAL level.
func (d *Deck) FatallnA(message ...any) *Log {
return d.mkLog(FATAL, fmt.Sprintln(message...))
}
// Fatalln immediately logs a message with no attributes and with a trailing newline at the FATAL level.
func (d *Deck) Fatalln(message ...any) {
d.FatallnA(message...).With(Depth(1)).Go()
}
// Close closes all backends in the default deck.
func Close() {
defaultDeck.Close()
}
// Close closes all backends in the deck.
func (d *Deck) Close() {
d.mu.Lock()
defer d.mu.Unlock()
for _, b := range d.backends {
b.Close()
}
}
// Log is a single log event that will be flushed to all registered backends.
//
// Each log may have one or more attributes associated with it.
type Log struct {
verbosity int
backends []Composer
attributes *AttribStore
mu sync.Mutex
}
// An Attrib is an attribute that can be associated with Logs.
//
// Attributes are actually functions which modify values in the AttribStore.
type Attrib func(*AttribStore)
// An AttribStore stores unique attributes associated with a given Log.
//
// The store is a string-keyed value map. Backends can interrogate the store for
// values, and use the values for their own purposes.
type AttribStore = sync.Map
// NewLog returns a new Log
func NewLog(verbosity int) *Log {
return &Log{
attributes: &AttribStore{},
verbosity: verbosity,
}
}
// With appends one or more attributes to a Log.
//
// deck.Info("message with attributes").With(V(2), EventID(3))
func (l *Log) With(attrs ...Attrib) *Log {
l.mu.Lock()
defer l.mu.Unlock()
for _, o := range attrs {
o(l.attributes)
}
return l
}
// Go commits a Log to all registered backends in the deck.
func (l *Log) Go() {
l.mu.Lock()
defer l.mu.Unlock()
for _, o := range l.backends {
i := 0
if lvl, ok := l.attributes.Load("Verbosity"); ok {
i = lvl.(int)
}
if i <= l.verbosity {
o.Compose(l.attributes)
o.Write()
}
}
}
// Depth is a general attribute that allows specifying log depth to backends. Depth
// may be used to modify log rendering under certain circumstances.
func Depth(d int) func(*AttribStore) {
return func(a *AttribStore) {
a.Store("Depth", d)
}
}
// V is a special attribute that sets the verbosity level on a message.
//
// deck.Info("example with verbosity 2").V(2).Go()
func V(v int) func(*AttribStore) {
return func(a *AttribStore) {
a.Store("Verbosity", v)
}
}