This is due to us introducing the web console and the uints needing to be string encoded (because of JavaScript).
186 lines
4.3 KiB
Go
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
|
|
}
|