diff --git a/app/cli.go b/app/cli.go index 61042e6a5..5d9b0e770 100644 --- a/app/cli.go +++ b/app/cli.go @@ -42,20 +42,23 @@ func (app *CortezaApp) InitCLI() { // loaded at this point! app.Opt = options.Init() - app.Log.Warn("loading plugins", zap.String("paths", app.Opt.Plugins.Paths)) - if app.Opt.Plugins.Enabled { - var paths []string - paths, err = plugin.Resolve(app.Opt.Plugins.Paths) + { + log := app.Log.Named("plugins") + if app.Opt.Plugins.Enabled && len(app.Opt.Plugins.Paths) > 0 { + log.Warn("loading", zap.String("paths", app.Opt.Plugins.Paths)) - app.Log.Warn("loading plugins", zap.Strings("paths", paths)) + var paths []string + paths, err = plugin.Resolve(app.Opt.Plugins.Paths) + log.Warn("loading", zap.Strings("resolved-paths", paths)) - app.plugins, err = plugin.Load(paths...) - if err != nil { - return err + app.plugins, err = plugin.Load(paths...) + if err != nil { + return err + } + } else { + // Empty set of plugins + app.plugins = plugin.Set{} } - } else { - // Empty set of plugins - app.plugins = plugin.Set{} } return err diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 9392cb0bc..2cd3767df 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -1,6 +1,7 @@ package logger import ( + "fmt" "time" "github.com/cortezaproject/corteza-server/pkg/options" @@ -10,7 +11,6 @@ import ( ) var ( - opt = options.Log() defaultLogger = zap.NewNop() ) @@ -26,38 +26,76 @@ func SetDefault(logger *zap.Logger) { defaultLogger = logger } -// Init (re)initializes logger according to the settings +// 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() { - if opt.Debug { - // Do we want to enable debug logger - // with a bit more dev-friendly output - defaultLogger = MakeDebugLogger() - defaultLogger.Debug("full debug mode enabled") - return - } - var ( - conf = applyOptions(zap.NewProductionConfig(), opt) - logger, err = conf.Build() + // @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 err != nil { - panic(err) + if hSrvOpt.WebConsoleEnabled { + // web console is the only thing right now + // that needs logger to buffer events for later access + logger = withDebugBuffer(logger) } - logger = applySpecials(defaultLogger, opt) - 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 +} + func MakeDebugLogger() *zap.Logger { - dbgOpt := *opt - dbgOpt.Debug = true - dbgOpt.Level = "debug" + return Must(Debug(options.Log())) +} + +// 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 = applyOptions(zap.NewDevelopmentConfig(), &dbgOpt) + conf = applyOptionsToConfig(zap.NewDevelopmentConfig(), dbgOpt) ) // Print log level in colors @@ -68,18 +106,19 @@ func MakeDebugLogger() *zap.Logger { enc.AppendString(t.Format("15:04:05.000")) } - logger, err := conf.Build() - if err != nil { - panic(err) - } + return conf.Build() +} - logger = withDebugBuffer(logger) +func Production(opt *options.LogOpt) (*zap.Logger, error) { + var ( + conf = applyOptionsToConfig(zap.NewProductionConfig(), opt) + ) - return applySpecials(logger, &dbgOpt) + return conf.Build() } // Applies options from environment variables -func applyOptions(conf zap.Config, opt *options.LogOpt) zap.Config { +func applyOptionsToConfig(conf zap.Config, opt *options.LogOpt) zap.Config { // LOG_LEVEL conf.Level = zap.NewAtomicLevelAt(mustParseLevel(opt.Level)) @@ -91,18 +130,25 @@ func applyOptions(conf zap.Config, opt *options.LogOpt) zap.Config { return conf } -// Applies "special" options - filtering and conditional stack-level -func applySpecials(l *zap.Logger, opt *options.LogOpt) *zap.Logger { - if len(opt.Filter) > 0 { - // LOG_FILTER - l = zap.New(zapfilter.NewFilteringCore(l.Core(), zapfilter.MustParseRules(opt.Filter))) +// 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))) } - // LOG_STACKTRACE_LEVEL - return l.WithOptions(zap.AddStacktrace(mustParseLevel(opt.StacktraceLevel))) + return l } -// adds Tee logger that copies all log messages to debug buffer +// 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(), diff --git a/pkg/logger/rrbuf.go b/pkg/logger/rrbuf.go index 2eac2973a..468c7d86e 100644 --- a/pkg/logger/rrbuf.go +++ b/pkg/logger/rrbuf.go @@ -30,6 +30,7 @@ type ( ) const ( + // allowing 10k entries (no limiting the entry size) debugLogCap = 10240 ) @@ -40,7 +41,13 @@ var ( } ) +// WriteLogBuffer provides access to default debug log buffer func WriteLogBuffer(w io.Writer, after, limit int) (_ int, err error) { + return writeLogBuffer(w, debugLogRR, after, limit) +} + +// writes stream of entries from log buffer into provided writer array of JSON objects. +func writeLogBuffer(w io.Writer, logBuf *rr, after, limit int) (_ int, err error) { var ( // was at least one entry outputted? has bool @@ -52,7 +59,7 @@ func WriteLogBuffer(w io.Writer, after, limit int) (_ int, err error) { return } - for _, e := range debugLogRR.buf { + for _, e := range logBuf.buf { if after >= e.num { continue } @@ -100,6 +107,7 @@ func (r *rr) append(ent []byte) { } } +// DebugBufferedLogger provides buffered logger compatible with zap. func DebugBufferedLogger(out *rr) *debugBufferingLogger { var encConf = zap.NewProductionEncoderConfig() encConf.EncodeTime = zapcore.RFC3339NanoTimeEncoder