From 0ef5f20aa7c41f7544c02aabd7b9e6e1a86342c3 Mon Sep 17 00:00:00 2001 From: mmetc <92726601+mmetc@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:44:09 +0100 Subject: [PATCH] bin/crowdsec: avoid writing errors twice when log_media=stdout (#2729) * bin/crowdsec: avoid writing errors twice when log_media=stdout * lint --- cmd/crowdsec/hook.go | 43 ++++++++++++++++++++++++++++++++++++++ cmd/crowdsec/main.go | 15 +++++++++++++ cmd/crowdsec/run_in_svc.go | 16 ++++---------- 3 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 cmd/crowdsec/hook.go diff --git a/cmd/crowdsec/hook.go b/cmd/crowdsec/hook.go new file mode 100644 index 000000000..28515d9e4 --- /dev/null +++ b/cmd/crowdsec/hook.go @@ -0,0 +1,43 @@ +package main + +import ( + "io" + "os" + + log "github.com/sirupsen/logrus" +) + +type ConditionalHook struct { + Writer io.Writer + LogLevels []log.Level + Enabled bool +} + +func (hook *ConditionalHook) Fire(entry *log.Entry) error { + if hook.Enabled { + line, err := entry.String() + if err != nil { + return err + } + + _, err = hook.Writer.Write([]byte(line)) + + return err + } + + return nil +} + +func (hook *ConditionalHook) Levels() []log.Level { + return hook.LogLevels +} + +// The primal logging hook is set up before parsing config.yaml. +// Once config.yaml is parsed, the primal hook is disabled if the +// configured logger is writing to stderr. Otherwise it's used to +// report fatal errors and panics to stderr in addition to the log file. +var primalHook = &ConditionalHook{ + Writer: os.Stderr, + LogLevels: []log.Level{log.FatalLevel, log.PanicLevel}, + Enabled: true, +} diff --git a/cmd/crowdsec/main.go b/cmd/crowdsec/main.go index 362ed1869..05a6db01d 100644 --- a/cmd/crowdsec/main.go +++ b/cmd/crowdsec/main.go @@ -80,11 +80,13 @@ func LoadBuckets(cConfig *csconfig.Config, hub *cwhub.Hub) error { err error files []string ) + for _, hubScenarioItem := range hub.GetItemMap(cwhub.SCENARIOS) { if hubScenarioItem.State.Installed { files = append(files, hubScenarioItem.State.LocalPath) } } + buckets = leakybucket.NewBuckets() log.Infof("Loading %d scenario files", len(files)) @@ -99,6 +101,7 @@ func LoadBuckets(cConfig *csconfig.Config, hub *cwhub.Hub) error { holders[holderIndex].Profiling = true } } + return nil } @@ -143,8 +146,10 @@ func (l labelsMap) Set(label string) error { if len(split) != 2 { return fmt.Errorf("invalid format for label '%s', must be key:value", pair) } + l[split[0]] = split[1] } + return nil } @@ -168,9 +173,11 @@ func (f *Flags) Parse() { flag.BoolVar(&f.DisableAPI, "no-api", false, "disable local API") flag.BoolVar(&f.DisableCAPI, "no-capi", false, "disable communication with Central API") flag.BoolVar(&f.OrderEvent, "order-event", false, "enforce event ordering with significant performance cost") + if runtime.GOOS == "windows" { flag.StringVar(&f.WinSvc, "winsvc", "", "Windows service Action: Install, Remove etc..") } + flag.StringVar(&dumpFolder, "dump-data", "", "dump parsers/buckets raw outputs") flag.Parse() } @@ -205,6 +212,7 @@ func newLogLevel(curLevelPtr *log.Level, f *Flags) *log.Level { // avoid returning a new ptr to the same value return curLevelPtr } + return &ret } @@ -238,6 +246,8 @@ func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet boo return nil, err } + primalHook.Enabled = (cConfig.Common.LogMedia != "stdout") + if err := csconfig.LoadFeatureFlagsFile(configFile, log.StandardLogger()); err != nil { return nil, err } @@ -282,6 +292,7 @@ func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet boo if cConfig.DisableAPI { cConfig.Common.Daemonize = false } + log.Infof("single file mode : log_media=%s daemonize=%t", cConfig.Common.LogMedia, cConfig.Common.Daemonize) } @@ -291,6 +302,7 @@ func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet boo if cConfig.Common.Daemonize && runtime.GOOS == "windows" { log.Debug("Daemonization is not supported on Windows, disabling") + cConfig.Common.Daemonize = false } @@ -308,6 +320,8 @@ func LoadConfig(configFile string, disableAgent bool, disableAPI bool, quiet boo var crowdsecT0 time.Time func main() { + log.AddHook(primalHook) + if err := fflag.RegisterAllFeatures(); err != nil { log.Fatalf("failed to register features: %s", err) } @@ -342,5 +356,6 @@ func main() { if err != nil { log.Fatal(err) } + os.Exit(0) } diff --git a/cmd/crowdsec/run_in_svc.go b/cmd/crowdsec/run_in_svc.go index 563f758c6..8b2cb2aee 100644 --- a/cmd/crowdsec/run_in_svc.go +++ b/cmd/crowdsec/run_in_svc.go @@ -4,10 +4,8 @@ package main import ( "fmt" - "os" log "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/writer" "github.com/crowdsecurity/go-cs-lib/trace" "github.com/crowdsecurity/go-cs-lib/version" @@ -24,16 +22,6 @@ func StartRunSvc() error { defer trace.CatchPanic("crowdsec/StartRunSvc") - // Set a default logger with level=fatal on stderr, - // in addition to the one we configure afterwards - log.AddHook(&writer.Hook{ - Writer: os.Stderr, - LogLevels: []log.Level{ - log.PanicLevel, - log.FatalLevel, - }, - }) - if cConfig, err = LoadConfig(flags.ConfigFile, flags.DisableAgent, flags.DisableAPI, false); err != nil { return err } @@ -46,6 +34,7 @@ func StartRunSvc() error { // Enable profiling early if cConfig.Prometheus != nil { var dbClient *database.Client + var err error if cConfig.DbConfig != nil { @@ -55,8 +44,11 @@ func StartRunSvc() error { return fmt.Errorf("unable to create database client: %s", err) } } + registerPrometheus(cConfig.Prometheus) + go servePrometheus(cConfig.Prometheus, dbClient, apiReady, agentReady) } + return Serve(cConfig, apiReady, agentReady) }