3
0
corteza/server/pkg/logger/logger.go
Tomaž Jerman 462619f2b9 Change logs to encode uint64 values as strings
This is due to us introducing the web console and the uints needing
to be string encoded (because of JavaScript).
2023-05-24 12:26:01 +02:00

186 lines
4.3 KiB
Go

package logger
import (
"fmt"
"strconv"
"time"
"github.com/cortezaproject/corteza/server/pkg/options"
"github.com/spf13/cast"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"moul.io/zapfilter"
)
var (
defaultLogger = zap.NewNop()
)
func Default() *zap.Logger {
if defaultLogger == nil {
return zap.NewNop()
}
return defaultLogger
}
func SetDefault(logger *zap.Logger) {
defaultLogger = logger
}
// Init (re)initializes global logger according to the settings
//
// It also peaks into http-server options to determinate if log events
// should be buffered for use from web console
func Init() {
var (
// @todo this should probably be refactored by adding a new option to LogOpt
// that controls if we create a buffered output as well; and when not explicitly
// set, we take state of web-console as a base
hSrvOpt = options.HttpServer()
logger = Must(Make(options.Log()))
)
if hSrvOpt.WebConsoleEnabled {
// web console is the only thing right now
// that needs logger to buffer events for later access
logger = withDebugBuffer(logger)
}
defaultLogger = logger
}
// Make creates a logger (debug or production) according to options
func Make(opt *options.LogOpt) (logger *zap.Logger, err error) {
if opt.Debug {
// Do we want to enable debug logger
// with a bit more dev-friendly output
logger, err = Debug(opt)
} else {
logger, err = Production(opt)
}
if err != nil {
return nil, err
}
logger = withFilter(logger, opt.Filter)
logger = withStacktraceLevel(logger, opt.StacktraceLevel)
return logger, nil
}
// MakeDebugLogger creates a logger
func MakeDebugLogger() *zap.Logger {
var (
logger = Must(Debug(options.Log()))
)
logger = withStacktraceLevel(logger, "dpanic")
return logger
}
// Must is a utility function that panics if given log maker returns an error
func Must(logger *zap.Logger, err error) *zap.Logger {
if err != nil {
panic(fmt.Errorf("failed to configure logger: %w", err))
}
return logger
}
// Debug prepares debug logger using options
func Debug(opt *options.LogOpt) (*zap.Logger, error) {
var (
// make a copy of debug options so that we do not
dbgOpt = &options.LogOpt{
Debug: true,
Level: "debug",
Filter: opt.Filter,
IncludeCaller: opt.IncludeCaller,
StacktraceLevel: opt.StacktraceLevel,
}
)
var (
conf = applyOptionsToConfig(zap.NewDevelopmentConfig(), dbgOpt)
)
// Print log level in colors
conf.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
// Shorten timestamp, we do not care about the date
conf.EncoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("15:04:05.000"))
}
return conf.Build()
}
func Production(opt *options.LogOpt) (*zap.Logger, error) {
var (
conf = applyOptionsToConfig(zap.NewProductionConfig(), opt)
)
return conf.Build()
}
// Uint64 properly encodes values to be represented in the web console
func Uint64(k string, id uint64) zap.Field {
return zap.String(k, strconv.FormatUint(id, 10))
}
// Uint64s properly encodes values to be represented in the web console
func Uint64s(k string, id []uint64) zap.Field {
return zap.Strings(k, cast.ToStringSlice(id))
}
// Applies options from environment variables
func applyOptionsToConfig(conf zap.Config, opt *options.LogOpt) zap.Config {
// LOG_LEVEL
conf.Level = zap.NewAtomicLevelAt(mustParseLevel(opt.Level))
// LOG_INCLUDE_CALLER
conf.DisableCaller = !opt.IncludeCaller
conf.Sampling = nil
return conf
}
// Applies filtering options
//
// This is controlled with LOG_FILTER environmental var
func withFilter(l *zap.Logger, filter string) *zap.Logger {
if len(filter) > 0 {
l = zap.New(zapfilter.NewFilteringCore(l.Core(), zapfilter.MustParseRules(filter)))
}
return l
}
// Applies stacktrace level options
//
// This is controlled with LOG_STACKTRACE_LEVEL environmental var
func withStacktraceLevel(l *zap.Logger, level string) *zap.Logger {
return l.WithOptions(zap.AddStacktrace(mustParseLevel(level)))
}
// Adds Tee logger that copies all log messages to debug buffer
func withDebugBuffer(in *zap.Logger) *zap.Logger {
return zap.New(zapcore.NewTee(
in.Core(),
DebugBufferedLogger(debugLogRR),
))
}
func mustParseLevel(l string) (o zapcore.Level) {
if err := o.Set(l); err != nil {
panic(err)
}
return
}