status-go/vendor/github.com/ipfs/go-log/v2/setup.go

265 lines
5.8 KiB
Go
Raw Normal View History

package log
import (
"errors"
"fmt"
"os"
"regexp"
"sync"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func init() {
SetupLogging(configFromEnv())
}
// Logging environment variables
const (
// IPFS_* prefixed env vars kept for backwards compatibility
// for this release. They will not be available in the next
// release.
//
// GOLOG_* env vars take precedences over IPFS_* env vars.
envIPFSLogging = "IPFS_LOGGING"
envIPFSLoggingFmt = "IPFS_LOGGING_FMT"
envLogging = "GOLOG_LOG_LEVEL"
envLoggingFmt = "GOLOG_LOG_FMT"
envLoggingFile = "GOLOG_FILE" // /path/to/file
)
type LogFormat int
const (
ColorizedOutput LogFormat = iota
PlaintextOutput
JSONOutput
)
type Config struct {
// Format overrides the format of the log output. Defaults to ColorizedOutput
Format LogFormat
// Level is the minimum enabled logging level.
Level LogLevel
// Stderr indicates whether logs should be written to stderr.
Stderr bool
// Stdout indicates whether logs should be written to stdout.
Stdout bool
// File is a path to a file that logs will be written to.
File string
}
// ErrNoSuchLogger is returned when the util pkg is asked for a non existant logger
var ErrNoSuchLogger = errors.New("Error: No such logger")
var loggerMutex sync.RWMutex // guards access to global logger state
// loggers is the set of loggers in the system
var loggers = make(map[string]*zap.SugaredLogger)
var levels = make(map[string]zap.AtomicLevel)
// primaryFormat is the format of the primary core used for logging
var primaryFormat LogFormat = ColorizedOutput
// defaultLevel is the default log level
var defaultLevel LogLevel = LevelError
// primaryCore is the primary logging core
var primaryCore zapcore.Core
// loggerCore is the base for all loggers created by this package
var loggerCore = &lockedMultiCore{}
// SetupLogging will initialize the logger backend and set the flags.
// TODO calling this in `init` pushes all configuration to env variables
// - move it out of `init`? then we need to change all the code (js-ipfs, go-ipfs) to call this explicitly
// - have it look for a config file? need to define what that is
func SetupLogging(cfg Config) {
loggerMutex.Lock()
defer loggerMutex.Unlock()
primaryFormat = cfg.Format
defaultLevel = cfg.Level
outputPaths := []string{}
if cfg.Stderr {
outputPaths = append(outputPaths, "stderr")
}
if cfg.Stdout {
outputPaths = append(outputPaths, "stdout")
}
// check if we log to a file
if len(cfg.File) > 0 {
if path, err := normalizePath(cfg.File); err != nil {
fmt.Fprintf(os.Stderr, "failed to resolve log path '%q', logging to %s\n", cfg.File, outputPaths)
} else {
outputPaths = append(outputPaths, path)
}
}
ws, _, err := zap.Open(outputPaths...)
if err != nil {
panic(fmt.Sprintf("unable to open logging output: %v", err))
}
newPrimaryCore := newCore(primaryFormat, ws, LevelDebug) // the main core needs to log everything.
if primaryCore != nil {
loggerCore.ReplaceCore(primaryCore, newPrimaryCore)
} else {
loggerCore.AddCore(newPrimaryCore)
}
primaryCore = newPrimaryCore
setAllLoggers(defaultLevel)
}
// SetDebugLogging calls SetAllLoggers with logging.DEBUG
func SetDebugLogging() {
SetAllLoggers(LevelDebug)
}
// SetAllLoggers changes the logging level of all loggers to lvl
func SetAllLoggers(lvl LogLevel) {
loggerMutex.RLock()
defer loggerMutex.RUnlock()
setAllLoggers(lvl)
}
func setAllLoggers(lvl LogLevel) {
for _, l := range levels {
l.SetLevel(zapcore.Level(lvl))
}
}
// SetLogLevel changes the log level of a specific subsystem
// name=="*" changes all subsystems
func SetLogLevel(name, level string) error {
lvl, err := LevelFromString(level)
if err != nil {
return err
}
// wildcard, change all
if name == "*" {
SetAllLoggers(lvl)
return nil
}
loggerMutex.RLock()
defer loggerMutex.RUnlock()
// Check if we have a logger by that name
if _, ok := levels[name]; !ok {
return ErrNoSuchLogger
}
levels[name].SetLevel(zapcore.Level(lvl))
return nil
}
// SetLogLevelRegex sets all loggers to level `l` that match expression `e`.
// An error is returned if `e` fails to compile.
func SetLogLevelRegex(e, l string) error {
lvl, err := LevelFromString(l)
if err != nil {
return err
}
rem, err := regexp.Compile(e)
if err != nil {
return err
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
for name := range loggers {
if rem.MatchString(name) {
levels[name].SetLevel(zapcore.Level(lvl))
}
}
return nil
}
// GetSubsystems returns a slice containing the
// names of the current loggers
func GetSubsystems() []string {
loggerMutex.RLock()
defer loggerMutex.RUnlock()
subs := make([]string, 0, len(loggers))
for k := range loggers {
subs = append(subs, k)
}
return subs
}
func getLogger(name string) *zap.SugaredLogger {
loggerMutex.Lock()
defer loggerMutex.Unlock()
log, ok := loggers[name]
if !ok {
levels[name] = zap.NewAtomicLevelAt(zapcore.Level(defaultLevel))
log = zap.New(loggerCore).
WithOptions(zap.IncreaseLevel(levels[name])).
Named(name).
Sugar()
loggers[name] = log
}
return log
}
// configFromEnv returns a Config with defaults populated using environment variables.
func configFromEnv() Config {
cfg := Config{
Format: ColorizedOutput,
Stderr: true,
Level: LevelError,
}
format := os.Getenv(envLoggingFmt)
if format == "" {
format = os.Getenv(envIPFSLoggingFmt)
}
switch format {
case "nocolor":
cfg.Format = PlaintextOutput
case "json":
cfg.Format = JSONOutput
}
lvl := os.Getenv(envLogging)
if lvl == "" {
lvl = os.Getenv(envIPFSLogging)
}
if lvl != "" {
var err error
cfg.Level, err = LevelFromString(lvl)
if err != nil {
fmt.Fprintf(os.Stderr, "error setting log levels: %s\n", err)
}
}
cfg.File = os.Getenv(envLoggingFile)
// Disable stderr logging when a file is specified
// https://github.com/ipfs/go-log/issues/83
if cfg.File != "" {
cfg.Stderr = false
}
return cfg
}