kaspad/logger/logger.go
Svarog 28681affda
[NOD-994] Greatly increase the amount of logs kaspad keeps before rotating them away (#720)
* [NOD-994] Greatly increased the amount of logs kaspad keeps before rotating them away

* [NOD-994] Actually invcrease the log file

* [NOD-994] Update comments

* [NOD-994] Fix typo
2020-05-14 10:58:46 +03:00

276 lines
7.3 KiB
Go

// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package logger
import (
"fmt"
"os"
"sort"
"strings"
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/logs"
)
// Loggers per subsystem. A single backend logger is created and all subsytem
// loggers created from it will write to the backend. When adding new
// subsystems, add the subsystem logger variable here and to the
// subsystemLoggers map.
//
// Loggers can not be used before the log rotator has been initialized with a
// log file. This must be performed early during application startup by calling
// InitLog.
var (
// BackendLog is the logging backend used to create all subsystem loggers.
BackendLog = logs.NewBackend()
adxrLog = BackendLog.Logger("ADXR")
amgrLog = BackendLog.Logger("AMGR")
cmgrLog = BackendLog.Logger("CMGR")
ksdbLog = BackendLog.Logger("KSDB")
kasdLog = BackendLog.Logger("KASD")
bdagLog = BackendLog.Logger("BDAG")
cnfgLog = BackendLog.Logger("CNFG")
discLog = BackendLog.Logger("DISC")
indxLog = BackendLog.Logger("INDX")
minrLog = BackendLog.Logger("MINR")
peerLog = BackendLog.Logger("PEER")
rpcsLog = BackendLog.Logger("RPCS")
scrpLog = BackendLog.Logger("SCRP")
srvrLog = BackendLog.Logger("SRVR")
syncLog = BackendLog.Logger("SYNC")
txmpLog = BackendLog.Logger("TXMP")
utilLog = BackendLog.Logger("UTIL")
profLog = BackendLog.Logger("PROF")
)
// SubsystemTags is an enum of all sub system tags
var SubsystemTags = struct {
ADXR,
AMGR,
CMGR,
KSDB,
KASD,
BDAG,
CNFG,
DISC,
INDX,
MINR,
PEER,
RPCS,
SCRP,
SRVR,
SYNC,
TXMP,
UTIL,
PROF string
}{
ADXR: "ADXR",
AMGR: "AMGR",
CMGR: "CMGR",
KSDB: "KSDB",
KASD: "KASD",
BDAG: "BDAG",
CNFG: "CNFG",
DISC: "DISC",
INDX: "INDX",
MINR: "MINR",
PEER: "PEER",
RPCS: "RPCS",
SCRP: "SCRP",
SRVR: "SRVR",
SYNC: "SYNC",
TXMP: "TXMP",
UTIL: "UTIL",
PROF: "PROF",
}
// subsystemLoggers maps each subsystem identifier to its associated logger.
var subsystemLoggers = map[string]*logs.Logger{
SubsystemTags.ADXR: adxrLog,
SubsystemTags.AMGR: amgrLog,
SubsystemTags.CMGR: cmgrLog,
SubsystemTags.KSDB: ksdbLog,
SubsystemTags.KASD: kasdLog,
SubsystemTags.BDAG: bdagLog,
SubsystemTags.CNFG: cnfgLog,
SubsystemTags.DISC: discLog,
SubsystemTags.INDX: indxLog,
SubsystemTags.MINR: minrLog,
SubsystemTags.PEER: peerLog,
SubsystemTags.RPCS: rpcsLog,
SubsystemTags.SCRP: scrpLog,
SubsystemTags.SRVR: srvrLog,
SubsystemTags.SYNC: syncLog,
SubsystemTags.TXMP: txmpLog,
SubsystemTags.UTIL: utilLog,
SubsystemTags.PROF: profLog,
}
// InitLog attaches log file and error log file to the backend log.
func InitLog(logFile, errLogFile string) {
err := BackendLog.AddLogFileWithCustomRotator(logFile, logs.LevelTrace, 100*1024, 4)
if err != nil {
fmt.Fprintf(os.Stderr, "Error adding log file %s as log rotator for level %s: %s", logFile, logs.LevelTrace, err)
os.Exit(1)
}
err = BackendLog.AddLogFile(errLogFile, logs.LevelWarn)
if err != nil {
fmt.Fprintf(os.Stderr, "Error adding log file %s as log rotator for level %s: %s", errLogFile, logs.LevelWarn, err)
os.Exit(1)
}
}
// SetLogLevel sets the logging level for provided subsystem. Invalid
// subsystems are ignored. Uninitialized subsystems are dynamically created as
// needed.
func SetLogLevel(subsystemID string, logLevel string) {
// Ignore invalid subsystems.
logger, ok := subsystemLoggers[subsystemID]
if !ok {
return
}
// Defaults to info if the log level is invalid.
level, _ := logs.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.
func SetLogLevels(logLevel string) {
// Configure all sub-systems with the new logging level. Dynamically
// create loggers as needed.
for subsystemID := range subsystemLoggers {
SetLogLevel(subsystemID, logLevel)
}
}
// DirectionString is a helper function that returns a string that represents
// the direction of a connection (inbound or outbound).
func DirectionString(inbound bool) string {
if inbound {
return "inbound"
}
return "outbound"
}
// PickNoun returns the singular or plural form of a noun depending
// on the count n.
func PickNoun(n uint64, singular, plural string) string {
if n == 1 {
return singular
}
return plural
}
// SupportedSubsystems returns a sorted slice of the supported subsystems for
// logging purposes.
func SupportedSubsystems() []string {
// Convert the subsystemLoggers map keys to a slice.
subsystems := make([]string, 0, len(subsystemLoggers))
for subsysID := range subsystemLoggers {
subsystems = append(subsystems, subsysID)
}
// Sort the subsystems for stable display.
sort.Strings(subsystems)
return subsystems
}
// Get returns a logger of a specific sub system
func Get(tag string) (logger *logs.Logger, ok bool) {
logger, ok = subsystemLoggers[tag]
return
}
// ParseAndSetDebugLevels attempts to parse the specified debug level and set
// the levels accordingly. An appropriate error is returned if anything is
// invalid.
func ParseAndSetDebugLevels(debugLevel string) error {
// When the specified string doesn't have any delimters, treat it as
// the log level for all subsystems.
if !strings.Contains(debugLevel, ",") && !strings.Contains(debugLevel, "=") {
// Validate debug log level.
if !validLogLevel(debugLevel) {
str := "The specified debug level [%s] is invalid"
return errors.Errorf(str, debugLevel)
}
// Change the logging level for all subsystems.
SetLogLevels(debugLevel)
return nil
}
// Split the specified string into subsystem/level pairs while detecting
// issues and update the log levels accordingly.
for _, logLevelPair := range strings.Split(debugLevel, ",") {
if !strings.Contains(logLevelPair, "=") {
str := "The specified debug level contains an invalid " +
"subsystem/level pair [%s]"
return errors.Errorf(str, logLevelPair)
}
// Extract the specified subsystem and log level.
fields := strings.Split(logLevelPair, "=")
subsysID, logLevel := fields[0], fields[1]
// Validate subsystem.
if _, exists := Get(subsysID); !exists {
str := "The specified subsystem [%s] is invalid -- " +
"supported subsytems %s"
return errors.Errorf(str, subsysID, strings.Join(SupportedSubsystems(), ", "))
}
// Validate log level.
if !validLogLevel(logLevel) {
str := "The specified debug level [%s] is invalid"
return errors.Errorf(str, logLevel)
}
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":
return true
}
return false
}
// LogClosure is a closure that can be printed with %s to be used to
// generate expensive-to-create data for a detailed log level and avoid doing
// the work if the data isn't printed.
type LogClosure func() string
func (c LogClosure) String() string {
return c()
}
// NewLogClosure casts a function to a LogClosure.
// See LogClosure for details.
func NewLogClosure(c func() string) LogClosure {
return c
}