mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
cd697913ef
Start using the new slog handlers. With this commit we also remove the need for the LogWriter since we let our LogRotator implement io.Writer and pass that in to our log file handler.
264 lines
7.3 KiB
Go
264 lines
7.3 KiB
Go
package build
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/btcsuite/btclog/v2"
|
|
)
|
|
|
|
// SubLogCreator can be used to create a new logger for a particular subsystem.
|
|
type SubLogCreator interface {
|
|
// Logger returns a new logger for a particular subsytem.
|
|
Logger(subsystemTag string) btclog.Logger
|
|
}
|
|
|
|
// SubLoggerManager manages a set of subsystem loggers. Level updates will be
|
|
// applied to all the loggers managed by the manager.
|
|
type SubLoggerManager struct {
|
|
genLogger SubLogCreator
|
|
|
|
loggers SubLoggers
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// A compile time check to ensure SubLoggerManager implements the
|
|
// LeveledSubLogger interface.
|
|
var _ LeveledSubLogger = (*SubLoggerManager)(nil)
|
|
|
|
// NewSubLoggerManager constructs a new SubLoggerManager.
|
|
func NewSubLoggerManager(handlers ...btclog.Handler) *SubLoggerManager {
|
|
return &SubLoggerManager{
|
|
loggers: make(SubLoggers),
|
|
genLogger: newSubLogGenerator(
|
|
newHandlerSet(btclog.LevelInfo, handlers...),
|
|
),
|
|
}
|
|
}
|
|
|
|
// GenSubLogger creates a new sub-logger and adds it to the set managed by the
|
|
// SubLoggerManager. A shutdown callback function is provided to be able to shut
|
|
// down in case of a critical error.
|
|
func (r *SubLoggerManager) GenSubLogger(subsystem string,
|
|
shutdown func()) btclog.Logger {
|
|
|
|
// Create a new logger with the given subsystem tag.
|
|
logger := r.genLogger.Logger(subsystem)
|
|
|
|
// Wrap the new logger in a Shutdown logger so that the shutdown
|
|
// call back is called if a critical log is ever written via this new
|
|
// logger.
|
|
l := NewShutdownLogger(logger, shutdown)
|
|
|
|
r.RegisterSubLogger(subsystem, l)
|
|
|
|
return l
|
|
}
|
|
|
|
// RegisterSubLogger registers the given logger under the given subsystem name.
|
|
func (r *SubLoggerManager) RegisterSubLogger(subsystem string,
|
|
logger btclog.Logger) {
|
|
|
|
// Add the new logger to the set of loggers managed by the manager.
|
|
r.mu.Lock()
|
|
r.loggers[subsystem] = logger
|
|
r.mu.Unlock()
|
|
}
|
|
|
|
// SubLoggers returns all currently registered subsystem loggers for this log
|
|
// writer.
|
|
//
|
|
// NOTE: This is part of the LeveledSubLogger interface.
|
|
func (r *SubLoggerManager) SubLoggers() SubLoggers {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
return r.loggers
|
|
}
|
|
|
|
// SupportedSubsystems returns a sorted string slice of all keys in the
|
|
// subsystems map, corresponding to the names of the subsystems.
|
|
//
|
|
// NOTE: This is part of the LeveledSubLogger interface.
|
|
func (r *SubLoggerManager) SupportedSubsystems() []string {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
// Convert the subsystemLoggers map keys to a string slice.
|
|
subsystems := make([]string, 0, len(r.loggers))
|
|
for subsysID := range r.loggers {
|
|
subsystems = append(subsystems, subsysID)
|
|
}
|
|
|
|
// Sort the subsystems for stable display.
|
|
sort.Strings(subsystems)
|
|
|
|
return subsystems
|
|
}
|
|
|
|
// SetLogLevel sets the logging level for provided subsystem. Invalid
|
|
// subsystems are ignored. Uninitialized subsystems are dynamically created as
|
|
// needed.
|
|
//
|
|
// NOTE: This is part of the LeveledSubLogger interface.
|
|
func (r *SubLoggerManager) SetLogLevel(subsystemID string, logLevel string) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
r.setLogLevelUnsafe(subsystemID, logLevel)
|
|
}
|
|
|
|
// setLogLevelUnsafe sets the logging level for provided subsystem. Invalid
|
|
// subsystems are ignored. Uninitialized subsystems are dynamically created as
|
|
// needed.
|
|
//
|
|
// NOTE: the SubLoggerManager mutex must be held before calling this method.
|
|
func (r *SubLoggerManager) setLogLevelUnsafe(subsystemID string,
|
|
logLevel string) {
|
|
|
|
// Ignore invalid subsystems.
|
|
logger, ok := r.loggers[subsystemID]
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
// Defaults to info if the log level is invalid.
|
|
level, _ := btclog.LevelFromString(logLevel)
|
|
|
|
logger.SetLevel(level)
|
|
}
|
|
|
|
// SetLogLevels sets the log level for all subsystem loggers to the passed
|
|
// level. It also dynamically creates the subsystem loggers as needed, so it
|
|
// can be used to initialize the logging system.
|
|
//
|
|
// NOTE: This is part of the LeveledSubLogger interface.
|
|
func (r *SubLoggerManager) SetLogLevels(logLevel string) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
// Configure all sub-systems with the new logging level. Dynamically
|
|
// create loggers as needed.
|
|
for subsystemID := range r.loggers {
|
|
r.setLogLevelUnsafe(subsystemID, logLevel)
|
|
}
|
|
}
|
|
|
|
// SubLoggers is a type that holds a map of subsystem loggers keyed by their
|
|
// subsystem name.
|
|
type SubLoggers map[string]btclog.Logger
|
|
|
|
// LeveledSubLogger provides the ability to retrieve the subsystem loggers of
|
|
// a logger and set their log levels individually or all at once.
|
|
type LeveledSubLogger interface {
|
|
// SubLoggers returns the map of all registered subsystem loggers.
|
|
SubLoggers() SubLoggers
|
|
|
|
// SupportedSubsystems returns a slice of strings containing the names
|
|
// of the supported subsystems. Should ideally correspond to the keys
|
|
// of the subsystem logger map and be sorted.
|
|
SupportedSubsystems() []string
|
|
|
|
// SetLogLevel assigns an individual subsystem logger a new log level.
|
|
SetLogLevel(subsystemID string, logLevel string)
|
|
|
|
// SetLogLevels assigns all subsystem loggers the same new log level.
|
|
SetLogLevels(logLevel string)
|
|
}
|
|
|
|
// ParseAndSetDebugLevels attempts to parse the specified debug level and set
|
|
// the levels accordingly on the given logger. An appropriate error is returned
|
|
// if anything is invalid.
|
|
func ParseAndSetDebugLevels(level string, logger LeveledSubLogger) error {
|
|
// Split at the delimiter.
|
|
levels := strings.Split(level, ",")
|
|
if len(levels) == 0 {
|
|
return fmt.Errorf("invalid log level: %v", level)
|
|
}
|
|
|
|
// If the first entry has no =, treat is as the log level for all
|
|
// subsystems.
|
|
globalLevel := levels[0]
|
|
if !strings.Contains(globalLevel, "=") {
|
|
// Validate debug log level.
|
|
if !validLogLevel(globalLevel) {
|
|
str := "the specified debug level [%v] is invalid"
|
|
|
|
return fmt.Errorf(str, globalLevel)
|
|
}
|
|
|
|
// Change the logging level for all subsystems.
|
|
logger.SetLogLevels(globalLevel)
|
|
|
|
// The rest will target specific subsystems.
|
|
levels = levels[1:]
|
|
}
|
|
|
|
// Go through the subsystem/level pairs while detecting issues and
|
|
// update the log levels accordingly.
|
|
for _, logLevelPair := range levels {
|
|
if !strings.Contains(logLevelPair, "=") {
|
|
str := "the specified debug level contains an " +
|
|
"invalid subsystem/level pair [%v]"
|
|
|
|
return fmt.Errorf(str, logLevelPair)
|
|
}
|
|
|
|
// Extract the specified subsystem and log level.
|
|
fields := strings.Split(logLevelPair, "=")
|
|
if len(fields) != 2 {
|
|
str := "the specified debug level has an invalid " +
|
|
"format [%v] -- use format subsystem1=level1," +
|
|
"subsystem2=level2"
|
|
|
|
return fmt.Errorf(str, logLevelPair)
|
|
}
|
|
subsysID, logLevel := fields[0], fields[1]
|
|
subLoggers := logger.SubLoggers()
|
|
|
|
// Validate subsystem.
|
|
if _, exists := subLoggers[subsysID]; !exists {
|
|
str := "the specified subsystem [%v] is invalid -- " +
|
|
"supported subsystems are %v"
|
|
|
|
return fmt.Errorf(
|
|
str, subsysID, logger.SupportedSubsystems(),
|
|
)
|
|
}
|
|
|
|
// Validate log level.
|
|
if !validLogLevel(logLevel) {
|
|
str := "the specified debug level [%v] is invalid"
|
|
return fmt.Errorf(str, logLevel)
|
|
}
|
|
|
|
logger.SetLogLevel(subsysID, logLevel)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// validLogLevel returns whether or not logLevel is a valid debug log level.
|
|
func validLogLevel(logLevel string) bool {
|
|
switch logLevel {
|
|
case "trace":
|
|
fallthrough
|
|
case "debug":
|
|
fallthrough
|
|
case "info":
|
|
fallthrough
|
|
case "warn":
|
|
fallthrough
|
|
case "error":
|
|
fallthrough
|
|
case "critical":
|
|
fallthrough
|
|
case "off":
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|