From cd31bdece1983ea3523b89aef1db018ffc8b3ef0 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sat, 22 Nov 2025 07:48:34 +0000 Subject: [PATCH] Add log level control to host agent Related to #742 --- cmd/pulse-host-agent/main.go | 37 +++++++++++++++++++++++++++++++++++- docs/HOST_AGENT.md | 1 + internal/hostagent/agent.go | 13 +++++++++++-- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/cmd/pulse-host-agent/main.go b/cmd/pulse-host-agent/main.go index 379761d70..afdfa1113 100644 --- a/cmd/pulse-host-agent/main.go +++ b/cmd/pulse-host-agent/main.go @@ -29,7 +29,9 @@ func (m *multiValue) Set(value string) error { func main() { cfg := loadConfig() - logger := zerolog.New(os.Stdout).With().Timestamp().Logger() + zerolog.SetGlobalLevel(cfg.LogLevel) + + logger := zerolog.New(os.Stdout).Level(cfg.LogLevel).With().Timestamp().Logger() cfg.Logger = &logger // Check if we should run as a Windows service @@ -69,6 +71,7 @@ func loadConfig() hostagent.Config { envInsecure := utils.GetenvTrim("PULSE_INSECURE_SKIP_VERIFY") envTags := utils.GetenvTrim("PULSE_TAGS") envRunOnce := utils.GetenvTrim("PULSE_ONCE") + envLogLevel := utils.GetenvTrim("LOG_LEVEL") defaultInterval := 30 * time.Second if envInterval != "" { @@ -85,6 +88,7 @@ func loadConfig() hostagent.Config { insecureFlag := flag.Bool("insecure", utils.ParseBool(envInsecure), "Skip TLS certificate verification") runOnceFlag := flag.Bool("once", utils.ParseBool(envRunOnce), "Collect and send a single report, then exit") showVersion := flag.Bool("version", false, "Print the agent version and exit") + logLevelFlag := flag.String("log-level", defaultLogLevel(envLogLevel), "Log level: debug, info, warn, error") var tagFlags multiValue flag.Var(&tagFlags, "tag", "Tag to apply to this host (repeatable)") @@ -112,6 +116,12 @@ func loadConfig() hostagent.Config { interval = 30 * time.Second } + logLevel, err := parseLogLevel(*logLevelFlag) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + tags := gatherTags(envTags, tagFlags) return hostagent.Config{ @@ -123,6 +133,7 @@ func loadConfig() hostagent.Config { Tags: tags, InsecureSkipVerify: *insecureFlag, RunOnce: *runOnceFlag, + LogLevel: logLevel, } } @@ -144,3 +155,27 @@ func gatherTags(env string, flags []string) []string { } return tags } + +func parseLogLevel(value string) (zerolog.Level, error) { + normalized := strings.ToLower(strings.TrimSpace(value)) + if normalized == "" { + return zerolog.InfoLevel, nil + } + + level, err := zerolog.ParseLevel(normalized) + if err != nil { + return zerolog.InfoLevel, fmt.Errorf("invalid log level %q: must be debug, info, warn, or error", value) + } + if level < zerolog.DebugLevel || level > zerolog.ErrorLevel { + return zerolog.InfoLevel, fmt.Errorf("invalid log level %q: must be debug, info, warn, or error", value) + } + + return level, nil +} + +func defaultLogLevel(envValue string) string { + if strings.TrimSpace(envValue) == "" { + return "info" + } + return envValue +} diff --git a/docs/HOST_AGENT.md b/docs/HOST_AGENT.md index 95b67c468..a7583e4be 100644 --- a/docs/HOST_AGENT.md +++ b/docs/HOST_AGENT.md @@ -204,6 +204,7 @@ Start-Service -Name PulseHostAgent | `--url` | Pulse base URL (defaults to `http://localhost:7655`) | | `--token` | API token with the `host-agent:report` scope | | `--interval` | Polling interval (`30s` default) | +| `--log-level` | Log verbosity (`debug`, `info`, `warn`, `error`; defaults to `info`). Also respects `LOG_LEVEL`. Use `warn` on noisy journald setups. | | `--hostname` | Override reported hostname | | `--agent-id` | Override agent identifier (used as dedupe key) | | `--tag` | Optional tag(s) to annotate the host (repeatable) | diff --git a/internal/hostagent/agent.go b/internal/hostagent/agent.go index a876dcbb0..22ef0ca4e 100644 --- a/internal/hostagent/agent.go +++ b/internal/hostagent/agent.go @@ -30,6 +30,7 @@ type Config struct { Tags []string InsecureSkipVerify bool RunOnce bool + LogLevel zerolog.Level Logger *zerolog.Logger } @@ -61,12 +62,20 @@ func New(cfg Config) (*Agent, error) { cfg.Interval = defaultInterval } + if zerolog.GlobalLevel() == zerolog.DebugLevel && cfg.LogLevel != zerolog.DebugLevel { + zerolog.SetGlobalLevel(cfg.LogLevel) + } + if cfg.Logger == nil { - defaultLogger := zerolog.New(zerolog.NewConsoleWriter()).With().Timestamp().Logger() + defaultLogger := zerolog.New(zerolog.NewConsoleWriter()). + Level(cfg.LogLevel). + With(). + Timestamp(). + Logger() cfg.Logger = &defaultLogger } - logger := cfg.Logger.With().Str("component", "host-agent").Logger() + logger := cfg.Logger.Level(cfg.LogLevel).With().Str("component", "host-agent").Logger() if strings.TrimSpace(cfg.APIToken) == "" { return nil, fmt.Errorf("api token is required")