mirror of
https://github.com/containerd/containerd.git
synced 2026-08-06 16:11:22 +00:00
449 lines
14 KiB
Go
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)
|
|
}
|
|
}
|